最高のPHP入力サニタイズ関数は何ですか?


161

すべての文字列を通過させてサニタイズできる関数を考え出そうとしています。それから出てくる文字列がデータベースの挿入に対して安全になるようにします。しかし、そこには非常に多くのフィルタリング機能があり、どの機能を使用する必要があるのか​​わかりません。

空白を埋めるのを手伝ってください:

function filterThis($string) {
    $string = mysql_real_escape_string($string);
    $string = htmlentities($string);
    etc...
    return $string;
}

4
挿入の場合、mysql_real_escape_stringを使用してSQLインジェクションに対してサニタイズするだけで問題ありません。(html出力またはphp数式/関数で)選択されたデータを使用しているときに、htmlentitiesを適用する必要があります
davidosomething

データベース挿入のクリーンアップに固有の回答については、stackoverflow.com / questions / 60174 /…を参照してください(PDOの例が示されています。
パット

回答:


433

やめる!

あなたはここで間違いを犯しています。ああ、いいえ、あなたはあなたのデータを少し安全にするために適切なPHP関数を選びました。それはいいです。間違いは、操作順序と、これらの関数をどこでどのように使用するかです。

ユーザーデータのサニタイズと検証、ストレージへのデータのエスケープ、およびプレゼンテーションへのデータのエスケープの違いを理解することが重要です。

ユーザーデータの無害化と検証

ユーザーがデータを送信するときは、期待どおりのものが提供されていることを確認する必要があります。

サニタイズとフィルタリング

たとえば、数値が予想される場合は、送信するデータが数値である ことを確認してください。ユーザーデータを他のタイプにキャストすることもできます。送信されたものはすべて最初は文字列のように扱われるため、既知の数値データを整数または浮動小数点数に強制すると、サニタイズが迅速かつ簡単になります。

自由形式のテキストフィールドとテキストエリアはどうですか?これらのフィールドに予期しないものが何もないことを確認する必要があります。主に、HTMLコンテンツがないはずのフィールドに実際にHTMLが含まれていないことを確認する必要があります。この問題に対処する方法は2つあります。

まず、でHTML入力のエスケープを試すことができますhtmlspecialcharshtmlentitiesHTMLの無効化には使用しないでください。HTMLは、エンコードする必要があると思われるアクセント付き文字やその他の文字のエンコードも実行するためです。

次に、可能なHTMLをすべて削除してみます。 strip_tags迅速かつ簡単ですが、ずさんなこともします。 HTML Purifierは、すべてのHTMLを取り除き、タグと属性の選択的なホワイトリストを通過させるという、より徹底的な作業を行います。

最新のPHPバージョンにはフィルター拡張機能梱されておりユーザー入力をサニタイズするための包括的な方法を提供します。

検証

送信されたデータに予期しないコンテンツが含まれていないことを確認することは、仕事の半分にすぎません。また、送信したデータに実際に使用できる値が含まれていることを確認する必要もあります。

1から10までの数値が予想される場合は、その値を確認する必要があります。スピナーとステップでこれらの新しい派手なHTML5時代の数値入力の1つを使用している場合は、送信されたデータがステップと一致していることを確認してください。

そのデータがドロップダウンメニューである必要がある場合は、送信した値がメニューに表示されたものであることを確認してください。

他のニーズを満たすテキスト入力についてはどうですか?たとえば、日付の入力は、strtotimeまたはDateTimeクラスを介して検証する必要があります。指定された日付は、予想される範囲内である必要があります。メールアドレスはどうですか?前述のフィルター拡張機能は、アドレスが整形式であることを確認できますが、私はis_emailライブラリーのファンです。

他のすべてのフォームコントロールについて同様です。ラジオボタンがありますか?リストに対して検証します。チェックボックスがありますか?リストに対して検証します。ファイルをアップロードしましたか?ファイルが予期したタイプであることを確認し、ファイル名をフィルタリングされていないユーザーデータのように扱います。

すべての最新のブラウザーには、開発者ツールの完全なセットが組み込まれているため、誰でも簡単にフォームを操作できます。 コードでは、ユーザーがフォームコンテンツのクライアント側の制限をすべて完全に削除したと想定する必要があります

ストレージのためのデータのエスケープ

データが期待される形式であり、期待される値のみが含まれていることを確認したので、そのデータをストレージに永続化することについて心配する必要があります。

すべてのデータストレージメカニズムには、データが適切にエスケープおよびエンコードされるようにする特定の方法があります。SQLを構築している場合、クエリでデータを渡す方法として受け入れられているのは、プレースホルダー付きの準備済みステートメントを使用する方法です。

PHPでほとんどのSQLデータベースを操作するためのより良い方法の1つは、PDO拡張機能です。それは共通のパターンは以下の声明を準備しステートメントに変数をバインド、その後、サーバーへの文と変数を送信します。これまでにPDOを使用したことがない場合は、MySQL指向の非常に優れたチュートリアルがあります。

SQL ServerPostgreSQLSQLite 3など、一部のSQLデータベースにはPHPで独自の拡張機能があります。これらの各拡張機能には、PDOと同じprepare-bind-execute方式で動作するステートメントサポートが用意されています。非標準の機能や動作をサポートするために、PDOの代わりにこれらの拡張機能を使用する必要がある場合があります。

MySQLには、独自のPHP拡張機能もあります。そのうちの2つ。mysqliと呼ばれるものだけを使用したいだけです。古い「mysql」拡張機能は廃止ており、現在の時代に使用するのは安全または正気ではありません。

私は個人的にはmysqliのファンではありません。準備されたステートメントで変数バインディングを実行する方法は柔軟性がなく、使いにくい場合があります。疑わしい場合は、代わりにPDOを使用してください。

SQLデータベースを使用してデータを保存していない場合は、使用しているデータベースインターフェイスのドキュメントを確認して、データを安全に渡す方法を確認してください。

可能な場合は、データベースにデータが適切な形式で格納されていることを確認してください。数値フィールドに数値を格納します。日付フィールドに日付を格納します。浮動小数点フィールドではなく、10進数フィールドにお金を格納します。さまざまなデータ型を適切に格納する方法について、データベースが提供するドキュメントを確認してください。

プレゼンテーション用のデータのエスケープ

ユーザーにデータを表示するときは常に、エスケープしてはいけないことがわかっていない限り、データが安全にエスケープされていることを確認する必要があります。

HTMLを出力するときは、ほとんどの場合、最初にからユーザーが提供したデータを渡す必要がありますhtmlspecialchars。実際には、あなたはときにこれを行うべきではないだけの時間がある知っているユーザーがHTMLを提供することを、あなたがいることを知って、それはすでにホワイトリストを使用して消毒されていますということ。

PHPを使用してJavascriptを生成する必要がある場合があります。JavaScriptには、HTMLと同じエスケープルールはありません。PHPを介してJavascriptにユーザー指定の値を提供する安全な方法は、を使用することjson_encodeです。

もっと

データ検証にはさらに多くのニュアンスがあります。

たとえば、文字セットのエンコーディングは巨大な罠になる可能性があります。アプリケーションは、「UTF-8まで」に概説されているプラ​​クティスに従う必要があります。文字列データを誤った文字セットとして扱う場合に発生する可能性のある架空の攻撃があります。

以前、ブラウザーのデバッグツールについて説明しました。これらのツールは、Cookieデータの操作にも使用できます。 クッキーは信頼できないユーザー入力として扱われるべきです。

データの検証とエスケープは、Webアプリケーションのセキュリティの1つの側面にすぎません。Webアプリケーションへの防御を構築できるように、Webアプリケーションの攻撃方法を自覚する必要があります。


また、それを指定するときは、サポートされているエンコーディングのリストに含まれていることを確認してください。
Charles

3
そして、htmlentitiesをまったく使用しないでください。これは、エンティティのすべての<>文字ではなく、単にを置き換える目的でhtmlspecialcharsに置き換えます
常識

6
htmlspecialchars「ユーザーがデータを送信するとき」の部分と「データを表示するとき」の部分で彼が話すので、2度呼び出さないように注意してください。
Savageman、2010年

2
賛成。SQLインジェクションに関する多くのQ&Aから読んだ最も役立つ回答。
akinuri 2016年

将来のユーザーがより多くのオプションを探索するための多くの説明とリンクを含む、絶対的な品質の回答。私からも1点アップしました...
James Walker

32

SQLインジェクションを防ぐための最も効果的なサニタイズは、を使用しPDOたパラメータ化です。パラメータ化されたクエリを使用すると、クエリがデータから分離されるため、1次のSQLインジェクションの脅威が取り除かれます。

HTMLを削除するという点でstrip_tagsは、HTMLを削除することをお勧めします。すべてを削除するだけだからです。 htmlentitiesそれがどのように聞こえるかを行うので、それも機能します。許可するHTMLを解析する必要がある場合(つまり、一部のタグを許可する場合)、HTML Purifierなどの成熟した既存のパーサーを使用する必要があります。


2
おやおや、HTMLの浄化機能について誰も言及しなかったからといって、その巨大なテキストの壁を書き上げました。;)
Charles

