私は、SQLインジェクションのトピックがすでにすべての人をスナップしたことを知っています。
しかし、このトピックは、彼女が絶えず自分自身に対する不信の火をtalkingり、扇動し、パニックに追いつき、コードに自信を持っていた人でさえも怖がっていることについて非常にエキサイティングです。
注射を防ぐ方法についてはすでに多くの記事がありました-私はそれを繰り返しません-それはすべて、実践のいくつかの平凡なポイントに帰着します:
- 外部ソースからのすべてのデータ(このデータが極秘で安全なサイトから取得されたXMLから取得された場合でも)をチェックする必要があります。
必ずタイプを確認して、期待どおりに持ってきてください。
整数を待っていたが、文字と(または)ドットが到着した場合、どこかでエラーを報告し、クライアントに500/404 /または応答に必要なコードを渡します。
符号なし整数のf-sの例:
function getAsUint( &$var ) { return ( preg_match( "~^\\s*(\\d+)\\s*$~i", $val, $t ) ) ? $t[1] : null; }
入力では、調査対象の変数を受け取り、出力では正しい値を返します。値が正しくないか空の場合はnullを返します。
これはほんの一例ですが、本質は明らかです-ブラックジャックと簡単な行動の妖精であなた自身のスーパー機能を作ることができます。
PHPでは、フィルタリング関数を使用できますが、 いくつかの関数があります 。
フォームの入力パラメータにギャップが生じる可能性を覚えて、考慮する必要があります。 たとえば、この関数は、数字のみを返すスペースを正しく破棄します。
したがって、この関数の「123」と「123」は正しい値ですが、常に「123」を返します
- 作成するSQLクエリでは、不要と思われるもの(数値など)でさえ、すべてをクォータする必要があります。
つまり フィールド、テーブルなどの名前と値の両方に正しい引用符を付ける必要があります。
第一に、「引用符を付けるかどうか」を考えるための不必要な休止からあなたを救います、第二に、それは次のような潜在的に脆弱なリクエストを書く機会を少なくします
"select * from `table` where `id`={$id}"
(多くの人にとって、idに関してはそのように書かれていると思います)。
- すべてのデータ(およびこれは、外部から来たものだけでなく、エンジン機能の内部で形成されたものでもあります)は、シールドを受ける必要があります。
mysql_real_escape_stringを使用できますが、独自のものを使用できます。
私の機能は次のとおりです。
function prepareStr( $str ) { return "'". str_replace( array( '\\', "\0", "\n", "\r", "'", '"', "\x1a" ), array( '\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z' ), $str ) . "'"; }
たとえば次のように使用します。
"select * from `table` where `id`=" . prepareStr( $tag )
パラメータ内の引用符は彼女には必要ありません-彼女は自分で置き換えます-これは、 n2を誤って忘れることができるという事実から保護します:)
ここからmysqlマニュアルから取得した置換データ
さて、mysql_real_escape_stringについて少し説明します。mysql_real_escape_stringには、既に接続IDがパラメーターの1つとして使用されています。
たとえば、すべての用途に使用できるわけではないため、あまり便利ではありません
しかし、何らかの理由で彼はこの機能が必要ですか? 彼らはそれなしでは適切に動作しないと書いた:)
mysqlソースを見ると、このファイルはmysys / charset.cにあり、識別子を使用してCHARSET_INFOを取得し、マルチバイトエンコーディングのみを判断します。
そして、1文字はどれくらいですか。 いずれにせよ、私はリストからそれを理解したように。 おそらく、教祖Cがいますか? 私が間違っている場合は修正してください。
データベースに接続するとき、または最悪の場合はWIN1251でUTFを使用するため、エンコードに煩わされることなくprepareStrを完全に回避できますが、本当にエキゾチックな場合は、mysql_real_escape_stringを使用することをお勧めします。
mysql_real_escape_stringのようなprepareStr関数には1つの脆弱性があります。実際、LIKE型の構造で使用される「%」と「_」をエスケープしないという事実です。
フォーム「\%」および「\ _」のシールドは、LIKEの内部で機能しますが、外部では機能しません-いいえ-2文字を返します。 なんで? -不明だが、本当。
したがって、このあいまいさを取り除くには、2つの方法があります。LIKE置換で異なるバージョンの関数を使用するか、1つの関数を使用しますが、普遍性のために、すべての文字「%」と「_」を次のようなものに置き換える必要があります
"CONCAT( '%_', '\%\_', '%_ ')"
おそらくより良い解決策がありますが、私はそれについて知りません。
- プレースホルダー/プリペアドステートメントを使用(またはそれらをPDO / ActiveRecord / ORMなどに統合)
ところで、これらは完全に異なるものです。
簡単に言えば-プレースホルダーは、データベースへの通常のテキストクエリを生成するアシスタントですが、すべてのパラメーター自体をエスケープします。 彼は「...」の必要性を排除します。 mysql_real_escape_string($ param)。 「...」。
また、準備されたステートメントは、たとえば複数の呼び出しに対して、データベースに要求を準備するように依頼する方法です。
私の観点からすると、通常のサイトと通常のCRUDに対して準備されたステートメントを使用することは、バズーカからスズメを撃つようなものです。 キャッシングなどに多くの落とし穴があります。
あなたがそれらを知っているか、あなたがそれを必要とすることを知っているなら-それで問題はすでに異なっています。
- 最も重要なことは、SQLインジェクションについて学ぶために必要なことです。特効薬はありません。
クエリを作成するときは、脳とロバを使用する必要があります。 脳-彼は彼が書いていることを理解しなければならないので、そして嫌いな人-彼女は何かが間違っていると感じるので;)
そして、ユーモアがなければ-PDOまたは準備されたステートメントを使用している人々がすべてを考慮している場合、私にとっては面白いです、彼らのプロジェクトでのSQLインジェクションは不可能ではありません-これらはあなたが考える必要がある場所の数を減らすがそれら。
そして、もし誰かが脆弱なリクエストをした場合、とにかく、彼はそれを通して彼をデータベースに投げ入れ、彼は脆弱になります。
私が分析したプロジェクトの1つで会った例(このような):
"select * from `{$table}` where `col` LIKE ?"
そして、システムはすべてを正しくエスケープして置換しました。エンジンのプラグイン構成ファイルから取得されたため、エンコーダだけが$テーブル変数に非常に自信を持っていました。
そして、このファイルはアップロードが許可されたディレクトリにありました...
そして、1つの脆弱なプラグインのために、悪い叔父はベース全体をマージしました...
まあ、このようなもの。
たくさんの記事や読み物を読んだ後、頭が混乱している人を助け、どうすれば自分を守ることができ、コードに自信のある人が増えていることを願っています。
コメントと追加を楽しみにしており、最後まで読んでくれてありがとう。
PSエラーについては、見つかった場合-PMでお願いします。
コメントから興味深い
- zapimirがmysql_real_escape_stringを正しく使用するように正しく指摘したように、mysql_set_charsetを使用してロケールを設定する必要があります。 SET NAMESは影響しません(デフォルトで使用されます)