3 * @see https://github.com/zendframework/zend-diactoros for the canonical source repository
4 * @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (http://www.zend.com)
5 * @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
8 namespace Zend\Diactoros;
10 use InvalidArgumentException;
12 use function get_class;
14 use function in_array;
15 use function is_numeric;
16 use function is_object;
17 use function is_string;
19 use function preg_match;
24 * Provide security tools around HTTP headers to prevent common injection vectors.
26 * Code is largely lifted from the Zend\Http\Header\HeaderValue implementation in
27 * Zend Framework, released with the copyright and license below.
29 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
30 * @license http://framework.zend.com/license/new-bsd New BSD License
32 final class HeaderSecurity
35 * Private constructor; non-instantiable.
38 private function __construct()
43 * Filter a header value
45 * Ensures CRLF header injection vectors are filtered.
47 * Per RFC 7230, only VISIBLE ASCII characters, spaces, and horizontal
48 * tabs are allowed in values; header continuations MUST consist of
49 * a single CRLF sequence followed by a space or horizontal tab.
51 * This method filters any values not allowed from the string, and is
54 * @see http://en.wikipedia.org/wiki/HTTP_response_splitting
55 * @param string $value
58 public static function filter($value)
60 $value = (string) $value;
61 $length = strlen($value);
63 for ($i = 0; $i < $length; $i += 1) {
64 $ascii = ord($value[$i]);
66 // Detect continuation sequences
68 $lf = ord($value[$i + 1]);
69 $ws = ord($value[$i + 2]);
70 if ($lf === 10 && in_array($ws, [9, 32], true)) {
71 $string .= $value[$i] . $value[$i + 1];
78 // Non-visible, non-whitespace characters
79 // 9 === horizontal tab
80 // 32-126, 128-254 === visible
83 if (($ascii < 32 && $ascii !== 9)
90 $string .= $value[$i];
97 * Validate a header value.
99 * Per RFC 7230, only VISIBLE ASCII characters, spaces, and horizontal
100 * tabs are allowed in values; header continuations MUST consist of
101 * a single CRLF sequence followed by a space or horizontal tab.
103 * @see http://en.wikipedia.org/wiki/HTTP_response_splitting
104 * @param string $value
107 public static function isValid($value)
109 $value = (string) $value;
112 // \n not preceded by \r, OR
113 // \r not followed by \n, OR
114 // \r\n not followed by space or horizontal tab; these are all CRLF attacks
115 if (preg_match("#(?:(?:(?<!\r)\n)|(?:\r(?!\n))|(?:\r\n(?![ \t])))#", $value)) {
119 // Non-visible, non-whitespace characters
120 // 9 === horizontal tab
122 // 13 === carriage return
123 // 32-126, 128-254 === visible
124 // 127 === DEL (disallowed)
125 // 255 === null byte (disallowed)
126 if (preg_match('/[^\x09\x0a\x0d\x20-\x7E\x80-\xFE]/', $value)) {
134 * Assert a header value is valid.
136 * @param string $value
137 * @throws InvalidArgumentException for invalid values
139 public static function assertValid($value)
141 if (! is_string($value) && ! is_numeric($value)) {
142 throw new InvalidArgumentException(sprintf(
143 'Invalid header value type; must be a string or numeric; received %s',
144 (is_object($value) ? get_class($value) : gettype($value))
147 if (! self::isValid($value)) {
148 throw new InvalidArgumentException(sprintf(
149 '"%s" is not valid header value',
156 * Assert whether or not a header name is valid.
158 * @see http://tools.ietf.org/html/rfc7230#section-3.2
160 * @throws InvalidArgumentException
162 public static function assertValidName($name)
164 if (! is_string($name)) {
165 throw new InvalidArgumentException(sprintf(
166 'Invalid header name type; expected string; received %s',
167 (is_object($name) ? get_class($name) : gettype($name))
170 if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $name)) {
171 throw new InvalidArgumentException(sprintf(
172 '"%s" is not valid header name',