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


1124

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


42
요즘에는 SQL 주입을 피하려면 PDO 또는 MySQLi를 사용하십시오.
Francisco Presencia

76
PDO 나 MySQLi만으로는 충분하지 않습니다. 과 같은 신뢰할 수없는 데이터로 SQL 문을 작성하는 select * from users where name='$name'경우 PDO, MySQLi 또는 MySQL을 사용하는지 여부는 중요하지 않습니다. 당신은 여전히 ​​위험에 처해 있습니다. 매개 변수화 된 쿼리를 사용해야하거나 필요한 경우 데이터에 이스케이프 메커니즘을 사용해야하지만 훨씬 덜 바람직합니다.
Andy Lester

26
@AndyLester 누군가가 준비된 진술없이 PDO를 사용한다는 것을 의미합니까? :)

63
"PDO 또는 MySQLi 사용"은 초보자에게 안전하게 사용하는 방법을 설명하기에 충분한 정보가 아닙니다. 당신과 나는 준비된 진술이 중요하다는 것을 알고 있지만,이 질문을 읽는 모든 사람이 그것을 알고 있다고 생각하지는 않습니다. 그렇기 때문에 명시적인 지침을 추가했습니다.
Andy Lester

30
Andy의 의견은 전적으로 유효합니다. 최근에 mysql 웹 사이트를 PDO로 변환하여 최근에 어떤 방식 으로든 주입 공격으로부터 안전하다고 생각했습니다. 프로세스 중에 만 내 SQL 문 중 일부가 여전히 사용자 입력을 사용하여 작성되었다는 것을 깨달았습니다. 그런 다음 준비된 진술을 사용하여 그 문제를 해결했습니다. 완전한 초보자에게는 많은 전문가들이 PDO 사용에 대한 의견을 던지지 만 준비된 진술의 필요성을 명시하지 않는 점이 명확하지 않습니다. 이것이 명백하다는 가정. 그러나 초보자에게는 아닙니다.
GhostRider

답변:


1183

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

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

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

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

그리고 등등 ...

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


245
"이것은 모든 단일 echo 또는 print 문이 htmlspecialchars를 사용해야한다는 것을 의미합니다." "물론 ... 사용자 입력을 출력하는 모든 문"을 의미합니다. htmlspecialchars ()-ifying "echo 'Hello, world!';" 미친 것 같아요;)
바비 잭

10
필터링이 올바른 솔루션이라고 생각하는 경우가 있습니다 : UTF-8. 응용 프로그램 전체에서 유효하지 않은 UTF-8 시퀀스를 원하지 않으면 (코드 경로에 따라 다른 오류 복구가 발생할 수 있음) UTF-8을 쉽게 필터링 (또는 거부) 할 수 있습니다.
Kornel

6
@jbyrd-아니요, LIKE는 특수 정규 표현식 언어를 사용합니다. 정규 표현식과 mysql 문자열 인코딩에 대해 한 번 입력 문자열을 두 번 이스케이프해야합니다. 코드 내의 코드 내의 코드입니다.
troelskn

6
현재 mysql_real_escape_string는 더 이상 사용되지 않습니다. 오늘날 SQL 주입을 방지하기 위해 준비된 명령문 을 사용하는 것이 좋습니다 . 따라서 MySQLi 또는 PDO로 전환하십시오.
Marcel Korpel 2016 년

4
공격 영역을 제한하기 때문입니다. 조기에 (입력시) 위생 처리하는 경우 응용 프로그램에 잘못된 데이터가 들어갈 수있는 다른 구멍이 없는지 확인해야합니다. 늦게 출력하는 경우 출력 함수는 안전한 데이터가 제공된다고 "신뢰"할 필요가 없으며 모든 것이 안전하지 않다고 가정합니다.
troelskn

217

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

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

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