3
出力でHTMLを取り除くだけではいけませんか?IMO入力データを変更してはいけません-いつ必要になるかわからない
Joe Phillips

11

データベース入力-SQLインジェクションを防ぐ方法

  1. 整数型などのデータが実際に整数であることを確認して、データが有効であることを確認してください
    • 非文字列の場合、データが実際に正しいタイプであることを確認する必要があります
    • 文字列の場合、クエリ内で文字列が引用符で囲まれていることを確認する必要があります(明らかに、それ以外の場合は機能しません)
  2. SQLインジェクションを回避しながらデータベースに値を入力します(mysql_real_escape_stringまたはパラメーター化されたクエリ)
  3. データベースから値を取得するときは、HTMLをページに挿入できないようにして(htmlspecialchars)、クロスサイトスクリプティング攻撃を回避してください。

ユーザー入力をデータベースに挿入または更新する前に、ユーザー入力をエスケープする必要があります。これは古い方法です。ここで(おそらくPDOクラスから)パラメーター化されたクエリを使用します。

$mysql['username'] = mysql_real_escape_string($clean['username']);
$sql = "SELECT * FROM userlist WHERE username = '{$mysql['username']}'";
$result = mysql_query($sql);

データベースからの出力-XSS(クロスサイトスクリプティング)を防ぐ方法

