nicoSWD /
headsec
| 1 | <?php declare(strict_types=1); |
||
| 2 | |||
| 3 | /** |
||
| 4 | * @license http://opensource.org/licenses/mit-license.php MIT |
||
| 5 | * @link https://github.com/nicoSWD |
||
| 6 | * @author Nicolas Oelgart <[email protected]> |
||
| 7 | */ |
||
| 8 | namespace nicoSWD\SecHeaderCheck\Infrastructure\Header; |
||
| 9 | |||
| 10 | use nicoSWD\SecHeaderCheck\Domain\Header\AbstractHeaderProvider; |
||
| 11 | use nicoSWD\SecHeaderCheck\Domain\Header\Exception\ConnectionTimeoutException; |
||
| 12 | use nicoSWD\SecHeaderCheck\Domain\Header\Exception\MaxHeaderSizeExceededException; |
||
| 13 | use nicoSWD\SecHeaderCheck\Domain\URL\URL; |
||
| 14 | |||
| 15 | final class NativeHeaderProvider extends AbstractHeaderProvider |
||
| 16 | { |
||
| 17 | private const ONE_KB = 1024; |
||
| 18 | private const MAX_HEADER_SIZE = self::ONE_KB * 8; |
||
| 19 | |||
| 20 | public function getRawHeaders(URL $url): string |
||
| 21 | { |
||
| 22 | $fp = $this->connect($url); |
||
| 23 | $this->sendRequest($url, $fp); |
||
| 24 | |||
| 25 | $headers = ''; |
||
| 26 | $bytesRead = 0; |
||
| 27 | |||
| 28 | while (!feof($fp)) { |
||
| 29 | $line = $this->readLine($fp); |
||
| 30 | |||
| 31 | if ($line === false || trim($line) === '') { |
||
| 32 | break; |
||
| 33 | } |
||
| 34 | |||
| 35 | $bytesRead += strlen($line); |
||
| 36 | |||
| 37 | if ($this->hasExceededMaxSize($bytesRead)) { |
||
| 38 | throw new MaxHeaderSizeExceededException(); |
||
| 39 | } |
||
| 40 | |||
| 41 | $headers .= $line; |
||
| 42 | } |
||
| 43 | |||
| 44 | fclose($fp); |
||
| 45 | |||
| 46 | return $headers; |
||
| 47 | } |
||
| 48 | |||
| 49 | private function readLine($fp) |
||
| 50 | { |
||
| 51 | return fgets($fp, self::ONE_KB * 2); |
||
| 52 | } |
||
| 53 | |||
| 54 | private function hasExceededMaxSize(int $bytesRead): bool |
||
| 55 | { |
||
| 56 | return $bytesRead > self::MAX_HEADER_SIZE; |
||
| 57 | } |
||
| 58 | |||
| 59 | private function sendRequest(URL $url, $fp): void |
||
| 60 | { |
||
| 61 | $request = "HEAD {$url->path()}{$url->query()} HTTP/1.1\r\n"; |
||
| 62 | $request .= "Host: {$url->host()}\r\n"; |
||
| 63 | $request .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"; |
||
| 64 | $request .= "User-Agent: Security Headers Scanner/1.0 (https://github.com/nicoSWD)\r\n"; |
||
| 65 | $request .= "Connection: Close\r\n\r\n"; |
||
| 66 | |||
| 67 | fwrite($fp, $request); |
||
| 68 | } |
||
| 69 | |||
| 70 | private function connect(URL $url) |
||
| 71 | { |
||
| 72 | if ($url->isHttps()) { |
||
| 73 | $scheme = 'ssl://'; |
||
| 74 | } else { |
||
| 75 | $scheme = ''; |
||
| 76 | } |
||
| 77 | |||
| 78 | $fp = @fsockopen($scheme . $url->host(), $url->port(), $errNo, $errStr, $this->connectionTimeout); |
||
| 79 | |||
| 80 | if (!$fp) { |
||
|
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
| 81 | throw new ConnectionTimeoutException(); |
||
| 82 | } |
||
| 83 | |||
| 84 | return $fp; |
||
| 85 | } |
||
| 86 | } |
||
| 87 |