PHPでユーザー入力をサニタイズするにはどうすればよいですか?


1124

特定のタイプのHTMLタグを許可しながら、SQLインジェクションとXSS攻撃のユーザー入力をサニタイズするために適切に機能するキャッチオール関数はどこにありますか?


42
現在、SQLインジェクションを回避するには、PDOまたはMySQLiを使用します。
Francisco Presencia 2013

76
PDOまたはMySQLiを使用するだけでは十分ではありません。のような信頼できないデータを使用してSQLステートメントを作成する場合select * from users where name='$name'、PDOを使用するか、MySQLiまたはMySQLを使用するかは問題ではありません。あなたはまだ危険にさらされています。パラメータ化されたクエリを使用する必要があります。必要な場合は、データに対してエスケープメカニズムを使用しますが、これはあまり好ましくありません。
アンディレスター

26
@AndyLester誰かが準備されたステートメントなしでPDOを使用することを暗示していますか?:)

64
「PDOまたはMySQLiを使用する」は、それらを安全に使用する方法について初心者に説明するのに十分な情報ではないと言っています。あなたと私は準備されたステートメントが重要であることを知っていますが、私はこの質問を読む誰もがそれを知っているとは思いません。そのため、明示的な指示を追加しました。
アンディレスター2014年

30
アンディのコメントは完全に有効です。私は最近、MySQL WebサイトをPDOに変換しました。注入攻撃から何とか安全になったと思います。私のSQLステートメントの一部がまだユーザー入力を使用して構築されていることに気付いたのは、このプロセス中だけでした。次に、準備済みステートメントを使用して修正しました。多くの専門家がPDOの使用についてコメントを投げているが、準備されたステートメントの必要性を明記していないため、完全な初心者には違いがあることは完全に明らかではありません。これは明白であるという仮定です。しかし、初心者には。
GhostRider 2014年

回答:


1184

ユーザー入力をフィルタリングできるというのはよくある誤解です。PHPには、このアイデアに基づいて作成されたmagic-quotesと呼ばれる(現在は非推奨の)「機能」もあります。それはナンセンスです。フィルタリング(またはクリーニング、または他の人が呼ぶもの)を忘れてください。

問題を回避するためにすべきことは非常に簡単です。外部コード内に文字列を埋め込むときはいつでも、その言語の規則に従って、文字列をエスケープする必要があります。たとえば、MySQLをターゲットとするSQLに文字列を埋め込む場合、この目的のためにMySQLの関数で文字列をエスケープする必要があります(mysqli_real_escape_string)。(または、データベースの場合、可能であれば、準備済みステートメントを使用する方がより良いアプローチです。)

別の例はHTMLです。HTMLマークアップ内に文字列を埋め込む場合は、でエスケープする必要がありますhtmlspecialchars。つまり、すべてのechoor printステートメントでを使用する必要がありますhtmlspecialchars

3番目の例はシェルコマンドです。外部コマンドに文字列(引数など)を埋め込み、それらをexecで呼び出す場合は、escapeshellcmdおよびを使用する必要がありescapeshellargます。

などなど...

唯一あなたがプリフォーマット入力を受け付けている場合は、積極的にフィルタデータを必要とする場合は、です。たとえば、ユーザーにHTMLマークアップの投稿を許可した場合、そのマークアップをサイトに表示することを計画します。ただし、これをどのようにうまくフィルタリングしても、常にセキュリティホールになる可能性があるため、これを絶対に回避することをお勧めします。


245
「これは、すべてのechoまたはprintステートメントがhtmlspecialcharsを使用する必要があることを意味します」-もちろん、「ユーザー入力を出力するすべての...ステートメント」を意味します。htmlspecialchars()-ifying "echo 'Hello、world!';" クレイジーになるだろう;)
ボビージャック

10
フィルタリングが適切な解決策であると私が考える1つのケースがあります:UTF-8。アプリケーション全体で無効なUTF-8シーケンスを使用したくない場合(コードパスによって異なるエラー回復が発生する可能性があります)、UTF-8は簡単にフィルター処理(または拒否)できます。
Kornel

6
@jbyrd-いいえ、LIKEは特殊な正規表現言語を使用します。入力文字列は2回エスケープする必要があります。1回は正規表現で、もう1回はmysql文字列エンコーディングです。コード内のコード内のコードです。
troelskn 2011年