htmlspecialchars()データベースからデータを出力する場合にのみ使用します。HTML Purifierについても同様です。例:

$html['username'] = htmlspecialchars($clean['username'])

そして最後に...あなたが要求したもの

パラメータ化されたクエリでPDOオブジェクトを使用する場合(適切な方法)、これを簡単に実現する簡単な方法はありません。しかし、古い「mysql」の方法を使用する場合、これが必要になります。

function filterThis($string) {
    return mysql_real_escape_string($string);
}

5

私の5セント。

誰もがそのmysql_real_escape_string仕組みを理解していません。この関数は、何もフィルタリングまたは「サニタイズ」しません。
したがって、この関数を、注入からあなたを救ういくつかの普遍的なフィルターとして使うことはできません。
がどのように機能し、どこに適用できるかを理解している場合にのみ使用できます。

私がすでに書いた非常によく似た質問に対する答えがあり ます。PHPでデータベースに文字列を送信するとき、htmlspecialchars()を使用して不正な文字を処理するか、正規表現を使用する必要がありますか?
データベース側の安全性の詳細については、クリックしてください。

htmlentitiesに関しては、Charlesがこれらの機能を分離するように言っています。
HTMLの投稿を許可されているadminによって生成されたデータを挿入することを想像してみてください。あなたの機能はそれを台無しにします。

htmlentitiesに対してはお勧めしますが。この機能はずっと前に廃止されました。HTMLの安全性のために、、、および文字のみを置き換える場合<>"その目的のために意図的に開発された関数(htmlspecialchars()関数)を使用します。


