ドロップダウンを使用した場合、SQLインジェクションを防ぐ必要がありますか?


96

主にSQLインジェクションが発生する可能性があるため、フォームからのユーザー入力は絶対に信頼しないでください。

ただし、これはドロップダウンからの入力のみのフォームにも適用されますか(以下を参照)?

私は$_POST['size']、セッション全体を保存して、サイト全体で(mysqli選択クエリを使用して)さまざまなデータベースにクエリを実行します。SQLインジェクションを実行すると、データベースに悪影響が及ぶ可能性があります(ドロップされる可能性があります)。

入力されたユーザー入力でデータベースをクエリする領域はなく、ドロップダウンのみです。

<form action="welcome.php" method="post">
<select name="size">
  <option value="All">Select Size</option> 
  <option value="Large">Large</option>
  <option value="Medium">Medium</option>
  <option value="Small">Small</option>
</select>
<input type="submit">
</form>

112
はい。攻撃者が<select>入力に必要な値を送信することを妨げるものはありません。実際、少し技術的なユーザーでも、ブラウザコンソールを使用してオプションを追加できます。利用可能な値の配列ホワイトリストを保持し、それと入力を比較する場合、それを軽減できます(不要な値を防ぐためです)
Michael Berkowski

13
基本的なリクエスト/レスポンスの事柄と、フロントエンドがリクエストをどのように構築するかは重要ではないこと、つまりこの場合はドロップダウンを理解する必要があります
Royal Bg

13
@YourCommonSenseいい質問だから。誰もがクライアントの操作性を理解しているわけではありません。これはこのサイトへの非常に貴重な答えを引き起こします。
ランチャー

8
@Cruncherなるほど。平均的なstackoverflowianにとって、それは彼らが以前に聞いたロケット科学です。PHPタグの下で最も投票された質問にもかかわらずです。
常識

9
「ユーザー入力を絶対に信用すべきではないことを理解しています。」例外なく。
Prinzhorn

回答:


69

次の例のように簡単なことを行って、投稿されたサイズが期待どおりであることを確認できます。

$possibleOptions = array('All', 'Large', 'Medium', 'Small');

if(in_array($_POST['size'], $possibleOptions)) {
    // Expected
} else {
    // Not Expected
}

結果を保存するために必要なphp> = 5.3.0のバージョンを使用している場合は、mysqli_ *を使用します。正しく使用すれば、SQLインジェクションに役立ちます。


これは「ホワイトリスト」のOliverBSの簡略版ですか?
Tatters

実際には非常に基本的なバージョンだけでなく、データベースに値を追加してチェック対象を簡単にして再利用できるようにすることもできます。データベースを使用したくない場合は、ホワイトリストプロパティがホワイトリストクラスの配列プロパティ内にある可能性があります。
Oliver Bayes-Shelton

9
それでも、Prepared Statements(推奨)またはを使用する必要がありますmysqli_real_escape_string。また、出力時に値を適切にエスケープします(たとえば、HTMLドキュメントでhtmlspecialchars()を使用します)。
ComFreek 2014年

4
厳密な比較のために、の3番目のパラメーターをin_arrayに設定することをお勧めしますtrue。何がうまくいかないのかはわかりませんが、緩い比較はかなり風変わりです。
ブリリアント、

1
@OliverBS $ _POST値は配列にすることもできます。数値文字列は数値として比較されます( '5' == '05')。特定の例ではセキュリティホールはないと思いますが、ルールは複雑であり、私が理解していない理由でホールが開く可能性があります。厳密な比較は推論するのが簡単なので、安全に使用できます。
ブリリアント、

195

はい、これを防ぐ必要があります。

Firefoxの開発者コンソールを使用して、その理由を説明しましょう。

ドロップダウンの値の1つをドロップテーブルステートメントに編集しました

このデータをクレンジングしないと、データベースが破壊されます。(これは完全に有効なSQLステートメントではないかもしれませんが、私は私のポイントを理解したと思います。)