18
또는 공식 문서를 방문하여 PDO 및 준비된 진술을 배우십시오. 작은 학습 곡선이지만 SQL을 잘 알고 있으면 적응하는 데 아무런 문제가 없습니다.
코더

2
SQL 인젝션의 구체적인 사례를 들어, 정답입니다!
Scott Arciszewski 2:30에

4
준비된 명령문은 보안을 추가하지 않으며 매개 변수화 된 쿼리에 추가됩니다. PHP에서 함께 사용하기가 매우 쉽습니다.
기본

유일한 보장 방법은 아닙니다. 16 진수 입력과 쿼리의 16 진수도 방지합니다. 16 진수를 올바르게 사용하면 16 진수 공격도 불가능합니다.
Ramon Bakker

이메일 주소 나 사용자 이름과 같은 특수한 정보를 입력하면 어떻게 되나요?
Abraham Brookes

78

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

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

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


52

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을 구문 분석하기 위해 자신의 레지스트리를 작성하지 마십시오!


17
이것은 입력을 확인하는 편리한 스크립트 인 것처럼 보이지만 질문과 는 전혀 관련이 없습니다.
rjmunro

43

아니 없어.

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

경험의 기본 규칙

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

1
따라서 입력에 각각 제거하거나 이스케이프하려는 HTML이 있음을 알고있을 때 strip_tags () 또는 htmlspecialchars () 만 사용하십시오. 보안 목적으로 사용하지 않습니까? 또한 바인드를 할 때 바비 테이블과 같은 것들에 대해 무엇을합니까? "로버트"); DROP 테이블 학생;- "그냥 따옴표를 이스케이프합니까?
Robert Mark Bram 1

2
데이터베이스에 들어가서 나중에 웹 페이지에 표시되는 사용자 데이터가있는 경우 일반적으로 작성된 것보다 더 많이 읽지 않습니까? 나에게 그것을 표시 할 때마다 필터링하지 않고 저장하기 전에 한 번 (입력으로) 필터링하는 것이 더 합리적입니다. 내가 누락 된 것이 있습니까? 많은 사람들이 이것과 허용되는 답변에서 불필요한 성능 오버 헤드에 투표합니까?
jbo5112 14

2
나에게 가장 좋은 대답. 당신이 저에게 묻는다면 그것은 짧고 질문을 잘 해결합니다. 약간의 주입으로 $ _POST 또는 $ _GET을 통해 PHP를 공격 할 수 있습니까?
Jo Smo

예, $ post 및 $ get 배열은 모든 문자를 허용하지만 게시 된 PHP 페이지에서 문자를 열거 할 수 있으면 해당 문자 중 일부를 사용할 수 있습니다. 따라서 캡슐화 문자 ( ", '및`등)를 피하지 않으면 공격 벡터가 열릴 수 있습니다.`문자가 종종 누락되고 명령 행 실행 해킹을 형성하는 데 사용될 수 있습니다. 위생은 사용자 입력 해킹을 방지합니다. 웹 응용 프로그램 방화벽 해킹에는 도움이되지 않습니다
drtechno

22

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

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

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


입력을 살균하는 것과 같은 것을하는 "최상의 방법"은 없습니다.. 일부 라이브러리를 사용하면 html purifier가 좋습니다. 이 라이브러리는 여러 번 두드리고 있습니다. 그래서 그것은 당신이 스스로 올 수있는 것보다 훨씬 더 방탄입니다
paan


워드 프레스의 문제점은 데이터베이스 침입을 유발하는 PHP-SQL 주입 공격 일 필요는 없다는 것입니다. xml 쿼리가 비밀을 드러내는 데이터를 저장하는 프로그래밍 된 플러그인이 더 문제가됩니다.
drtechno


17

페이지 /mypage?id=53가 있고 WHERE 절에서 id를 사용 하는 특정 상황에서 도움이 될 수있는 한 가지 트릭은 id가 반드시 정수인지 확인하는 것입니다.

if (isset($_GET['id'])) {
  $id = $_GET['id'];
  settype($id, 'integer');
  $result = mysql_query("SELECT * FROM mytable WHERE id = '$id'");
  # now use the result
}

그러나 물론 특정 공격 하나만 차단하므로 다른 모든 답변을 읽으십시오. (그리고 위의 코드는 훌륭하지 않지만 특정 방어를 보여줍니다.)


11
대신 $ id = intval ($ id)을 사용합니다 :)
Duc Tran

