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

プリペアドステートメントへの切り替え

    私も同じ状況にあります。連結ステートメントも使用していたので、アプリケーションをプリペアドステートメントに切り替えました。

    悪いニュース クライアントデータをSQLステートメントに連結して作成されたすべてのSQLステートメントを変更しますか。これは、50のソースファイルにあるほとんどすべてのSQLステートメントになります。

    朗報 プリペアドステートメントへの切り替えによる利益は貴重です。例:

    1-「SQLインジェクション攻撃」と呼ばれるものについて心配することはありません

    php 手動 言う

    私にとって、その理由-安心-は私のソースコードを変更するコストを支払うのに十分です。 、これで、クライアントはフォーム名フィールドrobert; DROP table students; -- ;) 何も起こらないので安心です

    2-クライアントパラメータをエスケープする必要はもうありません。次のように、SQLステートメントで直接使用できます:

    $query = "SELECT FROM user WHERE id = ?";
    $vars[] = $_POST['id'];
    

    代わりに

    $id = $mysqli->real_escape_string($_POST['id']);
    $query = "SELECT FROM user WHERE id = $id";
    

    これは、プリペアドステートメントを使用する前に行わなければならなかったことであり、通常の人間として1つのパラメーターをエスケープすることを忘れる危険がありました。攻撃者がシステムを破壊するのに必要なのは、エスケープされていない1つのパラメータだけです。

    コードの変更

    通常、ソースファイルを変更することは常にリスクが高く、苦痛を伴います。特に、ソフトウェアの設計が悪く、明確なテスト計画がない場合はなおさらです。でも、できるだけ簡単にするために私がしたことをお話しします。

    すべてのデータベースインタラクションコードが使用する関数を作成したので、後で必要なものを1か所で変更できます。その関数は次のように作成できます

    class SystemModel
    {
        /**
         * @param string $query
         * @param string $types
         * @param array $vars
         * @param \mysqli $conn
         * @return boolean|$stmt
         */
        public function preparedQuery($query,$types, array $vars, $conn)
        {
            if (count($vars) > 0) {
                $hasVars = true;
            }
            array_unshift($vars, $types);
            $stmt = $conn->prepare($query);
            if (! $stmt) {
                return false;
            }
            if (isset($hasVars)) {
                if (! call_user_func_array(array( $stmt, 'bind_param'), $this->refValues($vars))) {
                    return false;
                }
            }
            $stmt->execute();
            return $stmt;
        }
    
        /* used only inside preparedQuery */
        /* code taken from: https://stackoverflow.com/a/13572647/5407848 */
        protected function refValues($arr)
        {
            if (strnatcmp(phpversion(), '5.3') >= 0) {
                $refs = array();
                foreach ($arr as $key => $value)
                    $refs[$key] = &$arr[$key];
                    return $refs;
            }
            return $arr;
        }
    }
    

    これで、このインターフェイスをソースファイルのどこでも使用できます。たとえば、質問で指定した現在のSQLステートメントを変更しましょう。これを変えましょう

    $mysqli = new mysqli('localhost', "root", "", "testdb");
    $addresult = "
                    SELECT a.firstnames, a.surname, a.schoolrole, a.datejoined 
                    FROM teachers a LEFT JOIN schools b ON a.schoolid = b.id 
                    WHERE b.id = '".$inputvalues['schoolid']."'";
    
    if( $result = $mysqli->query($addresult) ) {
        while($row = $result->fetch_all())
        {
            $returnResult = $row;
        }
    }
    

    これに

    $mysqli = new mysqli('localhost', "root", "", "testdb");
    $sysModel = new SystemModel();
    $addresult = "
                    SELECT a.firstnames, a.surname, a.schoolrole, a.datejoined
                    FROM teachers a LEFT JOIN schools b ON a.schoolid = b.id
                    WHERE b.id = ?";
    $types = "i"; // for more information on paramters types, please check :
    //https://php.net/manual/en/mysqli-stmt.bind-param.php
    $vars = [];
    $vars[] = $inputvalues['schoolid'];
    
    $stmt = $sysModel->preparedQuery($addresult, $types, $vars, $mysqli);
    if (!$stmt || $stmt->errno) {
       die('error'); // TODO: change later for a better illustrative output
    }
    $result = $stmt->get_result();
    $returnResult = [];
    while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
        $returnResult[] = $row;
    }
    

    はい、SQLインジェクション攻撃は、SQLステートメントに不正な文字列を連結することによって適用されます。 INSERTであるかどうか 、SELECTDELETEUPDATE 。たとえば

    $query = "SELECT * FROM user WHERE name = '{$_GET['name']}' AND password = '{$_GET['pass']}'"
    

    そのようなものは

    によって悪用される可能性があります
    // exmaple.com?name=me&pass=1' OR 1=1; -- 
    

    これにより、SQLステートメントが生成されます

    $query = "SELECT * FROM user WHERE name = 'me' AND password = '1' OR 1=1; -- '"
    //executing the SQL statement and getting the result
    if($result->num_rows){
        //user is authentic
    }else{
        //wrong password
    }
    // that SQL will always get results from the table which will be considered a correct password
    

    ソフトウェアをプリペアドステートメントに切り替えて頑張ってください。何が起こっても、SQLインジェクション攻撃から安全であり、ソースファイルを変更する価値があることを知って安心することを忘れないでください




    1. データベースごとに、mysqldumpファイルを複数のデータベースに分割します

    2. カンマ区切り値を検索する方法

    3. csvからmysqlにインポートするときに特殊文字を保持する

    4. PostgreSQLで文字列値(evalなど)内でクエリを実行する方法はありますか?