1
mysql_real_escape_string文字列内の必要な文字をエスケープします。これは厳密にフィルタリングしたりサニタイズしたりするものではありませんが、文字列を引用符で囲むこともそうではありません(そして、誰もがそうしています。それで、SQLを書くときに何も無害化されませんか?もちろん違います。SQLインジェクションを妨げているのはの使用ですmysql_real_escape_string。また、引用符で囲みますが、誰もがそれを行います。そして、あなたが何をするかをテストすると、この省略によりSQL構文エラーが発生します。本当に危険な部分はで処理されmysql_real_escape_stringます。
Savageman、2010年

@サベージマンごめん、ごめんなさい。mysql_real_escape_stringの動作を理解していない。これらの「必要な文字」は引用です。この関数も引用符も単独では何もサニタイズしません。これら2つは一緒にのみ機能します。クエリ文字列を「インジェクションから安全」ではなく、構文的に正しいものにする。そして、どのような構文エラーが発生しますWHERE id = 1か?;)
常識2010年

WHERE my_field = two words(引用符なしで)構文エラーを取得してみてください。引用はエスケープもエスケープも不要で、数値チェックだけなので、あなたの例は悪いです。また、私は引用符が役に立たないと言っていませんでした。私は誰もがそれらを使用するので、これがSQLインジェクションに関する問題の原因ではないことを述べました。
Savageman、2010年

1
@Savagemanそう、私は言った:それがどのように機能し、どこに適用できるかを理解している場合にのみ使用できます。mysql_real_escape_stringはどこにも適用できるわけではないことを認めました。everyone use themあなたはここにSOのコードを確認することができます。多くの人々は数字で引用符を使用しません。図を行きます。私がここであなたが言ったことを話し合っていないことを心に留めておいてください。基本的なデータベースの安全規則について説明しています。空の議論の代わりに学ぶ方がよいでしょう。引用やキャスティングについては誰も言及していませんが、m_r_e_sはまるで魔法のようです。私が話していること
常識

1
1つだけでなく、@ Charles。初心者として、データベースの相互作用...入力と表示を安全にすること、特殊文字、注入の問題は、非常に急な学習曲線でした。あなたの投稿と彼(および他のPHPの他の質問への回答)を読むことは、私を大いに助けてくれました。すべての入力に対するTx
James Walker

2

データベースの挿入の場合、必要なのはmysql_real_escape_string(またはパラメーター化されたクエリを使用する)だけです。通常、データを保存する前に変更したくありません。これは、を使用しhtmlentitiesた場合に起こります。後でhtmlentitiesもう一度実行してWebページのどこかに表示すると、文字化けが発生します。

htmlentitiesWebページのどこかにデータを表示しているときに使用します。

やや関連性があります。たとえば、連絡先フォームなど、送信されたデータを電子メールのどこかに送信する場合は、ヘッダーで使用されるすべてのデータ(From:の名前と電子メールアドレス、subectなど)から改行を削除してください。 )

$input = preg_replace('/\s+/', ' ', $input);

これを行わなければ、スパムボットがフォームを見つけて悪用するのは時間の問題です。私は難しい方法を学びました。



2

使用しているデータの種類によって異なります。使用するのに一般的に最適なのは1つですmysqli_real_escape_stringが、たとえば、HTMLコンテンツがないことがわかっているため、strip_tagsを使用するとセキュリティがさらに強化されます。

許可してはいけないことがわかっている文字を削除することもできます。


1

私は常にGUMPのような小さな検証パッケージを使用することをお勧めします:https : //github.com/Wixel/GUMP

このようなライブラリを取り巻く基本的な機能をすべて構築し、衛生を忘れることはほぼ不可能です。「mysql_real_escape_string」は、優れたフィルタリングの最良の代替手段ではありません(「あなたの常識」の説明のように)-一度だけ使用するのを忘れると、システム全体がインジェクションやその他の厄介な攻撃によって攻撃可能になります。


1

ここでmysql_real_escape_stringについて話してそれに依存しているすべての人にとって、その関数はPHP5では廃止され、PHP7では存在しなくなったことに注意する必要があります。