캐스트 정수는 숫자 데이터 만 삽입되도록하는 좋은 방법입니다.
테스트

1
$id = (int)$_GET['id']그리고 $que = sprintf('SELECT ... WHERE id="%d"', $id)좋은 너무
vladkras

16

PHP로 사용자 입력을 삭제하는 방법 :

  • 최신 버전의 MySQL 및 PHP를 사용하십시오.

  • 문자셋을 명시 적으로 설정하십시오 :

    • $ mysqli-> set_charset ( "utf8");
      설명서
    • $ pdo = 새로운 PDO ( 'mysql : host = localhost; dbname = testdb; charset = UTF8', $ user, $ password);
      설명서
    • $ pdo-> exec ( "세트 이름 utf8");
      설명서
    • $ pdo = 새로운 PDO (
      "mysql : host = $ host; dbname = $ db", $ user, $ pass, 
      정렬(
      PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION,
      PDO :: MYSQL_ATTR_INIT_COMMAND => "이름 설정 utf8"
      )
      );
      설명서
    • mysql_set_charset ( 'utf8')
      [PHP 5.5.0에서 더 이상 사용되지 않고 PHP 7.0.0에서 제거됨].
  • 안전한 문자 세트를 사용하십시오.

    • 취약한 문자셋 big5, cp932, gb2312, gbk, sjis를 사용하지 말고 utf8, latin1, ascii ..를 선택하십시오.
  • 공간 함수 사용 :

    • MySQLi 준비 문장 :
      $ stmt = $ mysqli-> prepare ( 'SELECT * FROM test WHERE name =? LIMIT 1') ;; 
      $ param = " 'OR 1 = 1 / *";
      $ stmt-> bind_param ( 's', $ param);
      $ stmt-> execute ();
    • PDO :: quote () -입력 문자열을 따옴표로 묶고 ( 필요한 경우) 기본 드라이버에 적합한 따옴표 스타일을 사용하여 입력 문자열 내에서 특수 문자를 이스케이프합니다.

      $ pdo = 새로운 PDO ( 'mysql : host = localhost; dbname = testdb; charset = UTF8', $ user, $ password); 문자 세트를 명시 적으로 설정
      $ pdo-> setAttribute (PDO :: ATTR_EMULATE_PREPARES, false); MySQL이 기본적으로 (주입을 막기 위해) 준비 할 수없는 명령문을 에뮬레이션으로 대체하지 못하도록 준비된 명령문 에뮬레이션을 사용하지 않도록 설정
      $ var = $ pdo-> quote ( " 'OR 1 = 1 / *"); 리터럴을 이스케이프 할뿐만 아니라 따옴표 (작은 따옴표 '문자)로도 인용합니다. $ stmt = $ pdo-> query ( "SELECT * FROM test WHERE name = $ var LIMIT 1") ;;

    • PDO 준비된 명령문 : vs MySQLi 준비된 명령문은 더 많은 데이터베이스 드라이버 및 명명 된 매개 변수를 지원합니다.

      $ pdo = 새로운 PDO ( 'mysql : host = localhost; dbname = testdb; charset = UTF8', $ user, $ password); 문자 세트를 명시 적으로 설정
      $ pdo-> setAttribute (PDO :: ATTR_EMULATE_PREPARES, false); MySQL이 기본적으로 준비 할 수없는 명령문을 에뮬레이션으로 대체 (삽입 방지)하기 위해 준비된 명령문 에뮬레이션 사용 안함 $ stmt = $ pdo-> prepare ( 'SELECT * FROM test WHERE name =? LIMIT 1') ;; $ stmt-> execute ([ " 'OR 1 = 1 / *"]);

    • mysql_real_escape_string [PHP 5.5.0에서 더 이상 사용되지 않고 PHP 7.0.0에서 제거됨]
    • mysqli_real_escape_string 연결의 현재 문자 세트를 고려하여 SQL 문에서 사용하기 위해 문자열에서 특수 문자를 이스케이프합니다. 그러나 준비된 명령문은 단순히 이스케이프 된 문자열이 아니기 때문에 사용하는 것이 좋습니다. 명령문은 사용할 테이블 및 인덱스를 포함하여 완전한 쿼리 실행 계획을 제시하며, 최적화 된 방법입니다.
    • 쿼리 내 변수 주위에 작은 따옴표 ( '')를 사용하십시오.
  • 변수에 예상되는 내용이 포함되어 있는지 확인하십시오.

    • 정수가 필요한 경우 다음을 사용하십시오.
      ctype_digit — 숫자를 확인합니다. 
      $ value = (int) $ 값;
      $ value = intval ($ 값);
      $ var = filter_var ( '0755', FILTER_VALIDATE_INT, $ options);
    • 문자열의 경우 :
      is_string () — 변수의 타입이 문자열인지 확인

      필터 기능 사용 filter_var () — 지정된 필터로 변수를 필터링합니다.
      $ email = filter_var ($ 이메일, FILTER_SANITIZE_EMAIL);
      $ newstr = filter_var ($ str, FILTER_SANITIZE_STRING);
      더 사전 정의 된 필터
    • filter_input () — 이름으로 특정 외부 변수를 가져오고 선택적으로 필터링합니다.
      $ search_html = filter_input (INPUT_GET, '검색', FILTER_SANITIZE_SPECIAL_CHARS);
    • preg_match () — 정규식 일치를 수행합니다.
    • 자체 검증 기능을 작성하십시오.

11

여기서 설명하는 것은 두 가지 별도의 문제입니다.

  1. 사용자 입력 데이터의 살균 / 필터링.
  2. 이스케이프 출력.

1) 사용자 입력은 항상 나쁜 것으로 가정해야합니다.

