SQLインジェクション

前回の勉強内容

ponsuke-tarou.hatenablog.com

今回の勉強内容 : SQLインジェクションの対策を学ぶ

勉強のきっかけになった問題

SQLインジェクション対策について,Webアプリケーションの実装における対策とWebアプリケーションの実装以外の対策として,ともに適切なものはどれか。
https://www.sc-siken.com/kakomon/28_aki/img/17.gif
情報セキュリティスペシャリスト 平成28年秋期 午前Ⅱ 問17

SQLインジェクションとは、アプリケーションが想定しないSQL文を実行させることにより、データベースシステムを不正に操作する攻撃方法のことです。

  • 別名 : ダイレクトSQLコマンドインジェクション, SQL注入
  • 英語 : SQL injection(注入,投入)

https://blogs.mcafee.jp/wp-content/uploads/2018/06/sqlinjection-1.jpg
https://blogs.mcafee.jp/wp-content/uploads/2018/06/sqlinjection-2.jpg
SQLインジェクション攻撃への対策|脆弱性を悪用する仕組みと具体例

https://www.scutum.jp/information/web_security_primer/images/web_security_primar_ph_05.gif
【2】SQLインジェクションによる顧客情報流出 ― 不正アクセスの横綱!| Webサイトセキュリティ対策入門 by WAF「Scutum」プロジェクト

対策 : SQLを埋め込むところで特殊文字を適切にエスケープ

'    →  ''
\   →  \\
今夜分かるSQLインジェクション対策:Security&Trust ウォッチ(42) - @IT

入力値で仕込んでデータが再利用されたときに効力を発揮するセカンドオーダーSQLインジェクション

たとえアプリケーションが常にシングルクオートをエスケープしていても、 攻撃者はなおも、データベース中のデータがそのアプリケーションで再利用さ れるときにSQLをインジェクトすることができます。

  • 例えば・・・
    1. 攻撃者がアプリケーションに登録して、ユーザ名「admin'--」、 パスワード「password」のユーザ名を作る
    2. アプリケーションはシングルクオートを正しくエスケープして、INSERT文が作られる
      • insert into users values ( 123, 'admin''--', 'password', 0xffff)
    3. 攻撃者がパスワードを変更する
      • update users set password = '" + newpassword + "’ where username = '" + rso ( "admin'--") + "'"
    4. 最初にせっかくエスケープしたのに次には・・・・されず・・・クエリが生成される
      • update users set password = 'password' where username = 'admin'--'
    5. 攻撃者は、admin'-- というユーザを登録することによって、admin のパスワードを自由にセットできる

https://image.itmedia.co.jp/ait/articles/0611/02/r20_42_01.gif
今夜分かるSQLインジェクション対策:Security&Trust ウォッチ(42) - @IT

対策 : データベースでのアクセス権は最小限に留める

不正なSQLがWebアプリから送られても最小限のアクセス権だけを与えることで被害を軽減できます。

シングルクオーテーションを使わないSQLインジェクション

SQLインジェクション対策というと、「'」の扱いばかりが注目されるが、「'」を使わなくても成立する攻撃もあります。

  • 例えば・・・
    1. SELECT name FROM user where uid = '$uid' AND age > $age
    2. 渡される$uidと$ageの特殊文字は、適切にエスケープ!が、こんなものがきたら・・・
      • $uid:ueno
      • $age:31 UNION…
    3. SELECT name FROM user where uid = ' ueno ' AND age > 31 UNION……
    4. UNION以降のSQLも実行されてしまう・・・
    5. 対策 : 問題は、ageの値をシングルクオーテーションで囲う
      • SELECT name FROM user where uid = '$uid' AND age > '$age'

対策 : SQLプレースホルダを利用する

プレースホルダとは、ユーザ入力をもとに生成される部分に特殊文字を使用したSQL文中のひな形を用意し、その変数部分には実行時に値を割り当てる仕組みです。

PHPの場合 : $1と$2がプレースホルダ
PHPでのSQLインジェクション対策 - プレースホルダ編 | Let's Postgres

$res = pg_query_params(
  $dbconn,
  'UPDATE users SET profile = $1 WHERE userid = $2',
  array($_REQUEST['profile'], $_SESSION['me']['userid'])
);

Javaの場合 : ?がプレースホルダ
安全なSQLの呼び出し方 - Qiita

PreparedStatement prep = conn.prepareStatement("SELECT * FROM employee WHERE name=?");
prep.setString(1, "山田");

マルチバイト文字の問題

  • 例えば・・・入力値「\x97' OR A=A」をエスケープ処理すると「予' OR A=A」になる
    1. 「\x97' OR A=A」の「'」を「\'」とエスケープして処理する
      • \x97' > \x97\' (「\x97」だけでは意味をなさない)
    2. 「\'」の部分をエンコードする
      • \x97\' > \x97\x5C\x27 > \x97\x5C(予)\x27( ' ) > 予'
    3. 結果としてエスケープ処理すると「予' OR A=A」となり「'」を「\'」とエスケープしたが、「'」が残ってしまう

シフトJISは2バイト文字ですが、2バイト目に1バイト文字の文字コードを含んだ文字もあります。

  • \x94\x5C → 能
  • \x95\x5C → 表
  • \x96\x5C → 暴

「'」を「''」とエスケープしている場合にも同様の現象が起きる可能性がある。

対策 : クライアント側の文字コードシフトJISを使うのをやめてEUC-JPなどを使う

文字コードを変更できない場合の対策
  • 半端な1バイト文字を受け付けない処理を書く
  • マルチバイトを扱う関数を通して、文字コードを整理
// PHPの場合
// mb_convert_encoding — 文字エンコーディングを変換する
/* SJISエンコーディングからSJISに変換 */
$str = mb_convert_encoding($str, 'SJIS', 'SJIS');

f:id:ponsuke_tarou:20190331221754j:plain
思い出の一枚

次回の勉強内容

ponsuke-tarou.hatenablog.com