mysql_real_escape_string()を回避するSQLインジェクション


644

mysql_real_escape_string()関数を使用してもSQLインジェクションの可能性はありますか?

このサンプル状況を考えてみましょう。SQLはPHPで次のように構築されます。

$login = mysql_real_escape_string(GetFromPost('login'));
$password = mysql_real_escape_string(GetFromPost('password'));

$sql = "SELECT * FROM table WHERE login='$login' AND password='$password'";

そのようなコードは依然として危険であり、mysql_real_escape_string()関数を使用してもハッキングする可能性があると多くの人々が私に言うのを聞いたことがあります。しかし、私は可能性のある悪用を考えることができませんか?

このような古典的な注射:

aaa' OR 1=1 --

動作しない。

上記のPHPコードを通過する可能性のあるインジェクションを知っていますか?


34
@ThiefMaster-無効なユーザー/無効なパスワードなどの詳細なエラーを表示したくない...それはブルートフォースの販売者に有効なユーザーIDを持っていることを知らせ、それは推測する必要があるパスワードにすぎない
Mark Ba​​ker、

18
しかし、ユーザビリティの観点からすれば、それは恐ろしいことです。時々、メインのニックネーム/ユーザー名/メールアドレスを使用できず、しばらくするとこれを忘れるか、サイトが非アクティブのためにアカウントを削除したことがあります。次に、パスワードを試し続け、無効なのはユーザー名だけであるにもかかわらずIPをブロックしてしまうと、非常に煩わしくなります。
ThiefMaster 2011

50
mysql_*新しいコードでは関数を使用しないでください。それらはもはや維持されておらず、廃止プロセスが開始されています。赤い箱見える?について学ぶプリペアドステートメントの代わりに、および使用 PDOまたは MySQLiをする -この記事は、あなたがどれを決めるのに役立ちます。PDOを選択した場合こちらが良いチュートリアルです。
tereško

13
@ machineaddict、5.5(最近リリースされた)以降、mysql_*関数はすでにE_DEPRECATED警告を発しています。ext/mysql拡張子はもっとして10年以上にわたって維持されていません。あなたは本当にそんなに妄想ですか?
テレシュコ2013

13
@machineaddict彼らはPHP 7.0でその拡張機能を削除したばかりで、まだ2050にはなっていません。
GGG 2017年

回答:


379

次のクエリについて考えてみます。

$iId = mysql_real_escape_string("1 OR 1=1");    
$sSql = "SELECT * FROM table WHERE id = $iId";

mysql_real_escape_string()これからあなたを保護しません。 クエリ内の変数を一重引用符(' ')で囲むという事実は、これを防ぐためのものです。以下もオプションです。

$iId = (int)"1 OR 1=1";
$sSql = "SELECT * FROM table WHERE id = $iId";

9
しかし、mysql_query()複数のステートメントを実行しないので、これは本当の問題ではないでしょう?
ペッカ

11
@Pekka、通常の例ですがDROP TABLE、実際には攻撃者はより可能性が高いSELECT passwd FROM usersです。後者の場合、通常、2番目のクエリはUNION句を使用して実行されます。
ジャッコ、2012年

58
(int)mysql_real_escape_string- これは意味がありません。全然違いません(int)。そして、それらはすべての入力に対して同じ結果を生成します
zerkms 2012

28
これは何よりも機能の誤用のほうが多いです。結局のところ、それはではmysql_real_escape_stringなくという名前mysql_real_escape_integerです。整数フィールドで使用するという意味ではありません。
NullUserException

11
@ircmaxell、それでも答えは完全に誤解を招くものです。質問は明らかに引用符内の内容について尋ねています。「引用がありません」はこの質問に対する答えではありません
Pacerier、2015

629

簡単に言えば、はい、はい、回避する方法があります。mysql_real_escape_string()

非常にあいまいなエッジケースの場合!!!

長い答えはそれほど簡単ではありません。ここに示されている攻撃に基づいています

攻撃

それでは、攻撃を示すことから始めましょう...