준비된 명령문을 사용하거나 mysql_real_escape_string으로 필터링하는 것은 필수입니다. PHP에는 filter_input도 내장되어있어 시작하기에 좋습니다.

2) 이것은 큰 주제이며 출력되는 데이터의 컨텍스트에 따라 다릅니다. HTML의 경우 htmlpurifier와 같은 솔루션이 있습니다. 경험상 항상 출력하는 것을 피하십시오.

두 문제 모두 단일 게시물에 들어가기에는 너무 크지 만 더 자세한 게시물이 많이 있습니다.

PHP 출력 방법

보다 안전한 PHP 출력


9

PostgreSQL을 사용하는 경우 pg_escape_string ()을 사용하여 PHP 입력을 이스케이프 처리 할 수 ​​있습니다

 $username = pg_escape_string($_POST['username']);

설명서에서 ( http://php.net/manual/es/function.pg-escape-string.php ) :

pg_escape_string ()은 데이터베이스를 쿼리하기위한 문자열을 이스케이프합니다. PostgreSQL 형식으로 이스케이프 된 문자열을 따옴표없이 반환합니다.


1
pg_escape_literal () 은 PostgreSQL에 권장되는 함수입니다.
cryptic ツ

8

다루어야 할 여러 가지 우려가 있기 때문에 catchall 기능은 없습니다.

  1. SQL 인젝션 -오늘날 모든 PHP 프로젝트는 PHP Data Objects (PDO)통해 준비된 명령문을 모범 사례 로 사용하여 흩어져있는 따옴표로 인한 오류와 인젝션에 대한 모든 기능을 갖춘 솔루션을 방지해야합니다 . 또한 데이터베이스에 액세스 할 수있는 가장 유연하고 안전한 방법입니다.

    PDO 에 대해 알아야 할 모든 내용 은 (유일한) PDO 튜토리얼 을 확인하십시오 . (이 주제에 대한 훌륭한 자료를 주신 SO 개발자 (@YourCommonSense)에게 감사드립니다.)

  2. XSS-도중에 데이터를 삭제합니다 ...

    • HTML Purifier 는 오랫동안 사용되어 왔으며 여전히 활발하게 업데이트되고 있습니다. 이 태그를 사용하면 악의적 인 입력을 삭제하는 동시에 관대하고 구성 가능한 태그의 허용 목록을 허용 할 수 있습니다. 많은 WYSIWYG 편집기에서 잘 작동하지만 일부 사용 사례에서는 무거울 수 있습니다.

    • HTML / Javascript를 전혀 받아들이고 싶지 않은 다른 경우에는이 간단한 기능이 유용하다는 것을 알았습니다 (XSS에 대해 여러 감사를 통과했습니다).

      /* Prevent XSS input */ function sanitizeXSS () { $_GET = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING); $_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING); $_REQUEST = (array)$_POST + (array)$_GET + (array)$_REQUEST; }

  3. XSS-나갈 때 데이터 삭제 ... 데이터베이스에 데이터를 추가하기 전에 데이터가 제대로 정리 되었다고 보장하지 않으면 사용자에게 데이터를 표시하기 전에 정리 해야합니다. 다음과 같은 유용한 PHP 기능을 활용할 수 있습니다.

    • 사용자가 제공 한 값 을 호출 echo하거나 print표시 할 때 htmlspecialchars, 데이터가 제대로 안전하고 HTML을 표시 할 수없는 경우를 사용 하십시오.
    • json_encode PHP에서 Javascript로 사용자 제공 값을 제공하는 안전한 방법입니다
  4. exec()또는 system()함수를 사용 하거나 backtick운영자 에게 외부 쉘 명령을 호출 합니까? 그렇다면 SQL Injection & XSS 외에도 서버에서 악의적 인 명령을 실행하는 사용자 를 해결해야 할 추가 문제가있을 수 있습니다 . escapeshellcmd전체 명령을 이스케이프하거나 escapeshellarg개별 인수를 이스케이프 하려면 사용해야 합니다 .


