You are here

HeaderSecurity.php in Zircon Profile 8.0

Same filename and directory in other branches
  1. 8 vendor/zendframework/zend-diactoros/src/HeaderSecurity.php

Namespace

Zend\Diactoros

File

vendor/zendframework/zend-diactoros/src/HeaderSecurity.php
View source
<?php

/**
 * Zend Framework (http://framework.zend.com/)
 *
 * @see       http://github.com/zendframework/zend-diactoros for the canonical source repository
 * @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
 */
namespace Zend\Diactoros;

use InvalidArgumentException;

/**
 * Provide security tools around HTTP headers to prevent common injection vectors.
 *
 * Code is largely lifted from the Zend\Http\Header\HeaderValue implementation in
 * Zend Framework, released with the copyright and license below.
 *
 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd New BSD License
 */
final class HeaderSecurity {

  /**
   * Private constructor; non-instantiable.
   */
  private function __construct() {
  }

  /**
   * Filter a header value
   *
   * Ensures CRLF header injection vectors are filtered.
   *
   * Per RFC 7230, only VISIBLE ASCII characters, spaces, and horizontal
   * tabs are allowed in values; header continuations MUST consist of
   * a single CRLF sequence followed by a space or horizontal tab.
   *
   * This method filters any values not allowed from the string, and is
   * lossy.
   *
   * @see http://en.wikipedia.org/wiki/HTTP_response_splitting
   * @param string $value
   * @return string
   */
  public static function filter($value) {
    $value = (string) $value;
    $length = strlen($value);
    $string = '';
    for ($i = 0; $i < $length; $i += 1) {
      $ascii = ord($value[$i]);

      // Detect continuation sequences
      if ($ascii === 13) {
        $lf = ord($value[$i + 1]);
        $ws = ord($value[$i + 2]);
        if ($lf === 10 && in_array($ws, [
          9,
          32,
        ], true)) {
          $string .= $value[$i] . $value[$i + 1];
          $i += 1;
        }
        continue;
      }

      // Non-visible, non-whitespace characters
      // 9 === horizontal tab
      // 32-126, 128-254 === visible
      // 127 === DEL
      // 255 === null byte
      if ($ascii < 32 && $ascii !== 9 || $ascii === 127 || $ascii > 254) {
        continue;
      }
      $string .= $value[$i];
    }
    return $string;
  }

  /**
   * Validate a header value.
   *
   * Per RFC 7230, only VISIBLE ASCII characters, spaces, and horizontal
   * tabs are allowed in values; header continuations MUST consist of
   * a single CRLF sequence followed by a space or horizontal tab.
   *
   * @see http://en.wikipedia.org/wiki/HTTP_response_splitting
   * @param string $value
   * @return bool
   */
  public static function isValid($value) {
    $value = (string) $value;

    // Look for:
    // \n not preceded by \r, OR
    // \r not followed by \n, OR
    // \r\n not followed by space or horizontal tab; these are all CRLF attacks
    if (preg_match("#(?:(?:(?<!\r)\n)|(?:\r(?!\n))|(?:\r\n(?![ \t])))#", $value)) {
      return false;
    }

    // Non-visible, non-whitespace characters
    // 9 === horizontal tab
    // 10 === line feed
    // 13 === carriage return
    // 32-126, 128-254 === visible
    // 127 === DEL (disallowed)
    // 255 === null byte (disallowed)
    if (preg_match('/[^\\x09\\x0a\\x0d\\x20-\\x7E\\x80-\\xFE]/', $value)) {
      return false;
    }
    return true;
  }

  /**
   * Assert a header value is valid.
   *
   * @param string $value
   * @throws InvalidArgumentException for invalid values
   */
  public static function assertValid($value) {
    if (!self::isValid($value)) {
      throw new InvalidArgumentException('Invalid header value');
    }
  }

  /**
   * Assert whether or not a header name is valid.
   *
   * @see http://tools.ietf.org/html/rfc7230#section-3.2
   * @param mixed $name
   * @throws InvalidArgumentException
   */
  public static function assertValidName($name) {
    if (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $name)) {
      throw new InvalidArgumentException('Invalid header name');
    }
  }

}

Classes

Namesort descending Description
HeaderSecurity Provide security tools around HTTP headers to prevent common injection vectors.