mysql_query('SET NAMES gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

特定の状況では、それは複数の行を返します。ここで何が起こっているのかを分析してみましょう:

  1. 文字セットの選択

    mysql_query('SET NAMES gbk');

    仕事へのこの攻撃のために、我々は、サーバーの両方のエンコードへの接続に期待していることエンコーディングを必要とする'ASCII、すなわちのように0x27 してその最後のバイトASCIIであるいくつかの文字持っている\すなわち0x5c。結局のところ、デフォルトでのMySQL 5.6でサポートされている5つのなどのエンコーディングがありますbig5cp932gb2312gbksjisgbkここで選択します。

    ここで、SET NAMESここでの使用に注意することが非常に重要です。これにより、サーバー上の文字セットが設定されます。C API関数への呼び出しを使用した場合mysql_set_charset()、問題ありません(2006年以降のMySQLリリースでは)。しかし、その理由についての詳細...

  2. ペイロード

    この注入に使用するペイロードは、バイトシーケンスから始まります0xbf27。ではgbk、これは無効なマルチバイト文字です。ではlatin1、文字列¿'です。latin1 gbk0x27は、それ自体がリテラル'文字であることに注意してください。

    呼び出しaddslashes()た場合、文字の前にASCII \ieを挿入するため、このペイロードを選択しました。我々が羽目になるだろうので、その中には、2つの文字列です:続きます。または、言い換えると、エスケープされていない有効な文字が続きます。ただし、は使用していません。次のステップに進みます...0x5c'0xbf5c27gbk0xbf5c0x27'addslashes()

  3. mysql_real_escape_string()

    へのC API呼び出しmysql_real_escape_string()addslashes()、接続文字セットを認識するという点で異なります。したがって、サーバーが予期している文字セットに対して適切にエスケープを実行できます。ただし、この時点までは、クライアントはlatin1接続にまだ使用していると考えています。使用しているサーバーを指定しましたgbkが、クライアントはまだそれを使用していると考えていlatin1ます。

    したがって、mysql_real_escape_string()バックスラッシュを挿入する呼び出しにより'、「エスケープされた」コンテンツにフリーハンギング文字が含まれます。私たちが見にした場合、実際には、$var中にgbk文字セット、我々は参照してくださいね。

    縗 'OR 1 = 1 / *

    これはまさに攻撃に必要なものです。

  4. クエリ

    この部分は単なる形式ですが、ここにレンダリングされたクエリがあります:

    SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1

おめでとうございます。あなたは単にmysql_real_escape_string()... を使用してプログラムを攻撃しました。

悪い人

ひどくなる。MySQLではPDOデフォルトで準備済みステートメントをエミュレートします。つまり、クライアント側では、基本的にmysql_real_escape_string()(Cライブラリ内で)sprintfを実行します。つまり、次の結果は正常に注入されます。

$pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

ここで、エミュレートされた準備済みステートメントを無効にすることでこれを防ぐことができることに注意してください。

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

これは通常、真の準備済みステートメントになります(つまり、データはクエリとは別のパケットで送信されます)。ただし、PDOはMySQLがネイティブで準備できないステートメントのエミュレートに静かにフォールバックすることに注意してください。これはマニュアルにリストされている可能性がありますが、適切なサーバーバージョンを選択するように注意してください)。

ぶさいく

最初に、のmysql_set_charset('gbk')代わりに使用していれば、これらすべてを防ぐことができたと述べましたSET NAMES gbk。そして、2006年以降のMySQLリリースを使用している場合はそうです。

以前のMySQLリリースを使用している場合、バグによりmysql_real_escape_string()、ペイロードに含まれるような無効なマルチバイト文字は、クライアントが接続エンコーディング正しく通知されていたとして、エスケープの目的 1バイトとして扱われるため、この攻撃はまだ成功しています。バグは、MySQLで修正されました4.1.205.0.22および5.1.11

しかし、最悪なのは、PDOC APIがmysql_set_charset()5.3.6まで公開されなかったことです。そのため、以前のバージョンで、考えられるすべてのコマンドに対してこの攻撃を防ぐことはできません。現在はDSNパラメータとして公開されています

救いの恵み

最初に述べたように、この攻撃が機能するには、脆弱な文字セットを使用してデータベース接続をエンコードする必要があります。 utf8mb4脆弱ではなくすべての Unicode文字をサポートできます。そのため、代わりにそれを使用することを選択できますが、MySQL 5.5.3以降でのみ使用可能です。もう1つの方法はですutf8。これも脆弱ではなく、Unicode Basic Multilingual Plane全体をサポートできます。