ドロップダウンで使用できるオプションを制限したからといって、サーバーに送信できるデータが制限されたわけではありません

ページの動作を使用してこれをさらに制限しようとした場合、私のオプションにはその動作を無効にするか、このフォームの送信を模倣するカスタムHTTPリクエストをサーバーに書き込むだけです。そこと呼ばれるツールだカールまさにそのために使用さは、と私は考えて、とにかく、このSQLインジェクションを提出するためのコマンドは次のようになります。

curl --data "size=%27%29%3B%20DROP%20TABLE%20*%3B%20--"  http://www.example.com/profile/save

(これは完全に有効なcurlコマンドではない可能性がありますが、繰り返しになりますが、私のポイントが理解できたと思います。)

繰り返しますが、

ユーザー入力を信頼しないでください。常に自分を守ってください。

ユーザー入力が安全であると想定しないでください。フォーム以外の方法で届いたとしても、安全ではない可能性があります。SQLインジェクションから身を守ることを忘れるほど信頼できるものはありません。


24
でカスタムペイロードを作成することは言うまでもありませんcurl常に入力サニタイズサーバー側を
recursion.ninja

14
画像は千以上の言葉を言います。
davidkonrad 2014年

4
これがデフォルトの答えです。また、についての何かを含める必要がありますcurl。HTTPリクエストを任意のフォーマットから任意の場所に送信して値を渡すことができることを人々は理解していません。処理する前にリクエストが有効であることを確認するのはサーバー次第です。
レトロハッカー2014年

2
かわいいね!それは小さなボビーテーブルです!
オーラ

45

この質問は 、これがこの特定の種類の攻撃に関する回答です。

コメントで述べたように、例外なしで、変数データを含むすべてのクエリに対して準備されたステートメントを使用する必要があります

HTMLに関係なく!
SQLクエリは、HTML入力やその他の外部要因に関係なく、適切にフォーマットする必要があることを理解することが不可欠です。

入力検証の目的で他の回答で提案されているホワイトリストを使用できますが、SQL関連のアクションには影響しません。HTML入力を検証したかどうかに関係なく、同じままである必要があります。つまり、クエリに変数を追加する場合でも、準備されたステートメントを使用する必要があります。

ここでは、完全な説明、準備されたステートメントが必須である理由、それらを適切に使用する方法、それらが適用されない場所、およびそのような場合の対処法を見つけることができます。SQLインジェクション保護のヒッチハイカーガイド

また、この質問にはタグが付けられています 。ほとんどの場合、私は偶然と思いますが、とにかく、生のmysqli は古いmysq_ *関数の適切な代用ではないことを警告する必要があります。理由だけで、古いスタイルで使用されている場合、それはまったくセキュリティを追加しません。準備されたステートメントのサポートは苦痛で面倒ですが、平均的なPHPユーザーはそれらをまったく試すことができないほどです。したがって、ORMや何らかの抽象化ライブラリがない場合は、PDOが唯一の選択肢です。


5
i = random(0、15); // iを使用したクエリ。ここでステートメントを準備する必要がありますか?
ランチャー

2
の実装はrandom何ですか?
Slicedpan 2014年

9
@YourCommonSense狭視野?私には「常にXを実行し、私はその理由を提供しません」は狭義に見られます。
ランチャー、

5
@Cruncherいつでも、常に、準備されたステートメントを常に使用しない理由は考えられません。教えてもらえますか?
Wesley Murch、

3
@Cruncherそうですね、あなたはDevil's Advocateをプレイしているようです。回答の規定も「あらゆる可変データを含む」である。PHPのintキャストのようないくつかのケースがありますが、準備されたステートメントを使用する方が良いようです。(経験の浅い)人々に、マイナーパフォーマンスの「ブースト」がセキュリティよりも重要であることを伝えることは、はるかに重要です。答えは「不完全」ですが、他の人が無視しているという強いメッセージを送信します。私はケースを休ませます。
Wesley Murch、

