完全なSQLインジェクション対策

(Last Updated On: 2018/02/20)

不完全なSQLインジェクション対策だけで、SQLインジェクション対策は万全、と誤解しているケースが少なくないです。

  • プリペアードクエリ/プレイスホルダを使ったSQLインジェクション対策でOK

は誤りです。「とにかくプレイスホルダを使おう」では脆弱性は無くなりません。

参考:似たような間違いに「出力対策をするのがセキュリティ対策」だとする考え方があります。こういう考え方になる原因はセキュリティ設計や原則を理解していないことにあると思われます。

出力対策”のみ”のセキュリティはアンチプラクティス

前提条件

完全なSQLインジェクションでは入力対策も欠かせません。入力処理では、入力パラメータを厳格にバリデーションします。これは重要なセキュリティ対策ですが、重要でないと誤解しているケースが多々あります。入力処理も重要ですがここでは詳細は省略します。

参考:

OWASPセキュアコーディング – クイックリファレンスガイドにはセキュリティ対策の第一番目の要素として、セキュアに入力をバリデーションする方法が具体的に記載されています。ISO 27000/ISMSはより具体的なデータバリデーション仕様を紹介/要求しています。

完全なSQLインジェクション対策では入力処理時に以下のバリデーションがされていることが必須です。

  • 文字エンコーディングのバリデーション
  • 数値パラメータのバリデーション(範囲を含む)
  • その他、当たり前のバリデーションすべて(ヌル文字や制御文字など、不正な文字でおかしな動作をするDBがある)

セキュアなプログラムの基本構造は以下の図のようになります。

セキュアなプログラムの基本構造

 

完全なSQLインジェクション対策 – 出力対策ではコンテクストを考える

前置きが長くなりましたが、本題のSQLクエリをサポートするデータベースシステムに対する完全な対策を紹介します。

入力処理の時点ではデータがどのような出力に使われるか判りません。例えば、ユーザー名はデータベースにも、メール、HTML、JavaScript、XPATHクエリ、LDAPクエリ、IMAPクエリ、コマンド実行、ファイルのパス、その他、色々なモノに利用される可能性があります。

出力のコンテクストが重要です。出力コンテクストによって、安全な出力が方法が異るからです。データベース、ブラウザなど、出力先だけでなく、出力のコンテクストが重要です。

SQLインジェクション攻撃の完全防止にも出力コンテクストを考慮した対策が必須です。データベースではSQL言語以外に、正規表現/XML/JSONなど別の言語や表現がサポートされています。以下のどれが欠けても、不正なデータ操作が行われる可能性があります

  • パラメーターの安全化(エスケープ、バリデーション、プリペアードクエリなどのAPI)
  • 識別子の安全化(エスケープ、バリデーション)
  • LIKEクエリの安全化(エスケープ、バリデーション)
  • 正規表現の安全化 (エスケープ、バリデーション)
  • XMLとXMLクエリの安全化 (エスケープ、バリデーション、APIの利用)
  • JSONデータの安全化 (エスケープ、バリデーション、APIの利用)
  • その他、全てのコンテクストの安全化

出力対策では出力先のコンテクストが非常に重要ですが、これを疎かにしているケースは少なくありません。これを理解していないとSQLインジェクション対策として「パラメーターへのインジェクションを防止するプリペアードクエリだけ使えば大丈夫」と誤解します。1

完全なSQLインジェクション対策には”エスケープ”と”バリデーション”が欠かせないです。

  • エスケープや入力バリデーションはSQLインジェクションに対するセキュリティ対策ではない

とする主張や考え方は明らかな誤りであった、と言えます。

プリペアードクエリ/プレイスホルダは一部しか対応できない不完全な対策

SQLクエリにはSQLクエリパラメーター以外に、識別子、LIKEクエリ、正規表現、XML、JSON、場合によってはOSコマンド実行まで、様々な出力コンテクストが存在します。全ての出力コンテクストが安全に処理できなければなりません

全てのコンテクストに対応しないプリペアードクエリ/プレイスホルダ(普通はパラメーターにしか対応していない)が不完全なセキュリティ対策であることに議論の余地はありません。

例えば、ある程度本格的にデータを取り扱うシステムはソートカラムを指定できます。カラム名は識別子なのでプリペアードクエリ/プレイスホルダで分離できません。この為、カラム名でSQLインジェクションが可能になっているコードは少なからず存在します。

プリペアードクエリ/プレイスホルダでは、SQL言語の一部である識別子やLIKEクエリに対するインジェクションにさえ対応できません。(注:LIKEクエリインジェクションも正規表現インジェクションと同様に不正なデータ取得を許してしまうケースがある)

参考:SQL識別子のエスケープ