または、NO_BACKSLASH_ESCAPESSQLモードを有効にすることもできます。これにより、(特に)の操作が変更されmysql_real_escape_string()ます。このモードを有効に0x27すると、0x2727ではなくに置き換えられる0x5c27ため、エスケーププロセスでは、以前は存在しなかった(つまり、まだ存在する)脆弱なエンコーディングで有効な文字を作成できないため、サーバーは文字列を無効として拒否します。 。ただし、このSQLモードの使用により発生する可能性のある別の脆弱性については、@ eggyalの回答を参照してください。0xbf270xbf27

安全な例

次の例は安全です。

mysql_query('SET NAMES utf8');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

サーバーが期待しているのでutf8...

mysql_set_charset('gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

クライアントとサーバーが一致するように文字セットを適切に設定しているためです。

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

エミュレートされた準備済みステートメントをオフにしたからです。

$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=gbk', $user, $password);
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

文字セットを適切に設定したからです。

$mysqli->query('SET NAMES gbk');
$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "\xbf\x27 OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();

MySQLiは常に真の準備済みステートメントを実行するためです。

まとめ

もし、あんたが:

  • MySQLの最新バージョン(5.1以降、5.5、5.6などすべて)を使用し、かつ mysql_set_charset() / $mysqli->set_charset()/ PDOのDSN文字セットパラメータ(PHPで5.3.6以上)

または

  • 接続エンコーディングに脆弱な文字セットを使用しないでください(utf8/ のみを使用します)latin1 / ascii/など)

あなたは100%安全です。

そうでなければ、あなたが使用しているにもかかわらずmysql_real_escape_string()、あなたは脆弱です ...


3
MySQLのprepareステートメントをエミュレートするPDOは、本当に?ドライバーがネイティブでサポートしているので、その理由はわかりません。番号?
ネットコーダー2012

16
します。彼らは、ドキュメントではそうではないと言っています。しかし、ソースコードでは、それは明白に見え、修正が簡単です。私は開発者の無能さまでそれをチョークで書きます。
セオドアR.スミス

5
@ TheodoreR.Smith:修正するのは簡単ではありません。私はデフォルトの変更に取り組んできましたが、切り替えたときに大量のテストが失敗します。見た目よりも大きな変化です。私はそれを5.5で終えることをまだ望んでいます...
ircmaxell

14
@shadyyx:いいえ、この記事で説明した脆弱性はに関するものでしたaddslashes。この脆弱性はその脆弱性に基づいています。自分で試してみてください。MySQL 5.0を入手して、このエクスプロイトを実行し、自分で確認してください。それをPUT / GET / POSTに入れる方法に関しては、それはトリビアルです。入力データは単なるバイトストリームです。char(0xBF)バイトを生成する読みやすい方法です。私はこの脆弱性を複数の会議の前で実演しました。これについて私を信頼してください...しかし、そうでない場合は、自分で試してください。動作します...
ircmaxell

5
@shadyyx:そのようなファンキーさを$ _GETで渡すことについては... ?var=%BF%27+OR+1=1+%2F%2AURLと$var = $_GET['var'];コードで、そしてBobはあなたの叔父です。
cHao

183

TL; DR

mysql_real_escape_string()次の場合は、何も保護しません(さらに、データを変更する可能性があります)。

  • MySQLのNO_BACKSLASH_ESCAPESSQLモードが有効になっている(接続するたびに別のSQLモードを明示的に選択しない限り、有効になる可能性がある)。そして

  • SQL文字列リテラルは、二重引用符"文字を使用して引用されます。

これはバグ#72458として提出され、MySQL v5.7.6で修正されました(以下の「節約の猶予」のセクションを参照してください)。

これは別のものです(おそらく少ないですか?)曖昧なエッジケース!!!

に敬意を表して @ircmaxellの素晴らしい答えに(実際、これは盗作ではなくお世辞であることになっています!)、私は彼の形式を採用します。

攻撃

デモから始めましょう...

mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"'); // could already be set
$var = mysql_real_escape_string('" OR 1=1 -- ');
mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