mb_encode_numericentity를 대신 사용할 수 있습니까? 모든 것을 인코딩하기 때문에?
drtechno

@drtechno- mb_encode_numericentityhtmlspecialchars # 3 XSS
webaholik

5

입력 및 데이터를 삭제하는 실수를 피하는 가장 쉬운 방법은 Symfony , Nette 등과 같은 PHP 프레임 워크 또는 해당 프레임 워크의 일부 (템플릿 엔진, 데이터베이스 계층, ORM)를 사용하는 것입니다.

Twig 또는 Latte 와 같은 템플릿 엔진 은 기본적으로 출력 이스케이프를 설정합니다. 컨텍스트 (웹 페이지의 HTML 또는 Javascript 부분)에 따라 출력을 올바르게 이스케이프 한 경우 수동으로 해결할 필요가 없습니다.

프레임 워크는 입력을 자동으로 삭제하므로 $ _POST, $ _GET 또는 $ _SESSION 변수를 직접 사용하지 말고 라우팅, 세션 처리 등과 같은 메커니즘을 통해 사용해야합니다.

그리고 데이터베이스 (모델) 계층에는 Doctrine과 같은 ORM 프레임 워크 또는 Nette 데이터베이스와 같은 PDO 주변 래퍼가 있습니다.

자세한 내용은 여기를 참조하십시오- 소프트웨어 프레임 워크는 무엇입니까?


3

