[php] PHP로 사용자 입력을 어떻게 소독 할 수 있습니까?

특정 유형의 HTML 태그를 계속 허용하면서 SQL 삽입 및 XSS 공격에 대한 사용자 입력을 삭제하는 데 잘 작동하는 catchall 함수가 있습니까?



답변

사용자 입력을 필터링 할 수 있다는 것은 일반적인 오해입니다. PHP에는 이 아이디어를 기반으로 하는 (현재 사용되지 않는) “feature”( 매직 따옴표 )가 있습니다. 말도 안 돼요 필터링 (또는 청소 또는 사람들이 부르는 것)은 잊어 버리십시오.

문제를 피하기 위해해야 ​​할 일은 매우 간단합니다. 외부 코드에 문자열을 포함 할 때마다 해당 언어의 규칙에 따라 이스케이프해야합니다. 예를 들어, MySQL을 대상으로하는 일부 SQL에 문자열을 포함시키는 경우이 목적을 위해 MySQL 함수를 사용하여 문자열을 이스케이프해야합니다 ( mysqli_real_escape_string). 또는 데이터베이스의 경우 준비된 명령문을 사용하는 것이 가능하면 더 나은 방법입니다.

또 다른 예는 HTML입니다. HTML 마크 업 내에 문자열을 포함하는 경우을 사용하여 이스케이프해야합니다 htmlspecialchars. 즉, 모든 단일 echo또는 print명령문은를 사용해야합니다 htmlspecialchars.

외부 명령 (예 : 인수로) 삽입 문자열에가는 경우, 그리고 그들을 전화 : 세 번째 예는 쉘 명령이 될 수 exec다음 사용해야 escapeshellcmd하고 escapeshellarg.

그리고 등등 …

단지 당신이 미리 포맷 입력을 수신하는 경우 적극적 필터 데이터를 필요로하는 경우이다. 예를 들어, 사용자가 HTML 마크 업을 게시하도록 허용하면 사이트에 표시 할 계획입니다. 그러나 필터를 아무리 잘 필터링하더라도 항상 잠재적 인 보안 허점이 될 수 있으므로이를 피하는 것이 좋습니다.


답변

입력 데이터를 삭제하여 SQL 삽입을 막으려 고하지 마십시오.

대신, SQL 코드 작성에 데이터를 사용하지 마십시오 . 바운드 변수를 사용하는 준비된 명령문 (예 : 템플리트 조회에서 매개 변수 사용)을 사용하십시오. SQL 삽입에 대해 보장 할 수있는 유일한 방법입니다.

SQL 삽입 방지에 대한 자세한 내용 은 내 웹 사이트 http://bobby-tables.com/ 을 참조하십시오 .


답변

아니요. 용도에 대한 컨텍스트 없이는 일반적으로 데이터를 필터링 할 수 없습니다. 때로는 SQL 쿼리를 입력으로 사용하고 때로는 HTML을 입력으로 사용하려고 할 수도 있습니다.

화이트리스트에서 입력을 필터링해야합니다. 데이터가 예상 한 사양과 일치하는지 확인하십시오. 그런 다음 사용하는 컨텍스트에 따라 사용하기 전에 이스케이프해야합니다.

SQL 삽입을 방지하기 위해 SQL에 대한 데이터를 이스케이프 처리하는 프로세스는 XSS를 방지하기 위해 (X) HTML에 대한 데이터를 이스케이프 처리하는 프로세스와 매우 다릅니다.


답변

PHP에는 새로운 멋진 filter_input 함수가 있습니다. 예를 들어, 내장 FILTER_VALIDATE_EMAIL 유형이 있으므로 ‘최종 전자 메일 정규식’을 찾지 않아도됩니다.

내 자신의 필터 클래스 (JavaScript를 사용하여 잘못된 필드를 강조 표시)는 ajax 요청 또는 일반 양식 게시로 시작할 수 있습니다. (아래 예 참조)