IMHOこのタスクを実行する最良の方法は、PDOを使用してパラメーター化されたクエリを使用し、データベースと対話することです。これを確認してください:https : //phpdelusions.net/pdo_examples/select

ユーザー入力の処理には常にフィルターを使用してください。http://php.net/manual/es/function.filter-input.phpを参照してください


これは実際には質問の答えにはなりません。回答を修正してソリューションを含めることを検討してください。
クリス2018年

あなたがそれを好き願っています!
Kuntur 2018年

私がやります。素敵な答え!
クリス2018年

PHP 7 mysqli_real_escape_string()で利用できることに注意してください。
Chris

こんにちはクリス、ここで公開されたソリューションはmysql_real_escape_stringを参照しましたが、PHP7ではもう存在しないことをこれから読んだ人に気づき、mysqliではなくPDO(およびフィルター)を使用する代替案を提案しました。提案を使用して解決策を説明するメモを自由に追加してください。よろしくお願いします
Kuntur 2018

0

次のようなコードでmysql_real_escape_string()を使用します。

$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
  mysql_real_escape_string($user),
  mysql_real_escape_string($password)
);

ドキュメントが言うように、その目的は、接続の現在の文字セットを考慮に入れて、引数として渡された文字列内の特殊文字をエスケープして、mysql_query()に安全に配置できるようにすることです。ドキュメントには以下も追加されます:

バイナリデータを挿入する場合は、この関数を使用する必要があります。

htmlentities()は、HTMLコンテンツで文字列を出力するときに、エンティティの一部の文字を変換するために使用されます。


0

これは、私が現在実践している方法の1つです。

  1. csrf、salt temptトークンをユーザーが行うリクエストとともに埋め込み、リクエストからそれらをすべて検証します。ここを参照
  2. クライアント側のCookieに依存しすぎないようにし、サーバー側のセッションの使用を練習してください
  3. データを解析するときは、データタイプと転送方法(POSTやGETなど)のみを受け入れるようにしてください。
  4. ur webApp / Appには必ずSSLを使用してください
  5. スパム要求を意図的に制限するために、タイムベースセッション要求も生成するようにしてください。
  6. データがサーバーに解析されるとき、リクエストがjson、htmlなどの必要なデータメソッドで行われることを確認してから、続行してください
  7. realescapestringなどのエスケープタイプを使用して、入力からすべての不正な属性をエスケープします。
  8. その後、データタイプuのクリーンな形式のみをユーザーに要求することを確認します。
    例:
    -メール:入力が有効なメール形式かどうかを確認します
    -テキスト/文字列:入力のみがテキスト形式(文字列)
    のみであることを確認します-番号:数値形式のみが許可されていることを確認します。
    -etc. Pelaseは、phpポータルからのphp入力検証ライブラリーを参照します
    -検証されたら、準備されたSQLステートメント/ PDOを使用して続行してください。
    -完了したら、必ず接続を終了して終了します-完了したら
    、出力値をクリアすることを忘れないでください。

それが私が信じているすべては基本的な秒のために十分です。ハッカーによる主要な攻撃をすべて防ぐ必要があります。

サーバー側のセキュリティについては、アクセスの制限とロボットの防止、およびルーティングの防止のためにapache / htaccessを設定することをお勧めします。サーバー側のシステムの秒以外にも、サーバー側のセキュリティについて行うべきことがたくさんあります。

あなたはhtaccess apache secレベル(一般的なrpactices)からsecのコピーを学び、得ることができます


0
function sanitize($string,$dbmin,$dbmax){
$string = preg_replace('#[^a-z0-9]#i', '', $string); //useful for strict cleanse, alphanumeric here
$string = mysqli_real_escape_string($con, $string); //get ready for db
if(strlen($string) > $dbmax || strlen($string) < $dbmin){
    echo "reject_this"; exit();
    }
return $string;
}

0

これはどうですか

$string = htmlspecialchars(strip_tags($_POST['example']));

またはこれ

$string = htmlentities($_POST['example'], ENT_QUOTES, 'UTF-8');
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.