SQLインジェクションの基本を理解していると思います。また、準備済みステートメントをPHPファイルで使用することは、SQLインジェクションを防止する最良の方法です。SQLインジェクションは、攻撃者がフォームデータフィールドまたはファイル内に有効なSQLコマンドを入力したときに最もよく発生すると言われています。公開サイトの入力フィールド。
ただし、サイトにPHPファイルがあり、認証されたユーザーのみがアクセスできる場合)、準備されたステートメントを使用する必要はありますか?
また、実行に外部ユーザーデータを必要としないSQLクエリについてはどうでしょうか。
何かのようなもの:
SELECT * FROM tableName
クエリに変数を渡していない場合でも、SQLインジェクションに対して脆弱ですか?
すべての認証済みユーザーを完全に信頼していますか?攻撃者によってアカウントが侵害されることはありませんか?攻撃者がアカウントへのアクセスを取得することは悪いことですが、攻撃者がそれを利用してデータベースの残りの部分を盗むことができる場合はさらに悪いことです。
2番目のポイントとして、使用した例は脆弱ではありません。ただし、次のような境界ケースに注意してください。
$query = "SELECT * FROM tableName WHERE secret='".dbquery("SELECT secret FROM otherTable WHERE id=3")."'";
otherTable
3のレコードのsecret
列のid
にペイロードを挿入することが可能である場合、これは Second Order SQLi に対して脆弱です。 、挿入ルーチンが準備済みステートメントを使用した場合でも。
コメント通り:
SELECT * FROM tableName WHERE secret=(SELECT secret FROM otherTable WHERE id=3)
sQL内で実行されるため、問題ありません。この問題は、DBから取得したデータがアプリケーション層で信頼されていると見なされ、後のクエリで使用される場合に発生します。
「私はSQLインジェクションの基本を理解していると思います。」ただし、残りの質問は、おそらくまだ混乱している可能性があることを意味します。いずれにしても、これは重要なトピックであり、多くの人々は問題と解決策について混乱し続けています。
また、準備済みステートメントをPHPファイルで使用することは、SQLインジェクションを防ぐ最良の方法です。
準備済みステートメントの使用は、SQLインジェクション(SQLi)から保護するために必要ですが、十分ではありません。理由を理解するには、SQLインジェクションの脆弱性の原因を理解することが重要です。原因は次のとおりです。
それでおしまい。ユーザーから入力を受け取り、それをSQLステートメントに入れてからデータベースでコンパイルすると、SQLiの脆弱性の影響を受ける可能性があります(そうなる可能性があります)。これを行って準備済みステートメントを使用する場合、何も解決していません。あなたはまだ傷つきやすいです。
それでは、なぜ準備されたステートメントを常に答えとして使用するのですか?入力からSQLを連結できない場合は、SQLを多かれ少なかれハードコーディングする必要があることを意味します。しかし、ログインしたばかりのユーザーのユーザー情報を取り戻したい場合はどうでしょうか。新しいユーザーができるたびに新しいSQLステートメントを書くことはできません。
準備されたステートメントがこの問題を排除するのに役立つのは、パラメーターを使用できることです。つまり、次のようなことをする代わりに:
// Don't do this! Subject to injection
"SELECT credit_card_number FROM user_data where userid = '" + userinput + "'"
できるよ:
"SELECT credit_card_number FROM user_data where userid = ?"
そして、このステートメントをデータベースでコンパイルします。実行するときは、パラメーター値を渡す必要があります。そうしないと、クエリが失敗します。なぜこれで問題が解決するのですか?攻撃者が文字列を取得する方法を見つけたとしましょう:foo' OR 1 = 1 OR 'a' = 'a
をuserinput値に入力します。最初のケースでは、次のSQLが実行されます。
SELECT credit_card_number FROM user_data where userid = 'foo' OR 1 = 1 OR 'a' = 'a';
テーブルのすべての行を返します。 2番目のケースでは、このSQLが実行されます。
SELECT credit_card_number FROM user_data where userid = ?
パラメータfoo' OR 1 = 1 OR 'a' = 'a
。ありそうもないID foo' OR 1 = 1 OR 'a' = 'a
結果はありません。しかし、明確にしましょう。準備されたステートメントで最初の(間違った)アプローチを使用できますが、それでも脆弱です。入力を無害化する準備されたステートメントには、魔法のようなものはありません。それは、パラメーターをサポートしない通常のステートメントで2番目の(正しい)アプローチを使用できないことです。
これらを使用する主な理由はパラメーターをサポートするためであるため、これを理解している人は、「準備済みステートメントを使用する」という省略形を使用することが多いと思います。しかし、それは実際にこの問題の解決策であるパラメーターです。
マシューの素晴らしい答えに加えて、ユーザーを信頼している場合でも、 [〜#〜] csrf [〜#〜] に対して脆弱であり、攻撃者がSQLインジェクションを実行する可能性がありますアカウント。
攻撃者は、認証されたユーザーが誤ってクリックまたは送信するリンクまたはフォームを作成できるため、SQLインジェクションが発生する可能性があります。このため、ユーザー入力を信頼してはならず、すべてのユーザー入力に対してパラメーター化されたクエリをサニタイズして使用する必要があると私は断言します。
これはスタックオーバーフローで非常に人気のある質問であり、それに対する一種の「標準的な応答」を開発させました。
まず、この問題は、開発で「割引」を交渉しようとしているように聞こえます。しかし、なぜあなたはそうしようとしているのですか?なぜこのような疑問が生じるのでしょうか?準備されたステートメントは、把握/実装するのが難しいですか?次に、別の質問をする必要があります。「準備されたステートメントを退屈にするにはどうすればよいですか?」実際、準備されたステートメントは、他のデータベース相互作用方法よりも簡単です。
したがって、そもそもそのような質問をする理由はまったくありません。
脅威の可能性に関係なく、クエリをフォーマットする必要があります。あなたは有名な Bobby Tables ではなく、そもそも謙虚な少女Sarah O'Hara(またはデータベースを作成できる名前を付けられた他の人間またはエンティティ)のためにそれを行っています正しくフォーマットされていない場合、チョーク)。
したがって、準備されたステートメントを使用する必要があるSQLインジェクションではなく、クエリが常に構文的に正しいことを保証するだけです。
準備されたステートメントを使用しないことは、データを手動でフォーマットすることを辞任することを意味し、この道には多くの落とし穴があります。私はそれらを私の SQLインジェクションに関する記事 に要約するよう努めました。以下は抜粋です。
手動の書式設定は不完全な場合があります。ボビーテーブルの場合を考えてみましょう。これは不完全なフォーマットの完璧な例です:クエリに追加した文字列は引用されただけでエスケープされていません!上記から学んだように、引用とエスケープは(エスケープ関数の適切なエンコーディングの設定と共に)常に一緒に行われる必要があります。しかし、通常のPHPアプリケーションでSQL文字列の書式設定を個別に(一部はクエリで、一部は別の場所で)実行する場合)、書式設定の一部が単に見落とされる可能性が非常に高くなります。
手動の書式設定が誤ったリテラルに適用される可能性があります。 完全なフォーマットを使用している限り、大した問題ではありません(開発段階で修正できる即時エラーが発生するため)。ただし、不完全なフォーマットと組み合わせると、本当の災害。 Stack Overflowのすばらしいサイトには何百もの回答があり、識別子をエスケープするは文字列と同じ方法を示唆しています。これは完全に役に立たず、SQLインジェクションを引き起こします。
手動での書式設定は、本質的に非必須の手段です。まず第一に、適切なフォーマットが単純に忘れられる可能性のある注目すべきケースの明らかな欠如があります。しかし、本当に奇妙なケースがあります-多くのPHPユーザーが意図的に多くの場合、フォーマットを適用することを拒否します。 「クリーン」と「アンクリーン」、「ユーザー入力」と「非ユーザー入力」など。「安全な」データはフォーマットを必要としないと考えています。これは非常にナンセンスです-サラ・オハラを思い出してください。書式設定の観点では、重要なのはdestinationです。開発者は、データではなくSQLリテラルのタイプに注意する必要がありますソース。クエリに送信される文字列ですか?それは文字列としてフォーマットする必要があります。ユーザー入力からのものであるか、コード実行中にどこからともなく不思議な形で表示されるかは関係ありません。
手動の書式設定は、実際のクエリの実行からかなり離れている場合があります。
最も過小評価され、見落とされている問題。それでも従わなければ、他のすべてのルールを単独で台無しにする可能性があるため、それらすべての中で最も重要です。
ほぼすべてのPHPユーザーは、実際のクエリ実行から遠く離れた1か所ですべての「サニタイズ」を実行するように誘惑されます。そのような間違ったアプローチは、数え切れないほどの障害の発生源です。
時期尚早なフォーマットは、ソース変数を台無しにして、他のものには使用できなくなります。
最後だが大事なことは。クエリでは、データリテラルだけが可変部分ではないことを覚えておく必要があります。識別子またはキーワードも追加する必要がある場合があります。この場合、準備されたステートメントは役に立ちませんが、そのようなクエリ部分は完全に保護されるべきです。幸いにも、識別子とキーワードのどちらでも、可能なバリエーションのリストは常に制限されているので、私が 参照の質問に対する私の回答)で説明したwhitelistingアプローチを使用できますスタックオーバーフロー なので、繰り返しはしません。
あなたの最後の質問に答えるために-いいえ、可変部分のないクエリは、注入するポイントがないため、脆弱ではありません。
本当に信頼できるユーザーというものはありません。インサイダーの脅威を無視したとしても(これは最近の重大な見落としです)、他のリスクがあります。ユーザーがポストイットにパスワードを書き込んだり、デバイスがマルウェアに感染したりして、ユーザーが知らないうちに脆弱性を悪用する可能性があります。
SQLインジェクションホールをanyユーザーに公開することで、データベースへの無制限の監視されていないアクセスを効果的に許可できます。セキュリティモデルの設定に応じて、すべてを削除できる可能性があります。
基本的には、保護対象を決定し、それに基づいて努力のレベルを決定する必要がありますが、SQLインジェクションホールを開いたままにしておくのはずさんであり、具体的なメリットがないリスクにさらされます。パラメータ化されたクエリを正しく実装している場合、正しく実行するための努力に違いはありません。
最後の考慮事項...セキュリティは二元的なものではありません。多くのレイヤーが必要で、それぞれが異なる攻撃経路から保護します。 1つの層が危険にさらされた場合、残りの層はデータを保護します(または損失を軽減します)。このシナリオでは、クロスサイトリクエストフォージェリ攻撃により、攻撃者がデータベースを所有することが可能になります。 SQLインジェクションにパッチを適用した場合、アプリがユーザーに通常の状態で許可するのは、SQLインジェクションが実行できる最悪の事態です¹.
¹(そして、それを軽減するために、レート制限または監視/警告システムを実装できます。永遠に続けることができます)。