/**
 *  Pork.FormValidator
 *  Validates arrays or properties by setting up simple arrays.
 *  Note that some of the regexes are for dutch input!
 *  Example:
 *
 *  $validations = array('name' => 'anything','email' => 'email','alias' => 'anything','pwd'=>'anything','gsm' => 'phone','birthdate' => 'date');
 *  $required = array('name', 'email', 'alias', 'pwd');
 *  $sanitize = array('alias');
 *
 *  $validator = new FormValidator($validations, $required, $sanitize);
 *
 *  if($validator->validate($_POST))
 *  {
 *      $_POST = $validator->sanitize($_POST);
 *      // now do your saving, $_POST has been sanitized.
 *      die($validator->getScript()."<script type='text/javascript'>alert('saved changes');</script>");
 *  }
 *  else
 *  {
 *      die($validator->getScript());
 *  }
 *
 * To validate just one element:
 * $validated = new FormValidator()->validate('blah@bla.', 'email');
 *
 * To sanitize just one element:
 * $sanitized = new FormValidator()->sanitize('<b>blah</b>', 'string');
 *
 * @package pork
 * @author SchizoDuckie
 * @copyright SchizoDuckie 2008
 * @version 1.0
 * @access public
 */
class FormValidator
{
    public static $regexes = Array(
            'date' => "^[0-9]{1,2}[-/][0-9]{1,2}[-/][0-9]{4}\$",
            'amount' => "^[-]?[0-9]+\$",
            'number' => "^[-]?[0-9,]+\$",
            'alfanum' => "^[0-9a-zA-Z ,.-_\\s\?\!]+\$",
            'not_empty' => "[a-z0-9A-Z]+",
            'words' => "^[A-Za-z]+[A-Za-z \\s]*\$",
            'phone' => "^[0-9]{10,11}\$",
            'zipcode' => "^[1-9][0-9]{3}[a-zA-Z]{2}\$",
            'plate' => "^([0-9a-zA-Z]{2}[-]){2}[0-9a-zA-Z]{2}\$",
            'price' => "^[0-9.,]*(([.,][-])|([.,][0-9]{2}))?\$",
            '2digitopt' => "^\d+(\,\d{2})?\$",
            '2digitforce' => "^\d+\,\d\d\$",
            'anything' => "^[\d\D]{1,}\$"
    );
    private $validations, $sanatations, $mandatories, $errors, $corrects, $fields;


    public function __construct($validations=array(), $mandatories = array(), $sanatations = array())
    {
        $this->validations = $validations;
        $this->sanitations = $sanitations;
        $this->mandatories = $mandatories;
        $this->errors = array();
        $this->corrects = array();
    }

    /**
     * Validates an array of items (if needed) and returns true or false
     *
     */
    public function validate($items)
    {
        $this->fields = $items;
        $havefailures = false;
        foreach($items as $key=>$val)
        {
            if((strlen($val) == 0 || array_search($key, $this->validations) === false) && array_search($key, $this->mandatories) === false)
            {
                $this->corrects[] = $key;
                continue;
            }
            $result = self::validateItem($val, $this->validations[$key]);
            if($result === false) {
                $havefailures = true;
                $this->addError($key, $this->validations[$key]);
            }
            else
            {
                $this->corrects[] = $key;
            }
        }

        return(!$havefailures);
    }

    /**
     *
     *  Adds unvalidated class to thos elements that are not validated. Removes them from classes that are.
     */
    public function getScript() {
        if(!empty($this->errors))
        {
            $errors = array();
            foreach($this->errors as $key=>$val) { $errors[] = "'INPUT[name={$key}]'"; }

            $output = '$$('.implode(',', $errors).').addClass("unvalidated");';
            $output .= "new FormValidator().showMessage();";
        }
        if(!empty($this->corrects))
        {
            $corrects = array();
            foreach($this->corrects as $key) { $corrects[] = "'INPUT[name={$key}]'"; }
            $output .= '$$('.implode(',', $corrects).').removeClass("unvalidated");';
        }
        $output = "<script type='text/javascript'>{$output} </script>";
        return($output);
    }


    /**
     *
     * Sanitizes an array of items according to the $this->sanitations
     * sanitations will be standard of type string, but can also be specified.
     * For ease of use, this syntax is accepted:
     * $sanitations = array('fieldname', 'otherfieldname'=>'float');
     */
    public function sanitize($items)
    {
        foreach($items as $key=>$val)
        {
            if(array_search($key, $this->sanitations) === false && !array_key_exists($key, $this->sanitations)) continue;
            $items[$key] = self::sanitizeItem($val, $this->validations[$key]);
        }
        return($items);
    }


