Laravelにおける複文のSQLインジェクション対策
背景
LaravelアプリケーションでSQLインジェクションのテストを行っていて、わざとSQLインジェクションが起こるようなコードを書いたのに実行されなくて調べた。
(まずはSQLインジェクションが発生することを確認してから、その対応を入れて発生しなくなったことを確認しようと思っていた)
SQLインジェクションのクエリ
典型的な以下の文字列を渡した。
'; delete from admins; --
だが実行してみると構文エラーが発生した。
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'delete from admins; --%'' at line 5 (SQL:
理由
LaravelはデフォルトでPDO::ATTR_EMULATE_PREPARESをfalseにしているため。
これがfalseだとMySQLで複文(;
区切りで複数のクエリを発行すること)を許可しない。
参考:
試しにPDO::ATTR_EMULATE_PREPARES
をtrueにしたら意図通りSQLインジェクションが発生した。
ということでLaravelはデフォルトで複文対策が出来ている模様。
おまけ
パターンごとにパケットキャプチャした結果。
PDO::ATTR_EMULATE_PREPARES=true
+ \DB::select($query, $params)
(プレースホルダーを通して実行した場合)
Prepared Statement
は呼ばれない。シングルクオートはエスケープされる。
where name like '%\'; delete from admins; --%'
PDO::ATTR_EMULATE_PREPARES=true
+ 文字列に直接パラメータを埋め込んで実行
華麗にインジェクションがキマる。
where name like '%'; delete from admins; --%'
PDO::ATTR_EMULATE_PREPARES=false
+ 文字列に直接パラメータを埋め込んで実行
本文に書いた通り複文を許さないので構文エラーになる。
PDO::ATTR_EMULATE_PREPARES=false
+ \DB::select($query, $params)
(プレースホルダーを通して実行した場合)
基本はこれで書くはず。もしくはEloquent使うなら意識すらしないかも。
構文エラーにならずPrepared Statement
とかも呼ばれて実行される。