출력 이스케이프 주제에 추가하고 싶었습니다 .php DOMDocument를 사용하여 html 출력을 만들면 올바른 컨텍스트에서 자동으로 이스케이프됩니다. <span>의 속성 (value = "")과 내부 텍스트가 동일하지 않습니다. XSS로부터 안전을 위해 다음을 읽으십시오 : OWASP XSS Prevention Cheat Sheet


2

당신은 입력을 살균하지 않습니다.

당신은 항상 출력을 소독합니다.

SQL 문에 포함하기에 안전하도록 데이터에 적용하는 변환은 HTML에 포함하도록 적용하는 것과 완전히 다릅니다. Javascript에 포함하도록 신청 한 것과는 완전히 다릅니다. LDIF에 포함하기 위해 적용한 것과는 완전히 다릅니다. CSS에 포함하는 것과 완전히 다른 것은 이메일에 포함하는 것과는 완전히 다릅니다 ....

반드시 입력의 유효성을 검사 하십시오. 추가 처리를 위해 입력 을 수락해야하는지 또는 사용자에게 허용되지 않는지를 알려주십시오. 그러나 PHP 토지를 떠날 때까지 데이터 표현에 변경 사항을 적용하지 마십시오.

오래 전에 누군가가 데이터를 이스케이프하기위한 모든 메커니즘에 맞는 단일 크기를 만들려고했지만 모든 출력 대상에 대한 데이터를 올바르게 이스케이프하지 않고 다른 코드가 필요한 다른 설치가 발생하는 " magic_quotes "로 끝났습니다 .


이것의 한 가지 문제점은 항상 데이터베이스 공격이 아니며 모든 사용자 입력이 시스템으로부터 보호되어야한다는 것입니다. 하나의 언어 유형만이 아닙니다. 따라서 사이트에서 $ _POST 데이터를 열거 할 때 바인딩을 사용하더라도 쉘이나 다른 PHP 코드를 실행하기에 충분하지 않을 수 있습니다.
drtechno

"항상 데이터베이스 공격이 아님": "SQL 문에 포함시키기 위해 데이터에 적용하는 변환은 완전히 다릅니다 ...."
symcbean

"모든 사용자 입력은 시스템으로부터 보호되어야합니다": 시스템은 사용자 입력으로부터 보호되지 않아야합니다.
symcbean

글쎄, 나는 단어가 부족하지만 예, 입력이 시스템 작동에 영향을 미치지 않도록해야합니다. 이것을 명확히하기 위해 ...
drtechno

입력과 출력 모두 위생 처리해야합니다.
Tajni

1

사용자 데이터를 신뢰하지 마십시오.

function clean_input($data) {
  $data = trim($data);
  $data = stripslashes($data);
  $data = htmlspecialchars($data);
  return $data;
}

trim()함수는 문자열의 양쪽에서 공백 및 기타 사전 정의 된 문자를 제거합니다.

stripslashes()기능은 백 슬래시를 제거합니다

htmlspecialchars()함수는 사전 정의 된 일부 문자를 HTML 엔티티로 변환합니다.

사전 정의 된 문자는 다음과 같습니다.

& (ampersand) becomes &amp;
" (double quote) becomes &quot;
' (single quote) becomes &#039;
< (less than) becomes &lt;
> (greater than) becomes &gt;

1
이것이 무엇으로부터 보호됩니까? XSS에 대한 것입니까? 왜 clean_input그때 불려지 나요? 왜 슬래시를 제거하고 싶습니까?
Dharman

4
경고 : 이렇게해도 사용자 데이터가 마술처럼 안전하지는 않습니다. 이 기능은 데이터를 보호하지 않고 불필요하게 데이터를 손상시킵니다. 사용하지 마십시오!
Dharman

당신의 진술은 거짓입니다.
에릭 티아 르트

0

모든 GPC 변수와 잘 작동 하는 필터 확장 ( howto-link , manual )이 있습니다. 그것은 마술처럼 모든 것이 아니지만 여전히 사용해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.