    /**
     *
     * Adds an error to the errors array.
     */
    private function addError($field, $type='string')
    {
        $this->errors[$field] = $type;
    }

    /**
     *
     * Sanitize a single var according to $type.
     * Allows for static calling to allow simple sanitization
     */
    public static function sanitizeItem($var, $type)
    {
        $flags = NULL;
        switch($type)
        {
            case 'url':
                $filter = FILTER_SANITIZE_URL;
            break;
            case 'int':
                $filter = FILTER_SANITIZE_NUMBER_INT;
            break;
            case 'float':
                $filter = FILTER_SANITIZE_NUMBER_FLOAT;
                $flags = FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND;
            break;
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_SANITIZE_EMAIL;
            break;
            case 'string':
            default:
                $filter = FILTER_SANITIZE_STRING;
                $flags = FILTER_FLAG_NO_ENCODE_QUOTES;
            break;

        }
        $output = filter_var($var, $filter, $flags);
        return($output);
    }

    /**
     *
     * Validates a single var according to $type.
     * Allows for static calling to allow simple validation.
     *
     */
    public static function validateItem($var, $type)
    {
        if(array_key_exists($type, self::$regexes))
        {
            $returnval =  filter_var($var, FILTER_VALIDATE_REGEXP, array("options"=> array("regexp"=>'!'.self::$regexes[$type].'!i'))) !== false;
            return($returnval);
        }
        $filter = false;
        switch($type)
        {
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_VALIDATE_EMAIL;
            break;
            case 'int':
                $filter = FILTER_VALIDATE_INT;
            break;
            case 'boolean':
                $filter = FILTER_VALIDATE_BOOLEAN;
            break;
            case 'ip':
                $filter = FILTER_VALIDATE_IP;
            break;
            case 'url':
                $filter = FILTER_VALIDATE_URL;
            break;
        }
        return ($filter === false) ? false : filter_var($var, $filter) !== false ? true : false;
    }



}

물론, 사용중인 db 유형에 따라 SQL 쿼리 이스케이프 처리도 수행해야합니다 (mysql_real_escape_string ()은 SQL 서버에는 쓸모가 없습니다). ORM과 같은 적절한 애플리케이션 계층에서이를 자동으로 처리하려고합니다. 또한 위에서 언급했듯이 html로 출력하려면 htmlspecialchars와 같은 다른 PHP 전용 함수를 사용하십시오.)

스트립 된 클래스 및 / 또는 태그와 같은 HTML 입력을 실제로 허용하려면 전용 xss 검증 패키지 중 하나에 의존합니다. HTML을 구문 분석하기 위해 자신의 레지스트리를 작성하지 마십시오!


답변

아니 없어.

우선, SQL 주입은 입력 필터링 문제이고 XSS는 하나를 출력하는 출력이므로 코드 수명주기에서이 두 가지 작업을 동시에 실행조차하지 않습니다.

경험의 기본 규칙

  • SQL 쿼리의 경우, 매개 변수를 바인드하거나 (PDO에서와 같이 mysql_real_escape_string()) 쿼리 변수에 드라이버 고유 이스케이프 기능 (예 :)을 사용하십시오.
  • strip_tags()원치 않는 HTML을 필터링하는 데 사용
  • htmlspecialchars()여기에서 두 번째 및 세 번째 매개 변수를 사용하여 다른 모든 출력을 피 하십시오.

답변

XSS 문제를 해결하려면 HTML Purifier를 살펴보십시오 . 상당히 구성 가능하고 적절한 트랙 레코드를 가지고 있습니다.

SQL 주입 공격의 경우 사용자 입력을 확인한 다음 mysql_real_escape_string ()을 통해 실행하십시오. 그러나이 함수는 모든 인젝션 공격을 막지는 못하므로 쿼리 문자열에 덤프하기 전에 데이터를 확인하는 것이 중요합니다.

더 나은 해결책은 준비된 문장을 사용하는 것입니다. PDO 라이브러리 와 mysqli 확장은 다음을 지원합니다.


답변

PHP 5.2는 filter_var 함수를 도입했습니다 .

많은 SANITIZE, VALIDATE 필터를 지원합니다.

http://php.net/manual/en/function.filter-var.php