勉強日記

チラ裏

SQL Antipatterns ch21 SQL Injection

pragprog.com


SQL Injection

Let users input values, but never let users input code.

Objective: Write Dynamic SQL Queries

  • dynamic SQL
    • アプリケーションコード上で動的にSQLクエリを組み立てる
  • DBとアプリケーションとの対話
  • やりたいことを実現するのは容易い
  • セキュリティ的にやりたくないことを禁止するのは難しい

Antipattern: Execute Unverified Input As Code

  • DELETE文を仕込むやつが有名

Accidents May Happen

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?

  • ログを残す場合は下記両方を

Stored Procedures

  • fixedなSQL文から成るなら安全
  • 動的なSQL文を実行するなら危険

Data Access Frameworks

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とかを渡すのはNG
    • IN('1,2,3,4,5,6')になっちゃう
  • こういうときは、動的にIN(? ? ? ? ? ?)をプリペアしてからパラメータを渡す

Get a Buddy to Review Your Code

  • プライドやエゴにとらわれるな
    • セキュリティ欠陥を作り込むよりマシ
  • ガイドライン
    1. アプリケーションの変数を文字列結合または置換しているSQLを探す
    2. 動的なコンテンツの源はどこか。情報源が外部であるものを探す
    3. 外部情報源は潜在的に危険。下記を検討せよ
      • フィルタ
      • バリデーション
      • 値の読み替え
    4. クエリパラメータや、堅牢なエスケープ関数を適用せよ
    5. ストアドプロシージャ等、動的なSQLがある可能性のある場所をすべて調べること
  • コード検査は必須のものとして工数を確保せよ

英語

  • indictment
    • 告発
  • mundane
    • ありふれた
  • coconspirator
    • 共謀者
    • defendantではない
  • defendant
    • 被告
  • interpolate
    • 挿入する、書き入れる
  • exploit
  • panacea
    • 万能の方策
  • exotic
    • 珍しい