これにより、testテーブルからすべてのレコードが返されます。解剖:

  1. SQLモードの選択

    mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"');

    文字列リテラルに記載されているように:

    文字列内に引用文字を含めるには、いくつかの方法があります。

    • '」で囲まれた文字列内の「'」は「」と書くことができます''

    • "」で囲まれた文字列内の「"」は「」と書くことができます""

    • 引用文字の前にエスケープ文字(“ \”)を付けます。

    • '」で引用された文字列内の「"」は特別な処理を必要とせず、二重化またはエスケープする必要はありません。同様に、「"」で引用された文字列内の「'」は特別な処理を必要としません。

    サーバーのSQLモードにが含まれている場合、NO_BACKSLASH_ESCAPESこれらのオプションの3番目のオプション(これが採用する通常のアプローチmysql_real_escape_string()です)は使用できません。代わりに、最初の2つのオプションのいずれかを使用する必要があります。4番目の箇条書きの効果は、データの変更を回避するために、リテラルの引用に使用される文字を必ず知っている必要があることに注意してください。

  2. ペイロード

    " OR 1=1 -- 

    ペイロードは、文字どおり文字どおり、この注入を開始し"ます。特にエンコーディングはありません。特殊文字はありません。奇妙なバイトはありません。

  3. mysql_real_escape_string()

    $var = mysql_real_escape_string('" OR 1=1 -- ');

    さいわい、mysql_real_escape_string()SQLモードをチェックし、それに応じて動作を調整します。を参照してくださいlibmysql.c

    ulong STDCALL
    mysql_real_escape_string(MYSQL *mysql, char *to,const char *from,
                 ulong length)
    {
      if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
        return escape_quotes_for_mysql(mysql->charset, to, 0, from, length);
      return escape_string_for_mysql(mysql->charset, to, 0, from, length);
    }

    したがって、SQLモードが使用escape_quotes_for_mysql()されている場合は、異なる基礎となる関数が呼び出さNO_BACKSLASH_ESCAPESれます。上述したように、そのような関数は、他の引用文字が文字通り繰り返されないようにリテラルを繰り返すために、リテラルを引用するために使用される文字を知る必要があります。

    ただし、この関数、文字列が単一引用符'文字を使用して引用されることを任意に想定しています。を参照してくださいcharset.c

    /*
      Escape apostrophes by doubling them up
    
    // [ deletia 839-845 ]
    
      DESCRIPTION
        This escapes the contents of a string by doubling up any apostrophes that
        it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
        effect on the server.
    
    // [ deletia 852-858 ]
    */
    
    size_t escape_quotes_for_mysql(CHARSET_INFO *charset_info,
                                   char *to, size_t to_length,
                                   const char *from, size_t length)
    {
    // [ deletia 865-892 ]
    
        if (*from == '\'')
        {
          if (to + 2 > to_end)
          {
            overflow= TRUE;
            break;
          }
          *to++= '\'';
          *to++= '\'';
        }

    したがって、リテラルの引用に使用されている実際の文字に関係なく、二重引用符の"文字はそのまま残ります(およびすべての単一引用符の文字が2倍になります')。私たちのケースでは、提供された引数とまったく同じままです—エスケープがまったく行わていないかのようです。$varmysql_real_escape_string()

  4. クエリ

    mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

    形式的なもの、レンダリングされたクエリは次のとおりです。

    SELECT * FROM test WHERE name = "" OR 1=1 -- " LIMIT 1

私の学んだ友人が言ったように:おめでとうございます、あなたはプログラムを攻撃することに成功しました mysql_real_escape_string() ...

悪い人

mysql_set_charset()これは文字セットとは関係がないため、助けにはなりません。できないmysqli::real_escape_string()。これは、この同じ関数の単なるラッパーであるためです。

まだ明らかではないが、問題は、リテラルが引用される文字がどの文字であるmysql_real_escape_string() 知ることができないことです。これは、後で決めるのは開発者に任されているためです。つまり、NO_BACKSLASH_ESCAPESモードでは、文字通り方法はありませんこの関数は安全に(少なくとも、ない倍増ので、あなたのデータをいじる必要はありません文字を倍増させずに)、任意の引用で使用するために、すべての入力を逃れることができるということで。

ぶさいく

ひどくなる。 NO_BACKSLASH_ESCAPES標準SQLとの互換性のために使用する必要があるため、実際にはそれほど一般的ではないかもしれません(たとえば、SQL-92仕様のセクション5.3を参照してください。つまり、<quote symbol> ::= <quote><quote>文法の生成とバックスラッシュに特別な意味が与えられていない)。さらに、ircmaxellの投稿で説明されている(修正されてからの長い)バグの回避策として、その使用が明示的に推奨されました。知っている人もいますが、一部のDBAは、のような誤ったエスケープ方法の使用を阻止する手段として、デフォルトでオンになるように設定する場合もあります。addslashes()

また、新しい接続のSQLモードは、サーバーの構成(SUPERユーザーはいつでも変更できます)に従ってサーバーによって設定されます。したがって、サーバーの動作を確認するには、接続後に常に希望するモードを明示的に指定する必要があります。

救いの恵み

常にSQLモードを含まないように明示的に設定するNO_BACKSLASH_ESCAPESか、MySQL文字列リテラルを一重引用符文字で引用する限り、このバグは醜い頭を後回しにすることはできません:それぞれescape_quotes_for_mysql()使用されないか、繰り返しが必要な引用文字に関する仮定は正しいこと。

このため、単一引用符で囲まれた文字列リテラルの常用を強制NO_BACKSLASH_ESCAPESするため、ANSI_QUOTESモードを有効にするユーザーも有効にすることをお勧めします。これは、二重引用符で囲まれたリテラルが使用された場合のSQLインジェクションを防止しないことに注意してください。これは、その可能性を減らすだけです(通常の悪意のないクエリが失敗するため)。

PDOでは、同等の機能PDO::quote()と準備されたステートメントエミュレーターの両方が呼び出されますmysql_handle_quoter()—これはまさにこれを実行します。これにより、エスケープされたリテラルが単一引用符で囲まれることが保証されるため、PDOが常にこのバグの影響を受けないことが確実になります。

MySQL v5.7.6以降、このバグは修正されました。変更ログを参照してください:

追加または変更された機能

安全な例

ircmaxellで説明されているバグを考慮すると、次の例は完全に安全です(4.1.20、5.0.22、5.1.11以降のMySQLを使用しているか、GBK / Big5接続エンコーディングを使用していないと想定)。 :

mysql_set_charset($charset);
mysql_query("SET SQL_MODE=''");
$var = mysql_real_escape_string('" OR 1=1 /*');
mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

...を含まないSQLモードを明示的に選択したためNO_BACKSLASH_ESCAPESです。

mysql_set_charset($charset);
$var = mysql_real_escape_string("' OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

...文字列リテラルを一重引用符で囲んでいるからです。

$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(["' OR 1=1 /*"]);

... PDOの準備されたステートメントはこの脆弱性の影響を受けないため(そしてPHP≥5.3.6を使用していて、DSNで文字セットが正しく設定されているか、準備されたステートメントのエミュレーションが無効になっている場合は、ircmaxellも同様です) 。

$var  = $pdo->quote("' OR 1=1 /*");
$stmt = $pdo->query("SELECT * FROM test WHERE name = $var LIMIT 1");

... PDOのquote()関数はリテラルをエスケープするだけでなく、それを(単一引用符で')引用するためです。この場合ircmaxellのバグを回避するためにそのノートは、あなたがしなければならない PHP≥5.3.6を使用することにし、正しくDSNの文字セットを設定しています。

$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "' OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();

...なぜなら、MySQLiで準備されたステートメントは安全だからです。

まとめ

したがって、次の場合:

  • ネイティブの準備済みステートメントを使用する

または

  • MySQL v5.7.6以降を使用する

または

  • ircmaxellの要約にあるソリューションの1つを使用することに加えて、少なくとも次の1つを使用します。

    • PDO;
    • 一重引用符で囲まれた文字列リテラル。または
    • を含まない明示的に設定されたSQLモード NO_BACKSLASH_ESCAPES

...その後、完全に安全である必要があります(文字列がエスケープされる範囲外の脆弱性)。


10
だから、TL;あなたは、単一引用符を使用していない場合は、注射を引き起こす可能性がありますNO_BACKSLASH_ESCAPESのMySQLサーバモードがある」のようなDRは次のようになります。
あなたの常識

bugs.mysql.com/bug.php?id=72458にアクセスできません。アクセス拒否ページが表示されます。セキュリティ上の問題で公開されていませんか?また、この回答から、あなたが脆弱性の発見者であることを正しく理解していますか?その場合、おめでとうございます。
マークアメリー2014

1
人々はそもそも"文字列を使うべきではありません。SQLによると、これは識別子のことです。しかし、ええと... MySQLが「ねじ規格、私がやりたいことは何でもします」と言っているちょうど別の例。(幸いにも、ANSI_QUOTES引用の破損を修正するためにモードに含めることができます。ただし、標準のオープンな無視は、より深刻な対策を必要とする可能性があるより大きな問題です。)
cHao

2
@DanAllen:PDOの関数を介してこの特定のバグを回避できるという点で、私の答えは少し広範でしたが、準備されたステートメントは、注入を一般的に回避するためのはるかに安全で適切な方法です。もちろん、エスケープされていない変数をSQLに直接連結している場合は、その後使用するメソッドに関係なく、注入に対して確実に脆弱になります。quote()
eggyal 2017

1
@eggyall:私たちのシステムは、上記の2番目の安全な例に依存しています。mysql_real_escape_stringが省略されているエラーがあります。緊急モードでそれらを修正することは、修正の前に核にされないことを望んで、賢明な道のようです。私の理論的根拠は、準備されたステートメントに変換することは、後に続く必要があるはるかに長いプロセスになるでしょう。エラーが脆弱性を引き起こさないという事実は、準備されたステートメントがより安全である理由ですか?言い換えれば、正しく実装されている上記の2番目の例は、準備されたステートメントと同じくらい安全ですか?
DanAllen 2017

18

まあ、実際には、%ワイルドカード以外に、通過できるものはありません。LIKEステートメントを使用している%場合、フィルターをかけないと攻撃者がログインと同じように配置でき、ユーザーのパスワードを総当たりにする必要があるため、危険な場合があります。データはクエリ自体に干渉しないため、準備されたステートメントを使用して100%安全にすることをお勧めします。しかし、そのような単純なクエリの場合、おそらく次のようなことを行う方が効率的です。$login = preg_replace('/[^a-zA-Z0-9_]/', '', $login);


2
+1。ただし、ワイルドカードはLIKE句用であり、単純な等価ではありません。
ドール

7
more efficient準備されたステートメントを使用するよりも、単純な置換をどのように考えますか?(準備されたステートメントは常に機能し、ライブラリは攻撃の場合に迅速に修正でき、人為的エラー(完全な置換文字列の入力ミスなど)を露呈せず、ステートメントを再利用するとパフォーマンスが大幅に向上します。)
MatBailie

7
@Slava:ユーザー名とパスワードを単語の文字のみに効果的に制限しています。セキュリティについて何か知っているほとんどの人は、それが検索スペースをかなり縮小するので、それを悪い考えと考えるでしょう。もちろん、データベースにクリアテキストのパスワードを保存することは悪い考えだと彼らは考えていますが、問題を複雑にする必要はありません。:)
cHao 2013年

2
@cHao、私の提案はログインのみに関するものです。明らかに、パスワードをフィルタリングする必要はありません。申し訳ありませんが、私の回答には明記されていません。しかし、実際にはそれは良い考えかもしれません。覚えにくいタイプの「a4üua3!@v \ "ä90; 8f」の代わりに「石の無知なツリースペース」を使用すると、ブルートフォースがはるかに難しくなります。たとえば、3000語の辞書を使用しても、あなたは正確に4ワードを使用しました-それでもおよそ3.3 * 10 ^ 12の組み合わせでしょう:)
スラバ

2
@スラバ:私は以前にその考えを見たことがあります。xkcd.com/936を参照してください。問題は、数学がそれを完全に裏付けていないことです。17文字のパスワードの例では、96 ^ 17の可能性があります。これは、ウムラウトを忘れて、印刷可能なASCIIに限定した場合です。約4.5x10 ^ 33です。私たちは文字通り、10兆兆倍以上の総当たりの仕事を話している。8文字のASCIIパスワードでも7.2x10 ^ 15の可能性があります-3000倍以上です。
cHao 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.