6
現時点でmysql_real_escape_stringは非推奨です。準備されたステートメントを使用して SQLインジェクションを防ぐことは、今日では良い習慣と考えられています。MySQLiまたはPDOに切り替えてください。
Marcel Korpel 2013年

4
攻撃面を制限するため。(入力時に)早期にサニタイズする場合は、アプリケーションに不正なデータが侵入する可能性のある他のホールがないことを確認する必要があります。一方、遅くする場合、出力関数は安全なデータが与えられていることを「信頼」する必要はありません。単にすべてが安全でないと想定しているだけです。
troelskn 14

217

入力データをサニタイズしてSQLインジェクションを防止しようとしないでください。

代わりに、SQLコードの作成にデータの使用を許可しないでください。バインドされた変数を使用する準備済みステートメントを使用します(つまり、テンプレートクエリのパラメーターを使用します)。これは、SQLインジェクションに対して保証される唯一の方法です。

SQLインジェクションの防止の詳細については、私のWebサイトhttp://bobby-tables.com/を参照してください。


18
または、公式ドキュメントにアクセスして、PDOと準備されたステートメントを学びます。小さな学習曲線ですが、SQLをよく知っていれば、順応するのに問題はありません。
コーダー、2014年

2
SQLインジェクションの特定のケースでは、これが正解です。
Scott Arciszewski、2015年

4
準備されたステートメントはセキュリティを追加しませんが、パラメータ化されたクエリは追加することに注意してください。PHPで一緒に使用するのは非常に簡単です。
基本的な

唯一の保証された方法ではありません。入力を16進数にして、クエリを16進数にしないこともできます。また、ヘクシング権を使用している場合、ヘクス攻撃は不可能です。
Ramon Bakker、2016

メールアドレスやユーザー名などの特別なものを入力している場合はどうなりますか?
アブラハムブルックス2017年

79

いいえ。データの目的に関するコンテキストがなければ、データを一般的にフィルター処理することはできません。入力として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;
    }       



}

もちろん、使用しているデータベースのタイプに応じて、SQLクエリのエスケープも行う必要があることを覚えておいてください(たとえば、mysqlサーバーでは、mysql_real_escape_string()は役に立たない)。おそらく、ORMのような適切なアプリケーション層でこれを自動的に処理する必要があります。また、上記のように:htmlに出力するには、htmlspecialcharsのような他のphp専用関数を使用してください;)

削除されたクラスやタグのようなHTML入力を実際に許可するには、専用のxss検証パッケージの1つに依存します。HTMLを解析するために独自の正規表現を記述しないでください!


18
これは、入力を検証するための便利なスクリプトのようですが、質問とはまったく関係ありません。
rjmunro 2011

43

いいえ、ありません。

まず第一に、SQLインジェクションは入力フィルタリングの問題であり、XSSは出力をエスケープする問題なので、コードのライフサイクルでこれら2つの操作を同時に実行することすらありません。

基本的な経験則

  • SQLクエリの場合は、パラメーターをバインド(PDOと同様)するか、クエリ変数(などmysql_real_escape_string())にドライバー固有のエスケープ関数を使用します
  • strip_tags()不要なHTMLを除外するために使用します
  • 他のすべての出力をエスケープしhtmlspecialchars()、ここで2番目と3番目のパラメーターに注意してください。

1
したがって、入力にそれぞれHTMLを取り除くかエスケープしたいHTMLがあることがわかっている場合にのみ、strip_tags()またはhtmlspecialchars()を使用します。セキュリティ上の目的でそれを使用していませんか?また、バインドを行うと、Bobby Tablesなどの機能はどうなりますか?"ロバート '); DROP TABLE学生;-"引用符をエスケープするだけですか?
Robert Mark Bram、

2
データベースに格納され、後でWebページに表示されるユーザーデータがある場合、通常、書き込まれるよりもはるかに多く読み取られませんか?私にとっては、表示するたびにフィルタリングする必要はなく、保存する前に(入力として)一度フィルタリングする方が理にかなっています。私は何かを逃しているのですか、またはこれと受け入れられた答えで、多くの人々が不必要なパフォーマンスのオーバーヘッドに投票しましたか?
jbo5112 2014

2
私にとって最良の答え。それは短く、私に尋ねれば、その問題にうまく対応できます。$ _POSTまたは$ _GETを介して、何らかのインジェクションでPHPを攻撃することは可能ですか、それとも不可能ですか?
Jo Smo