プリペアードクエリ/プレイスホルダでは、正規表現やXML、JSONなど、現在では当たり前のデータベース機能を安全に使うことも出来ません。

SQLクエリ以外の高度なストアードプロシージャー言語を持つRDBMSの場合、ストアードプロシージャー言語がサポートする出力コンテクスト全てが安全でないとRDBMS経由であらゆるインジェクション攻撃を行える可能性があります。

プリペアードクエリ/プレイスホルダはDoS攻撃にも対応できない

以下のクエリを考えると、プリペアードクエリ/プレイスホルダはインジェクションによるDoS攻撃にも対応できないことが明白です。

limit句以降に100000000が挿入(インジェクション)2されて困るシステムは数えきれません。

単純な整数パラメータに対するインジェクション攻撃でさえ、プリペアードクエリ/プレイスホルダだけでは完全に対応できません

参考:

正規表現をサポートするデータベースの場合、ReDoS攻撃に対する対策も欠かせません。これもプリペアードクエリ/プレイスホルダでは対策できません。

入力バリデーション

 

出力時の対策はセキュアなアプリケーションの十分条件を満す対策です。入力データのバリデーションは必要条件を満す対策です。「不正な値」は誤作動の原因になります。

  • ユーザーID/グループID(数値、文字列)が意味を持ち、権限などに影響する

数値/文字列が意味を持つ場合、「不正な値のインジェクション」により、開発者が予定していない動作をする可能性があります。これには不正操作だけでなく、サービス妨害(DoS)なども含まれます。例えば、

  • SELECT * FROM mytable LIMIT 999999999
  • 壊れた文字エンコーディングによるDoS(システムの何処かに保存された不正文字エンコーディングが発生させる例外/エラーはDoSの原因になる。例えば、今のブラウザは壊れた文字エンコーディングで”真っ白”なページを表示する)
  • ReDoSによるDoS

必要十分条件を満たしていないとデータベースを利用した攻撃の可能性を廃除できません。入力バリデーションと入力エラーチェックは「完全なSQLインジェクション対策」に欠かせません。

入力値の種類は3種類しかなく、不正な入力値の受け入れはメリットのないリスクの受け入れです。特にアプリケーションにとってはメリットが一切ないリスクの受け入れ3です

※ 攻撃用入力の大半がアプリにとって”不正な入力”です。アプリに一切不要な”不正な入力”を受け入れる=攻撃者の都合のよい攻撃しやすい脆弱なアプリ、です。2017年版のOWASP TOP 10から10番目の脆弱性として登録されています。

入力値の種類は3種類しかない

アプリケーションレベルのソフトウェアに於て、入力値をバリデーションするセキュリティ対策は必ず行うべき必須中の必須セキュリティ対策で、入力バリデーションはソフトウェアセキュリティの第一原則です。

バリデーションにはの3種類があります。

  • ”入力バリデーション”(不正入力データ廃除)
  • ”ロジックバリデーション”(入力ミス処理)
  • ”出力バリデーション”(不正出力データ廃除)

全て大切ですが、最も重要なバリデーションは最初のバリデーション4、つまりアプリケーションに対する入力処理のバリデーションです。残念ながらほとんどのアプリケーションに十分な入力バリデーション処理がありません。ほとんどのアプリケーションは不十分なロジックバリデーションしか持っていません。

まとめ

出力処理際にはコンテクストが重要であることはセキュリティ専門家にとっては当たり前のこと5ですが、その当たり前が正しく啓蒙されていない時期が長くありました。今でも勘違いしている方が少くありません。

何が根本的な対策か?と区別するような考え方もあるようですが、この考え方は意味がないどころか、リスク分析の観点から有害でさえあります。

セキュリティ対策の評価はリスク削減の費用対効果で決まります。対策が根本的かどうか?は「ITセキュリティ対策の目的」である「許容可能な範囲内のリスクに抑えてITシステムを利用する」に対して決定的な意味はありません。”セキュリティ対策”としては全く根本的対策ではないWebアプリケーションファイアーウオール6でさえ、費用対効果/リスク削減効果が優れているなら導入すべきです。

入力、出力、ロジック全てが根本的な対策であるので区別する意味はありません。 入力バリデーションと合わせて、ソフトウェア全体で安全性を高めることが必要です。

SQLシステムに限らず、出力対策は出力セキュリティ対策の三原則で完全に対応できます。

  1. エスケープ
  2. API(エスケープが不必要なAPI)
  3. バリデーション

この3つのうち1つ欠けても完全な対策になりません。この三原則を完全に適用するには、出力先のコンテクスト(多くの場合はそのコンテクストにおけるエスケープ方法、エスケープできない場合はバリデーションでリスクを廃除する方法)を正しく理解する必要があります。

参考:

 

蛇足

