PHP function mysql_escape_string にマルチバイト文字に関連するセキュリティの脆弱性があると聞きました。すべてのテーブルがLatin1エンコーディングを使用している場合、脆弱性はありますか?
問題は、mysql_escape_string()
がデータベースで文字エンコーディングをチェックしないことです。その結果、mysql_escape_string()
はデータベースのエンコーディングを認識せず、マルチバイト文字をシングルバイト文字として扱う可能性があります。これにより、最後の2バイトが_’
_などの予約文字や、SQLエンジンに特別な関連性を持つ他の無数の文字にエスケープされる可能性があります。
このような場合、攻撃者はSQLコマンドを追加して、意図しないデータや機能にアクセスする可能性があります。これが、関数が廃止された理由であり、mysql_real_escape_string()
の使用が推奨されます。
mysql_real_escape_string()
は、データベースに接続してデータベースで使用されているエンコーディングを判別することを除いてほぼ同じように機能し、既知のマルチバイトエスケープの問題を防ぎます。
また、mysql_escape_string()
およびmysql_real_escape_string()
は、%および_文字をエスケープしないことに注意してください(マニュアル参照 http://php.net/manual/en/function。 mysql-escape-string.php および http://php.net/manual/en/function.mysql-real-escape-string.php 注を参照)。これにより、LIKE
などのキーワードと共に使用すると意図しないデータにアクセスできる可能性があるため、特別な注意が必要です。
更新
では、mysql_escape_stringはどのエンコーディングを想定していますか?
ASCIIだと思いますが、確認できません。いずれにせよ、結果に違いはありません。データベースのエンコードにLatin1を使用すると、適切にエンコードされていない場合、セキュリティ上の脆弱性が生じる可能性があります。
これはデータベースの脆弱性だけではないことにも注意してください。アプリケーション/サーバー側スクリプト内では、文字列が関数から関数に渡される方法を認識し、正しいエンコーディングが保持されるようにするために、入力を処理するときに注意する必要があります。理想的には、エンドツーエンドでUTF-8を使用することは良いことですが、常に選択肢とは限りません。
個人的に、あなたは間違った質問をしていると思います。 SQLインジェクションの脆弱性を回避したい場合は(同様に回避する必要があります)、答えはmysql_escape_string
をより慎重に使用しないことです。正しい答えは、準備されたステートメントを使用することです。
基本的な問題は、mysql_escape_string
が壊れやすく、壊れる特定の方法をすべて予測することが難しいことです。脆弱性の原因の1つをmysql_escape_string
で学習しましたが、それが唯一の原因かどうか疑問に思いました。
個人的に、私は別のレッスンを持ち帰ります。私が描く教訓は、セキュリティが仕事である場合、mysql_escape_string
はその仕事に適したツールではないということです。代わりに、準備済みステートメント(パラメーター化されたクエリ)を使用する必要があります。
セキュリティの世界では、SQLインジェクションを回避するための最も堅牢な方法は、準備されたステートメントを使用することであることが広く受け入れられています。データをエスケープ/エンコードしてから、文字列連結を使用してSQLクエリを構築しないでください。このアプローチは脆弱であり、たとえばデータベースがクエリを予想とは異なる方法で解釈した場合、簡単に中断する可能性があります。ですから、私のアドバイスは、賢いことをしようとしないでください。準備されたステートメントを使用して、満足してください。
@BernieWhiteの回答 に従って、mysql_escape_string()
は、実際に使用されている文字列エンコーディングを完全に無視してエスケープを実行しますデータベース接続(これとは別です)テーブルで使用されます)。
したがって、_'
_文字は、発生するたびに_\'
_に置き換えることでエスケープしようとします。これはバイト単位で行われ、出現するすべての_0x27
_を_0x5c27
_で置き換えます(つまり、文字列がASCIIの1バイトのスーパーセットでエンコードされているという前提で効果的に動作しています)。たとえば、文字列_0xbf27
_を_0xbf5c27
_に変換しますが、接続エンコーディングがGBKの場合、これにより無効な文字列が_縗'
_に変換されます(エスケープされていない_'
_に注意してください)キャラクター)。
したがって、 mysql_real_escape_string()
は、エスケープを適切に、つまり接続文字エンコーディングに従って実行するために導入されました。ただし、 mysql_set_charset()
を呼び出して、クライアントライブラリに文字エンコードを通知する必要があります。このステップは見過ごされがちで、元のmysql_escape_string()
と同じように脆弱なままにします使用されていました!
しかし、mysql_real_escape_string()
を使用していても、他のEdgeケースが存在し、脆弱なままになる可能性があります。 で説明したように、「mysql_real_escape_string()を回避するSQLインジェクション」に対する私の回答 StackOverflowでのオーバー:
TL; DR
mysql_real_escape_string()
は、次の場合に何の保護も提供しません(さらにデータを変更する可能性があります)。
MySQLの _
NO_BACKSLASH_ESCAPES
_ SQLモードが有効になっている(explicitly別のSQLモードを選択しない限り、これはmightになります);そしてsQL文字列リテラルは、二重引用符_
"
_文字を使用して引用されます。
埋め込まれた文字列リテラルが安全に処理されることを保証する上でのこれらの実際的な困難のため、一般に、それを試みさえしないことが推奨されます!代わりに、リテラル値をSQLから完全に分離したパケットでデータベース接続に送信できます。その結果、サーバーはこれらの値を解析してattemptも実行しません。これは「クエリのパラメーター化」と呼ばれ、 @DWの回答 が示唆するものであり、 でうまく説明されていますPHPでSQLインジェクションを防ぐにはどうすればよいですか?