はい、$ post配列と$ get配列はすべての文字を受け入れますが、投稿されたphpページで文字の列挙が許可されている場合は、それらの文字の一部を使用できません。したがって、カプセル化文字( "、 '、`など)をエスケープしないと、攻撃ベクトルが開かれる可能性があります。`文字は見落とされがちで、コマンドライン実行ハックを形成するために使用できます。サニテーションは、ユーザー入力ハッキングを防ぎます。しかし、Webアプリケーションファイアウォールのハッキングには役立ちません
drtechno

22

XSSの問題に対処するには、HTML Purifierをご覧ください。それはかなり構成可能で、まともな実績があります。

SQLインジェクション攻撃については、ユーザー入力を確認してから、mysql_real_escape_string()を使用して実行してください。ただし、この関数はすべての注入攻撃を無効にするわけではないため、クエリ文字列にダンプする前にデータを確認することが重要です。

より良い解決策は、準備されたステートメントを使用することです。PDOライブラリとmysqli拡張モジュールはこれらをサポートしています。


入力のサニタイズなどの「最善の方法」はありません。いくつかのライブラリを使用してください。htmlピュリファイアが適しています。これらのライブラリは何度も叩かれてきました。ですから、自分で思いつくものよりもはるかに防弾です
paan

bioinformatics.org/phplabware/internal_utilities/htmLawedも参照してください。私の理解では、WordPressは古いバージョンのcore.trac.wordpress.org/browser/tags/2.9.2/wp-includes/kses.phpを
Steve Clay

wordpressの問題は、必ずしもデータベース違反を引き起こすphp-sqlインジェクション攻撃ではないということです。XMLクエリがシークレットを明らかにするデータを格納するミスプログラミングされたプラグインは、より問題があります。
drtechno


17

次のようなページが/mypage?id=53あり、WHERE句でIDを使用する特定の状況で役立つ1つのトリックは、IDが確実に整数であることを確認することです。

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

もちろん、これは特定の攻撃を1つだけカットするので、他のすべての答えを読んでください。(そして、はい、上のコードはすばらしいものではないことはわかっていますが、特定の防御策を示しています。)


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 = new PDO( 'mysql:host = localhost; dbname = testdb; charset = UTF8'、$ user、$ password);
      マニュアル
    • $ pdo-> exec( "set names utf8");
      マニュアル
    • $ pdo =新しいPDO(
      "mysql:host = $ host; dbname = $ db"、$ user、$ pass、 
      アレイ(
      PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION、
      PDO :: MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
      )
      );
      マニュアル
    • mysql_set_charset( 'utf8')
      [PHP 5.5.0で廃止、PHP 7.0.0で削除]。
  • 安全な文字セットを使用する:

    • utf8、latin1、asciiを選択します。脆弱な文字セットbig5、cp932、gb2312、gbk、sjisを使用しないでください。
  • 空間化関数を使用する:

    • 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 = new 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 = new 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;
      $ value = intval($ value);
      $ var = filter_var( '0755'、FILTER_VALIDATE_INT、$ options);
    • 文字列の場合:
      is_string()-変数のタイプがストリングかどうかを調べる

      フィルター機能を使用する filter_var()を —指定したフィルターで変数をフィルターします。
      $ email = filter_var($ email、FILTER_SANITIZE_EMAIL); 
      $ newstr = filter_var($ str、FILTER_SANITIZE_STRING);
      より多くの定義済みフィルター
    • filter_input() —特定の外部変数を名前で取得し、オプションでそれをフィルタリングします。
      $ search_html = filter_input(INPUT_GET、 'search'、FILTER_SANITIZE_SPECIAL_CHARS);
    • preg_match() —正規表現一致を実行します。
    • 独自の検証関数を記述します。

11

ここで説明しているのは、2つの別個の問題です。

  1. ユーザー入力データの無害化/フィルタリング。
  2. 出力をエスケープします。

1)ユーザー入力は常に悪いと見なされるべきです。

準備されたステートメント、またはmysql_real_escape_stringでのフィルタリング、あるいはその両方を使用することは間違いなく必須です。PHPにはfilter_inputも組み込まれており、開始するのに適しています。

2)これは大きなトピックであり、出力されるデータのコンテキストによって異なります。HTMLには、htmlpurifierなどのソリューションがあります。経験則として、出力するものは常にエスケープしてください。