12

はい。

実際に送信される値はだれでも何でも偽装できます-

だから、ドロップダウンメニューを検証するには、作業している値がドロップダウンにあったかどうかを確認するだけです-このようなものが最も(最も正気に妄想的な)方法になるでしょう:

if(in_array($_POST['ddMenu'], $dropDownValues){

    $valueYouUseLaterInPDO = $dropDownValues[array_search("two", $arr)];

} else {

    die("effin h4x0rs! Keep off my LAMP!!"); 

}

5
この回答は不完全です。さらに、準備されたステートメントを使用する必要があります。そのため、値が何に設定されているかに関係なく、注入は不可能です
Slicedpan

7
@Slicedpan本当に?なぜかわからないままにワゴンに飛び乗っているように思えます...クエリへの入力の可能性が少しあり、それらがすべて大丈夫であることを確認できる場合(私が知っているので、作成したので)、その後、準備済みステートメントを使用してもセキュリティ上のメリットはありません
Cruncher

3
慣例として準備済みステートメントの使用を強制することで、将来SQLインジェクションの脆弱性が導入されるのを防ぐことができます。
Slicedpan 2014年

1
@Cruncher「ドロップダウンのすべての値は常に安全である」という約束は、非常に安全ではありません。値は変更され、コードは必ずしも更新されません。値を更新する人は、安全でない値が何であるかさえ知らないかもしれません!特に、あらゆる種類のセキュリティ上の懸念が蔓延しているWebプログラミングの領域では、このようなことを軽視することは、無責任だ(そして他のあまりいい言葉ではない)。
hyde

1
@Cruncher SQLを適切に処理すれば、SQL の適切な動作を妨げることなく、任意の入力を受け入れることができます。SQLパーツは、どこから来たものでも何でも受け入れる準備をして準備しておく必要があります。それ以外はすべてエラーが発生しやすくなります。
glglgl 2014年

8

ユーザーがコンソールを使用してドロップダウンを変更しないように保護する1つの方法は、ドロップダウンに整数値のみを使用することです。次に、POST値に整数が含まれていることを検証し、必要に応じて配列を使用してそれをテキストに変換できます。例えば:

<?php
// No, you don't need to specify the numbers in the array but as we're using them I always find having them visually there helpful.
$sizes = array(0 => 'All', 1 => 'Large', 2 => 'Medium', 3 => 'Small');
$size = filter_input(INPUT_POST, "size", FILTER_VALIDATE_INT);

echo '<select name="size">';
foreach($sizes as $i => $s) {
    echo '<option value="' . $i . '"' . ($i == $size ? ' selected' : '') . '>' . $s . '</option>';
}
echo '</select>';

その後、整数または整数$sizeのみが含まれることを知っているクエリで使用できますFALSE


2
@OliverBS filter_inputサーバー側のチェックでない場合はどうなりますか?整数以外は投稿できません。
Styphon

Styphonに感謝します。これでOPのフォームが置き換えられますか?そして、これは複数のドロップダウンを持つ大きなフォームに適用できますか?「色」などのドロップダウンをもう1つ追加した場合、
Tatters

@SamuelTattersfieldはい、はい。これは、ドロップダウンにいくつでも使用でき、オプションもいくつでも使用できます。ドロップダウンごとに新しい配列を作成し、配列にドロップダウンのすべてのオプションを配置するだけです。
Styphon 2014年

いいね!私はそれが好きです。私はそれを試して、テストで何が得られるかを見てみましょう。
ぼろ

@SamuelTattersfieldすばらしい。filter_input検証のためにもパーツを必ず使用してください。そうでない場合は、セキュリティのためにチョコレートティーポットと同じくらい便利です。
Styphon

7

他の回答はすでにあなたが知る必要があることをカバーしています。しかし、それはもう少し明確にするのに役立ちます:

あなたがする必要がある2つの事があります:

1.フォームデータを検証します。

以下のようジョナサン・ホッブズ回答ショーは非常に明確に、フォーム入力のためのhtml要素の選択は、あなたのための任意の信頼性の高いフィルタリングを行いません。

検証は通常、データを変更しない方法で行われますが、フィールドには「これを修正してください」とマークされたフォームが再度表示されます。

ほとんどのフレームワークとCMSには、このタスクに役立つフォームビルダーがあります。それだけでなく、攻撃のもう1つの形式であるCSRF(または「XSRF」)に対しても役立ちます。

2. SQLステートメントの変数をサニタイズ/エスケープします。

..または、準備されたステートメントに任せてください。

(My)SQLステートメントを、ユーザー指定の変数かどうかに関係なく変数で作成する場合は、これらの変数をエスケープして引用符で囲む必要があります。

一般に、MySQLステートメントに挿入するそのような変数は、文字列か、PHPがMySQLで消化できる文字列に確実に変換できるものでなければなりません。数など。

文字列の場合、文字列をエスケープするためにいくつかの方法の1つを選択する必要があります。つまり、MySQLで副作用のある文字を置き換えます。

  • 昔ながらのMySQL + PHPでは、mysql_real_escape_string()がその役割を果たします。問題は、簡単に忘れることができるため、準備されたステートメントまたはクエリビルダーを絶対に使用する必要があることです。
  • MySQLiでは、準備済みステートメントを使用できます。
  • ほとんどのフレームワークとCMSには、このタスクに役立つクエリビルダーが用意されています。

数値を扱う場合は、エスケープと引用符を省略できます(これが、準備されたステートメントで型を指定できる理由です)。

SQLステートメントの変数エスケープし、データベース自体はエスケープしないことを指摘することが重要です。データベースは元の文字列を格納しますが、ステートメントにはエスケープバージョンが必要です。

これらの1つを省略するとどうなりますか?

フォーム検証を使用せずに SQL入力をサニタイズすると、あらゆる種類の問題が発生する可能性がありますが、SQLインジェクションは表示されません。(*)

まず、アプリケーションを予定外の状態にすることができます。たとえば、すべてのユーザーの平均年齢を計算したいが、1人のユーザーが年齢に「aljkdfaqer」を与えた場合、計算は失敗します。

第二に、考慮する必要のある他のあらゆる種類のインジェクション攻撃がある可能性があります。たとえば、ユーザー入力にJavaScriptまたはその他のものが含まれている可能性があります。

それでもデータベースに問題がある可能性があります。たとえば、フィールド(データベーステーブルの列)が255文字に制限されていて、文字列がそれより長い場合などです。または、フィールドが数字しか受け入れず、代わりに非数値文字列を保存しようとした場合。しかし、これは「注入」ではなく、単に「アプリケーションをクラッシュさせる」だけです。

ただし、検証なしで入力を許可するフリーテキストフィールドがある場合でも、データベースステートメントに移動するときに適切にエスケープすれば、これをそのままデータベースに保存できます。この文字列をどこかで使用したいときに問題が発生します。

(*)またはこれは本当にエキゾチックなものになります。

SQLステートメントの変数をエスケープしなくても、フォーム入力を検証した場合でも、問題が発生していることがわかります。

まず、データベースにデータを保存して再度ロードすると、同じデータでなくなり、「翻訳が失われる」というリスクがあります。

次に、SQLステートメントが無効になり、アプリケーションがクラッシュする可能性があります。たとえば、変数に引用符または二重引用符が含まれている場合、使用する引用符のタイプに応じて、無効なMySQLステートメントが取得されます。

第三に、SQLインジェクションを引き起こす可能性があります。

フォームからのユーザー入力が既にフィルター/検証されている場合、入力がハードコードされたオプションのリストに減らされるか、数値に制限されていると、意図的な SQlインジェクションの可能性が低くなる可能性があります。ただし、SQLステートメントの変数を適切にエスケープしないと、任意のフリーテキスト入力をSQLインジェクションに使用できます。

そして、フォーム入力がまったくない場合でも、あらゆる種類のソースからの文字列を保持できます。ファイルシステムからの読み取り、インターネットからのスクレイピングなど。これらの文字列が安全であることを誰も保証できません。


数値フィールドへの文字列ではない長すぎるデータも何もクラッシュしません
あなたの常識

うーん、今これを試したところ、実際にはエラーではなく警告が表示されました。これをエラーにするのはPDOだと思います。drupal.orgで、varcharに対して文字列が長すぎるというダースの問題で、私はきっと議論しました。
ドンキホーテ2014年

6

Webブラウザーは、phpからページを受信して​​いることを「認識」していません。見ているのはhtmlだけです。そして、http層はそれよりも少ないことしか知りません。httpレイヤーを通過できるほぼすべての種類の入力を処理できる必要があります(幸い、ほとんどの入力phpはすでにエラーを出します)。悪意のあるリクエストがデータベースをめちゃくちゃにしないようにしようとしている場合は、相手側の人が彼の行動を知っていて、通常の状況でブラウザに表示されるものに限定されないことを前提とする必要があります(ブラウザの開発者ツールでいじることができることは言うまでもありません)。つまり、ドロップダウンからの入力には対応する必要がありますが、ほとんどの入力でエラーが発生する可能性があります。


6

ユーザーが特定のドロップダウンリストからの値のみを使用するように制限したという事実は関係ありません。テクニカルユーザーは、サーバーからネットワークに送信される前に、サーバーに送信されたhttp要求をキャプチャし、ローカルプロキシサーバーなどのツールを使用してそれを変更し、そのまま続行できます。変更された要求を使用して、ドロップダウンリストで指定したものではないパラメーター値を送信できます。開発者は、クライアント上のあらゆるものが変更される可能性があるため、クライアント制限はしばしば無意味であるという考え方を持っている必要があります。サーバーの検証は、クライアントデータが入力されるすべてのポイントで必要です。攻撃者は、この唯一の側面で開発者の素朴さに依存しています。


5

SQLインジェクションを防ぐために、パラメーター化されたクエリを使用するのが最善です。その場合、クエリの外観は次のようになります。

SELECT * FROM table WHERE size = ?

整合性が検証されていない(入力はサーバーで検証されない)テキストを使用して上記のようなクエリを指定すると、SQLインジェクションコードが含まれているため、正しく処理されます。つまり、リクエストにより、データベースレイヤーで次のようなことが発生します。

SELECT * FROM table WHERE size = 'DROP table;'

これにより、返される結果が0になるので、ホワイトリスト、検証チェック、またはその他の手法を使用しなくても、クエリが実際にデータベースに悪影響を与えることはありません。責任のあるプログラマーはレイヤーでセキュリティを行い、クエリのパラメーター化に加えて検証を行うことが多いことに注意してください。ただし、パフォーマンスの観点からクエリをパラメーター化しない理由はほとんどなく、このプラクティスによって追加されるセキュリティは、パラメーター化されたクエリに慣れるための良い理由です。


4

フォームから送信されたものはすべて、ワイヤーを介してテキストとしてサーバーに送られます。誰かがボットを作成してクライアントを模倣したり、必要に応じて端末からボットを入力したりするのを妨げるものは何もありません。あなたがクライアントをプログラムしたので、あなたが思っているように振る舞うと思い込まないでください。これは、なりすましが本当に簡単です。

クライアントを信頼したときに何が起こり、何が起こるかの


4

ハッカーは、Telnetを使用してリクエストを送信することにより、Javascriptフォームチェックを含め、ブラウザを完全にバイパスできます。もちろん、彼はあなたが使用しなければならないフィールド名を取得するためにあなたのhtmlページのコードを調べますが、それ以降、彼にとっては「すべてがうまくいく」でしょう。そのため、サーバー上で送信されたすべての値を、HTMLページに由来しないものとして確認する必要があります。

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