sql >> データベース >  >> RDS >> Mysql

mysql_real_escape_string()とmysql_escape_string()はアプリのセキュリティに十分ですか?

    @Charlesは非常に正しいです!

    複数の種類の既知のリスクにさらされます あなたが言及したように、SQL攻撃を含む

    • SQLインジェクション:はい! Mysql_Escape_Stringは、クエリでPHP変数を使用する場所によっては、SQLインジェクションの影響を受けやすくなります。

    これを考慮してください:

    $sql = "SELECT number FROM PhoneNumbers " .
           "WHERE " . mysql_real_escape_string($field) . " = " . mysql_real_escape_string($value);  
    

    それを安全かつ正確にそのように逃れることができますか?いいえ!なんで?ハッカーはまだこれをうまく行うことができるので:

    私の後に繰り返します:

    mysql_real_escape_string() 変数データをエスケープすることのみを目的としており、ではありません。 テーブル名、列名、特にLIMITフィールドではありません。

    • LIKEエクスプロイト:LIKE "$ data%"ここで、$dataは"%"であり、すべてのレコードを返します...これは非常にうまくいく可能性があります セキュリティの悪用...クレジットカードの最後の4桁によるルックアップを想像してみてください...OOP!これで、ハッカーはシステム内のすべてのクレジットカード番号を受け取る可能性があります。 (ところで:完全なクレジットカードを保存することはほとんど推奨されません!)

    • 文字セットのエクスプロイト:嫌いな人が何を言おうと、InternetExplorerはまだ 、2011年、文字セットエクスプロイトに対して脆弱であり、それは if <meta name="charset" value="UTF-8"/>と同等のHTMLページを正しく設計しました。 !これらの攻撃は、ハッカーにSQLインジェクションと同じくらい多くの制御を与えるため、非常に厄介です。満杯。

    これをすべて示すためのサンプルコードを次に示します。

    // Contains class DBConfig; database information.
    require_once('../.dbcreds');                       
    
    $dblink = mysql_connect(DBConfig::$host, DBConfig::$user, DBConfig::$pass);
    mysql_select_db(DBConfig::$db);
    //print_r($argv);
    
    $sql = sprintf("SELECT url FROM GrabbedURLs WHERE %s LIKE '%s%%' LIMIT %s",
                   mysql_real_escape_string($argv[1]),
                   mysql_real_escape_string($argv[2]),
                   mysql_real_escape_string($argv[3]));
    echo "SQL: $sql\n";
    $qq = mysql_query($sql);
    while (($data = mysql_fetch_array($qq)))
    {
            print_r($data);
    }
    

    さまざまな入力が渡​​されたときのこのコードの結果は次のとおりです。

    $ php sql_exploits.php url http://www.reddit.com id
    SQL generated: SELECT url FROM GrabbedURLs 
                   WHERE url LIKE 'http://www.reddit.com%'
                   ORDER BY id;
    Returns: Just URLs beginning w/ "http://www.reddit.com"
    
    $ php sql_exploits.php url % id
    SQL generated: SELECT url FROM GrabbedURLs 
                   WHERE url LIKE '%%' 
                   ORDER BY id;
    Results: Returns every result Not what you programmed, ergo an exploit --
    

    $ php sql_exploits.php 1 =1' http://www.reddit.com 'id Results:すべての列とすべての結果を返します。

    次に、本当に厄介なLIMITエクスプロイトがあります:

    $ php sql_exploits.php url 
    > 'http://www.reddit.com'
    > "UNION SELECT name FROM CachedDomains"
    Generated SQL: SELECT url FROM GrabbedURLs 
                   WHERE url LIKE 'http://reddit.com%' 
                   LIMIT 1 
                   UNION
                   SELECT name FROM CachedDomains;
    Returns:  An entirely unexpected, potentially (probably) unauthorized query
              from another, completely different table. 
    

    攻撃のSQLを理解しているかどうかは関係ありません。これが示しているのは、mysql_real_escape_string()が簡単であることです。 最も未熟なハッカーでさえ回避されました。それはリアクティブな防御メカニズムだからです。データベース内の非常に限定された既知のエクスプロイトのみを修正します。

    すべてのエスケープは、データベースを保護するのに決して十分ではありません。実際、既知のすべてのエクスプロイトに明示的に反応することができ、将来的には、コードが将来発見される攻撃に対して脆弱になる可能性が高くなります。

    適切な、そして唯一の(本当に)防御はプロアクティブなものです:準備されたステートメントを使用してください。プリペアドステートメントは、有効でプログラムされたSQLのみが実行されるように特別な注意を払って設計されています。これは、正しく実行されると、予期しないSQLが実行される可能性が大幅に減少することを意味します。

    理論的には、完全に実装されたプリペアドステートメントは、データベースサーバー自体とプログラミング言語とインターフェイスするライブラリによって処理されるサーバーサイドの手法であるため、既知および未知のすべての攻撃に対して不浸透性です。したがって、最低限でも、すべての既知のハックから保護されることが常に保証されます。

    そしてそれはより少ないコードです:

    $pdo = new PDO($dsn);
    
    $column = 'url';
    $value = 'http://www.stackoverflow.com/';
    $limit = 1;
    
    $validColumns = array('url', 'last_fetched');
    
    // Make sure to validate whether $column is a valid search parameter.
    // Default to 'id' if it's an invalid column.
    if (!in_array($column, $validColumns) { $column = 'id'; }
    
    
    $statement = $pdo->prepare('SELECT url FROM GrabbedURLs ' .
                               'WHERE ' . $column . '=? ' .
                               'LIMIT ' . intval($limit));
    $statement->execute(array($value));
    while (($data = $statement->fetch())) { }
    

    さて、それほど難しくはありませんでしたか?そしてそれは47パーセント少ないコードです (195文字(PDO)と375文字(mysql_)。これを「勝利に満ちた」と呼んでいます。

    編集:この答えがかき立てられたすべての論争に対処するために、私がすでに言ったことを繰り返しさせてください:

    プリペアドステートメントを使用すると、SQLサーバー自体の保護手段を利用できるため、SQLサーバーの人々が知っていることから保護されます。この追加レベルの保護により、どんなに徹底的にでも、エスケープを使用するよりもはるかに安全です。



    1. last_insert_rowid()がSQLiteでどのように機能するか

    2. MySQLデータベースに写真を保存する方法

    3. (大きい?)数の値に対するMySQLINオペレーターのパフォーマンス

    4. エラーコード:2013。クエリ中にMySQLサーバーへの接続が失われました