どちらの問題も大きすぎて1つの投稿で扱うことはできませんが、詳細を説明する投稿は多数あります。

メソッドPHP出力

より安全なPHP出力


9

PostgreSQLを使用している場合、PHPからの入力はpg_escape_string()でエスケープできます

 $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で使用することをお勧めする関数です。
不可解なツメイ

8

対処する必要がある複数の懸念があるため、キャッチオール機能はありません。

  1. SQLインジェクション -今日、一般に、すべてのPHPプロジェクトはPHPデータオブジェクト(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関数を利用できます。

    • を呼び出すechoprint、ユーザー指定の値を表示htmlspecialcharsするには、データが安全に適切にサニタイズされ、HTMLの表示が許可されている場合を除き、使用します。
    • json_encode PHPからJavaScriptにユーザー指定の値を提供する安全な方法です
  4. exec()またはsystem()関数を使用して、またはbacktickオペレーターに外部シェルコマンドを呼び出しますか?その場合、SQLインジェクションとXSSに加えて、サーバー上で悪意のあるコマンドを実行しているユーザーに対処するという追加の懸念がある可能性がありますescapeshellcmdコマンド全体をエスケープする場合、またはescapeshellarg個々の引数をエスケープする場合に使用する必要があります。


代わりにmb_encode_numericentityを使用できますか?すべてをエンコードするので?
drtechno

@drtechno- #3 XSSのリンクでmb_encode_numericentity説明されていますhtmlspecialchars
webaholik

5

入力のサニタイズとデータのエスケープのミスを回避する最も簡単な方法は、SymfonyNetteなどのPHPフレームワークまたはそのフレームワークの一部(テンプレートエンジン、データベースレイヤー、ORM)を使用することです。

TwigやLatteのようなテンプレートエンジンは、デフォルトで出力をエスケープします。コンテキスト(WebページのHTMLまたはJavascriptの部分)に応じて出力を適切にエスケープしていれば、手動で解決する必要はありません。

フレームワークは入力を自動的にサニタイズしているため、$ _ POST、$ _ GET、または$ _SESSION変数を直接使用するのではなく、ルーティング、セッション処理などのメカニズムを使用する必要があります。

データベース(モデル)レイヤーには、DoctrineのようなORMフレームワークや、Nette DatabaseのようなPDOのラッパーがあります。

あなたはそれについてもっと読むことができます- ソフトウェアフレームワークとは何ですか?


3

php DOMDocumentを使用してhtml出力を作成すると、適切なコンテキストで自動的にエスケープされます。属性(value = "")と<span>の内部テキストが等しくありません。XSSに対して安全であるためにこれを読んでください: OWASP XSS防止チートシート


2

入力を無害化することはありません。

常に出力をサニタイズします。

SQLステートメントに含めるためにデータを安全に変換するために適用する変換は、HTMLに含めるために適用するものとは完全に異なり、Javascriptに含めるために適用するものとは完全に異なり、LDIFに含めるために適用するものとはまったく異なります。 CSSへの組み込みに適用するものとは完全に異なり、Eメールへの組み込みに適用するものとは完全に異なります。

必ず入力検証してください-さらなる処理のためにそれを受け入れる必要があるか、それとも受け入れられないことをユーザーに伝えるべきかを決定してください。ただし、PHPの土地を離れるまでは、データの表現に変更を加えないでください。

ずっと前に、誰かがデータをエスケープするためのすべてのメカニズムに適合する単一サイズを発明しようとしたところ、すべての出力ターゲットのデータを適切にエスケープせず、異なるインストールが機能するために異なるコードを必要とする「magic_quotes」が発生しました。


これに関する1つの問題は、常にデータベースへの攻撃とは限らず、すべてのユーザー入力をシステムから保護する必要があることです。1つの言語タイプだけではありません。したがって、サイトで$ _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ですか?なぜスラッシュを取り除く必要があるのですか?
ダーマン

5
警告:これは魔法のようにユーザーデータを安全にするものではありません。この機能は、何も保護せずにデータを不必要に損傷します。使用しないでください。
ダーマン

あなたの発言は誤りです。
Erik Thiart、

0

フィルター拡張機能(howto-linkmanual)があり、すべてのGPC変数で非常にうまく機能します。しかし、それはすべてを実行する魔法ではありませんが、それでも使用する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.