なぜ繰り返し解説していることを解説しているのか?を説明する蛇足なので、興味がない方は読む必要はありません。

「根本的なことが重要で他はオマケ、入力バリデーションはセキュリティ対策としてオマケ」の考え方の出所を知らなかったのですが、徳丸さん(スライド p54)のようでした。セキュリティ対策ではない、から進歩しているのですが、これはいただけません。

バリデーションも重要なセキュリティ対策です。「元々実行すべきモノがセキュリティ対策でない」というロジックも聞いたのですが、出力対策も元々実施すべきモノなのでセキュリティ対策ではない、も成り立ってしまいます。何を言いたいのか理解できません。

言った方が徳丸さんの考えから直接影響を受けたのか判りませんが「元々実施すべきで効果はあってもセキュリティ対策とは違う」と考えていることは、これはこのやり取りからも明らかです。

意味が通らないセキュリティ概念でセキュリティ対策をしていては、何時まで経っても「セキュリティとは何が何だか分らない」と混乱してしまう状況は変わりません。意味が通らない概念だと解らなくて当たり前です。

理由: セキュリティ対策の目的と手段 〜 根本的誤りの元 〜

徳丸さんとの議論は終わっている(標準セキュリティの概念とは全く異る、と結論は出ている)ので、議論するつもりはありませんが、誤りの指摘はしないとならないです。

このエントリは最近作ったと思われる不適切な資料(p53)を見かけたので改めて不十分なSQLインジェクション対策の指摘しておきます。

 

出力のセキュリティ対策は「一部の脆弱性の為に、とにかく◯◯をする」では無く、出力セキュリティの三原則(まとめ参照)がとにかく出力のセキュリティ対策で行うべきことです。

  1. エスケープ
  2. API(エスケープが不必要なAPI)
  3. バリデーション

SQLシステムへの出力は以下のようなモノが在ります。

  • パラメーターの安全化(エスケープ、プリペアードクエリなどのAPI、バリデーション)
  • 識別子の安全化(エスケープ、バリデーション)
  • LIKEクエリの安全化(エスケープ、バリデーション)
  • 正規表現の安全化 (エスケープ、バリデーション)
  • XMLとXMLクエリの安全化 (エスケープ、バリデーション、APIの利用)
  • JSONデータの安全化 (エスケープ、バリデーション、APIの利用)
  • その他、全てのコンテクストの安全化

これを見れば、出力セキュリティの三原則(エスケープ、API、バリデーション)で考えなければならないことは明らかです。いい加減、ソースコード検査時に「SQLにはプレイスホルダだけ使っていればOK」とする脆弱なコードが無くなってほしいので、繰り返しになりますが指摘しました。

他の資料にも信頼境界線の考え方、リスク分析の考え方、ソフトウェアセキュリティの概念部分で根本的な誤りも見つけてしまいました。ツイッターで指摘したのですが無駄でした。ソフトウェアの信頼境界線が間違っている(アプリとデータベースが同じ境界内になっている)件を指摘した回答がこれです。

面倒ですが指摘しなければなりません。これも近日中に改めて誤りを指摘します。

 

 


  1. コンテクストに加えて文字エンコーディングも重要です。出力先(この場合はSQLシステム)が期待する文字エンコーディングを使わないと誤作動する場合があります。通常、文字エンコーディングのバリデーションは入力処理で行うのでここでは省略しています。出力対策としては省略はしていますが、出力対策の前提として”文字エンコーディングが正しい”は必須のセキュリティ要素です。 
  2. 通常はこのような異常な整数入力は入力データバリデーションで廃除されるべき。値をユーザーが制御可能な場合は入力エラー処理で廃除されるべき。 
  3. アプリにとっては不正な入力値の受け入れは120%無用なリスク受容です。しかし、関数/メソッドなどでは必要なリスク受け入れである場合が多いです。全ての関数/メソッドで入力データバリデーションを行なえば、確実にコードの安全性は向上します。しかし、確実に遅くなります。これのバランスを取るのが契約による設計です。 
  4. プログラミングの基礎原則にFail Fast原則(失敗するモノは早く失敗させる)があります。アプリケーション内でのバリデーションで最も早いバリデーションは、アプリケーション入力に対するバリデーションで最も重要です。 ただし、全てのバリデーションが大切です。最も重要 ≠ 他は不必要、です。 
  5.  OWASPセキュアコーディング – クイックリファレンスガイドの7番目 
  6. WAF(Webアプリケーションファイアーウォール)で対応するセキュリティ脆弱性の多くは、問題の原因を”解決”するのではなく、”隠蔽”します。「WAFで仮想パッチをする」とよく言われるのはアプリ/システムの問題を”解決”せず”隠蔽”するからです。 

Comments

comments

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です