SQL Injection
Let users input values, but never let users input code.
- 連続ハッカー犯の話
-
Albert Gonzalez
- SQL Injectionでクレジット/デビットカード番号を大量に盗んだ
Objective: Write Dynamic SQL Queries
-
dynamic SQL
- アプリケーションコード上で動的にSQLクエリを組み立てる
- DBとアプリケーションとの対話
- やりたいことを実現するのは容易い
- セキュリティ的にやりたくないことを禁止するのは難しい
Antipattern: Execute Unverified Input As Code
- DELETE文を仕込むやつが有名
Accidents May Happen
- アポストロフィで文字列リテラルを閉じちゃう
- 【補】O’Reillyとか
- 単にシンタックスエラーが出るだけなのでマシな部類
The Top Web Security Threat
http://bugs.example.com/setpass?password=xyzzy&userid=123 OR TRUE
- こうなる
UPDATE accounts
SET password_has = SHA2('xyzzy')
WHERE account_id = 123 OR TRUE
- 全ユーザのパスワードが変更されてしまう
- SQLをパースする前に動的な部分を挿入してしまうのが本質的な危険性
The Quest for a Cure
- どう守ればいいの
- ここに挙げるものはすべてのケースで適用可能なものではない
Escaping Values
- クォーテーションをエスケープする
Query Parameters
- プリペアドステートメント
-
非常に強力な防衛策だが、万能の方策ではない
-
値のリストは入れられない
IN ( ? )
-
テーブルの識別子は入れられない
FROM ?
-
カラムの識別子は入れられない
-
ORDER BY ?
ORDER BY 'date_reported'
に展開されて意図通り動かない
-
-
SQLのキーワードは入れられない
-
ORDER BY date_reported ?
にDESCを入れるとか'DESC'
になっちゃう
-
- 【補】
LIKE '%?%'
とかも駄目
-
コラム: What Was My Complete Query?
-
ログを残す場合は下記両方を
-
プリペア時のSQL文
?
等のプレースホルダー入り
- 実行時のパラメータ
-
Stored Procedures
- fixedなSQL文から成るなら安全
- 動的なSQL文を実行するなら危険
Data Access Frameworks
- フレームワーク安全神話は、SQLを文字列として書かせる限り嘘
-
Practice Good Hygiene
- フレームワークを使ってSQLインジェクションを防ぐのは、歯ブラシを使って虫歯を防ぐのと似ている
- 絶えず使うこと
-
安全なSQLコードを強制するフレームワークは存在しない
- 安全な機能を提供するが、簡単に迂回できる
- 【補】Laravelのクエリビルダの
whereRaw()
とか
How to Recognize the Antipattern
- 文字列結合でSQL文を構築し、変数を挿入する限り、潜在的に危険
- ことさらにレビューを行わない限り、いかなるアプリケーションにも脆弱性が入っていると考えるべき
Legitimate Uses of The Antipattern
- ない
- 一番危険な部分がアプリケーション全体での危険性となる
- くれぐれも戦犯にならないよう
Solutinon: Trust No One
- 単一のテクニックはない
- 適宜組み合わせて
Filter Input
- アプリケーション側で有害な文字をフィルタリングする
- 数値を期待しているところにそれ以外の文字が入ってきた場合など
コラム: Rule #31: Check the Back Seat
- 怪物映画では、化物は車の運転席の後ろに隠れているもの
- 慣れ親しんだ場所にも危険は潜んでいる
-
間接的にやられる例
- ユーザ名をSELECTで取得
- それを別のdynamic SQLに挿入
Parameterize Dynamic Values
- プリペアドステートメントからシンタックスが変わらない
UPDATE accounts
SET password_has = SHA2('xyzzy')
WHERE account_id = ?
UPDATE accounts
SET password_has = SHA2('xyzzy')
WHERE account_id = '123 OR TRUE'
-
比較的無害
- 1行も得られないことはあっても、間違った行が得られることはない
Quoting Dynamic Values
- クエリパラメータはふつう最善の選択
-
インデックス利用の最適化まわりがうまくいかないことがある
- カーディナリティが低く、値の分布が偏っている場合など
-
例: is_activeカラム
- 99%がtrue
- 1%がfalse
- falseの行を取得する場合だけインデックスが効果的
- だがプリペアドステートメントの時点ではインデックスを使用すべきかわからない
- 上記の例では値を直接SQL文に入れたほうが良い
-
その場合、よく枯れている機能を利用すること
PDO::quote()
メソッド等- セキュリティを徹底的に勉強していない限り、くれぐれも自分で作らないこと
Isolate User Input From Code
- ユーザ入力を直接SQL文に挿入せず、連想配列等で読み替える
コラム: Parameterizeng an IN() Predicate
- 値のリストをクエリパラメータに渡したい場合
-
IN( ? )
に1,2,3,4,5,6
とかを渡すのはNGIN('1,2,3,4,5,6')
になっちゃう
- こういうときは、動的に
IN(? ? ? ? ? ?)
をプリペアしてからパラメータを渡す
Get a Buddy to Review Your Code
-
プライドやエゴにとらわれるな
- セキュリティ欠陥を作り込むよりマシ
-
ガイドライン
- アプリケーションの変数を文字列結合または置換しているSQLを探す
-
動的なコンテンツの源はどこか。情報源が外部であるものを探す
- ユーザ入力
- ファイル
- 環境変数等
- Webサービス
- サードパーティ製コード
- DBから取得した文字列
-
外部情報源は潜在的に危険。下記を検討せよ
- フィルタ
- バリデーション
- 値の読み替え
- クエリパラメータや、堅牢なエスケープ関数を適用せよ
- ストアドプロシージャ等、動的なSQLがある可能性のある場所をすべて調べること
- コード検査は必須のものとして工数を確保せよ
英語
-
indictment
- 告発
-
mundane
- ありふれた
-
coconspirator
- 共謀者
- defendantではない
-
defendant
- 被告
-
interpolate
- 挿入する、書き入れる
-
exploit
- 脆弱性を突くスクリプトまたはプログラム
-
panacea
- 万能の方策
-
exotic
- 珍しい