ITシステムに限らずセキュリティ対策で最初に行うべき対策は境界防御(契約による設計と信頼境界線、標準と基本概念、開発者は必修SANS TOP 25)です。ソフトウェアにおける境界防御の第一番は入力の確認(入力の確実な制御)です。このブログでは何度も取り上げていますが、最も重要なセキュリティ対策である境界防御の概念と入力の確認が正しく認識されていない場合がよくあります。
開発者であるから「これで解るはず」と思い書いたエントリは幾つか(「合成の誤謬」の罠、エンジニアに見られるセキュリティ対策理解の壁など)ありますが、どうも成功しているとは言えないようです。今回は開発者でなくても理解できるよう努力してみます。
ホワイトリストの基本中の基本は”デフォルトで全て拒否する”であることに注意してください。全て拒否した上で、許可するモノ、を指定しないとホワイトリストになりません。
セキュリティ対策の目的
セキュリティ対策の目的は「攻撃されないようにする事」です。これ以外の目的もありますが「攻撃されないようする事」が一番の目的と言って良いでしょう。
この目的を達成するのは簡単です。
- 攻撃対象にアクセスできなくする
「攻撃対象にアクセスできなくする」には
- 物理的に近寄れなくする
- 保護が必要な物を持たない
を行なえば良いです。
しかし、ITシステムは利用する為に作られます。誰も近寄れない、ネットワークでも利用できない、データも保存できないITシステムでは何の役にも立ちません。そこで「必要な部分だけ使える」ようにします。
必要な部分だけ使えるようにする
必要な部分だけ使えるようにするには、利用を許可された人だけ使える(認証 – Authentication)する、利用を許可された人が権限の範囲内で使える(認可 – Authorization)ようにします。認証と認可の重要性に異論がある方は見た事がありません。恐らく全ての方が認証/認可の重要性を理解していると思われます。セキュリティ対策の基本は「境界防御」です。認証/認可は”境界”で行います。
- 認証は保護された領域の”境界”で行う
認証と認可は基本的にホワイトリスト方式(許可リスト)で行います。一部ではブラックリスト(拒否リスト)が有効な場合もありますが用途は限定的です。セキュリティ対策でホワイトリスト方式を優先して使うことは常識と言えます。しかし、この点については、ホワイトリスト方式とブラックリスト方式では優劣は付けれない、とするセキュリティ専門家も居ます。
ブラックリスト方式を採用せざるをえない場合もありますが、一般論としてホワイトリスト方式とブラックリスト方式を同列に扱うことは間違っています。「必要な部分だけ使えるようにする」にはブラックリスト方式が劣っていることは明らかです。例えば、経理部以外は経理データにアクセスできない、というビジネスルールがある場合に
- 総務部、営業部、開発部はアクセスできない(拒否リスト)
を作ったとします。新たに「研究部」を作った場合、「研究部」も拒否リストに追加しないと、アクセスできてはならない者が経理データにアクセスできてしまいます。
一般にこのようなビジネスルールは大量にあることが多いです。このため拒否リストの管理は難しく、拒否リストを正確に更新できないために不正利用を許す、という事態に簡単になります。このため認証/認可システムをブラックリスト方式だけで作るようなことはありません。普通はホワイトリスト方式で作ります。
- 経理部のみに経理データにアクセスを許す(許可リスト)
のように定義します。
アプリケーションの安全性と「必要な部分だけ使えるようにする」
ソフトウェアのセキュリティを考えた場合、認証と認可だけでは不十分です。ソフトウェアは非常に複雑です。非常に複雑な上に常に変化しています。誰か一人でソフトウェア全体を理解することは不可能です。それが複数人のチームであってもソフトウェア全体を理解することは無理です。
アプリケーションが攻撃される原因には、OSの問題、ライブラリの問題、アプリケーションフレームワークの問題、アプリケーションの問題があります。しかも、OS、ライブラリ、アプリケーションフレームワーク、アプリケーションは継続的に更新されています。仮に作った時点で完璧であったとしても利用しているうちに問題が発生することはよくあります。
発生する問題の多くが「インジェクション攻撃」です。「ソフトウェアの問題」として報告される問題の大半がインジェクション攻撃に対する脆弱性です。Webアプリケーションの場合、8割、9割のセキュリティ問題はインジェクション脆弱性です。インジェクション攻撃は入力に「攻撃用データ」を含ませ、ソフトウェアを誤作動させることにより攻撃を実行します。攻撃用データには本来は「許されるべきでないデータ」が含まれていることが多いです。
アプリケーションの安全性を向上させるには、認証/認可で採用するホワイトリスト方式で安全性を向上できます。アプリケーションで攻撃者に脆弱性の悪用を許さず「必要な部分だけ使えるようにする」には、まず
- アプリケーションへの入力が許可された物か?
を確認します。「アプリケーションへの入力が許可された物か?」、「入力が妥当であるのか?」を確認する作業を「入力バリデーション」と言います。
入力バリデーションの方法
入力バリデーションには、認証/認可と同じくホワイトリスト方式を利用します。例外としてブラックリストを用いることもありますが、例外は例外であり本則となることはありません。
入力バリデーションは”境界”行います。アプリケーションの場合はアプリケーションの”境界”、つまりユーザー入力、データベース入力、ファイル、コマンドなどの入出力を行う”境界”では入力バリデーションを行います。”境界”で確認するので、何かの処理をする前の入力受付時にバリデーションします。
入力バリデーションでは入力値の以下のような項目を確認します。
- 名前(パラメータの名前)
- 数(パラメータの数)
- 値の形式(文字種、電話番号形式、郵便番号形式、URL形式、ファイル形式など)
- 値の範囲(最大、最小)
- 値の長さ(最大、最小)
- 文字列のエンコーディング
などが妥当であるか確認します。これらの値が妥当でない場合は処理を中止して、バリデーションエラーを記録します。
入力バリデーションの効果
既に書いた通り、攻撃用データには本来は「許されるべきでないデータ」が含まれていることが多いです。確実な入力バリデーションを行うとソフトウェアの中に脆弱性があっても、その脆弱性を利用(攻撃)することが困難になります。そもそも入力仕様に合っていないデータを受け入れることは間違いです。
入力バリデーションは
- 攻撃用データで意図しない操作を行うインジェクション攻撃
を緩和したり、防止したりすることができます。更に良いことは
- ソフトウェア開発者が脆弱性の存在を知らなくても対策可能
であることです。ソフトウエア開発者はシステムに潜在する脆弱性を知らなくても、自分が作っているソフトウェアの「入力仕様」は知っています。既知の入力仕様に合わせて入力値をバリデーションするだけで、攻撃のリスクを減らせます。もし開発者が入力仕様を知らずにソフトウェアを作っているなら開発プロジェクトのマネジメントに重大な問題があります。
ソフトウェアのセキュリティ対策の基本は「必要な部分だけ使えるようにする」ことにあります。攻撃者は「使える部分」に「攻撃用データ」を挿入して「不正な操作を使えるように」しようとします。入力バリデーションは「使える部分」への「攻撃用データ挿入防止」の助けになります。
効果に疑問を持つかも知れませんが、例えば「これは数値だから」と気軽に安全な値として使っているプログラムは少くありません。
入力ミスと入力バリデーションエラーの違い
入力ミスと入力バリデーションエラーは同じではありません。別の物として取り扱います。例えば、任意テキスト1000文字を入力できる項目があるとします。1001文字のデータがあった場合、
- 送信元で文字数が確認されている場合は入力バリデーションエラー
- 送信元で文字数が確認されていない場合は入力エラー
とします。
送信元で文字数が確認されていないシステムの場合、何文字でバリデーションエラーにするかは仕様で決めて構いません。1500文字でも、1万文字でも構いません。安全性を考えて妥当な数値を仕様として決めます。
郵便番号など特定の形式の形式を求めている項目でも同じです。送信元で確認されているかどうかを考慮して仕様を決めます。
入力バリデーションはセキュリティ対策
入力バリデーションは「直接、セキュリティ問題を解消する物ではないからセキュリティ対策ではない」と勘違いされている方も居るでしょう。しかし、セキュリティ対策は直接的セキュリティ問題(攻撃可能な脆弱性)を解消するモノだけがセキュリティ対策ではありません。
セキュリティ対策は
- リスクを変化させるモノ
がセキュリティ対策です。国際ITセキュリティ規格のISO 27000ではこのように定義しています。
「リスクを変化させても直接対策ではないのでセキュリティ仕様ではなく、アプリケーション仕様として管理すればよい」などという意見も見られます。しかし、これは正しくありません。セキュリティ対策とはリスク対策、つまりリスクマネジメントです。
入力バリデーションはリスクを変化(しかも効果的に変化)させます。リスクを変化させるモノをセキュリティ対策として管理するのがセキュリティマネジメントです。入力バリデーションをセキュリティ対策として管理しないことは、セキュリティマネジメントに問題があることを意味します。
ISO 27000では明示的に入力バリデーションのような処理(対策)を実装し、セキュリティ対策の一環として定期的に見直すことを要求しています。独自のセキュリティ概念でどれがセキュリティ対策で、どれがセキュリティ対策でない、と定義する人もいます。論理的整合性があるならまだ良いのですが、ほとんどの場合は論理的整合性がありません。これでは混乱を生むだけです。通常はISO 27000などの標準的な概念を利用すべきです。
セキュリティ対策はセキュリティマネジメント
セキュリティマネジメントの基本は”境界防御”です。入力バリデーションは境界防御の1つであり、セキュリティ教育やコンサルティングを手がけるSANSの資料でも一番のセキュリティ対策であるとしています。
ソフトウェア開発手法を熟知していたり、個々の脆弱性の攻撃手法を熟知している人でもセキュリティマネジメントを理解していない方が多くいます。(少なくとも多くいるように私には見えます)セキュリティ対策のベストプラクティスとソフトウェア開発のベストプラクティスは相反する関係にあることを知らなかったり、個別のセキュリティ脆弱性に対処するだけでは全体最適化はできないことを知らない場合が多いように思えます。
利用者としては個々のセキュリティ問題に対応することも重要ですが、どうやったら攻撃されない、攻撃されにくくなるかの方が重要です。やるべき事は色々ありますが、ソフトウェア開発を委託する場合、
- 入力バリデーションは境界(入力受け入れ直後)で行い、全ての外部入力に対して行うこと
- 入力バリデーションを行わない例外の入力はその理由をセキュリティ仕様として明記すること
- 入力仕様を厳格に定義すること
- 入力バリデーション仕様を明確にすること
- 正しい入力のみ受け付け、異常な入力は入力バリデーションエラーとして処理を中止しログを記録すること
- バリデーションはホワイトリスト型で行うこと
- バリデーションとしてデータ型のキャスト(変換)を行う場合、そのキャストが妥当である理由を明記すること
- ブラックリスト型の例外はその理由/仕様を明記すること
などと入力バリデーションを要求仕様に入れるだけでリスクを低減できます。入力バリデーションの具体例としては既に挙げた以下を例としてよいでしょう。
- 名前(パラメータの名前)
- 数(パラメータの数)
- 値の形式(文字種、電話番号形式、郵便番号形式、URL形式、ファイル形式など)
- 値の範囲(最大、最小)
- 値の長さ(最大、最小)
- 文字列のエンコーディング
ただし
- これらは例であり、これらに限らず、適切なバリデーションを行うこと(XMLやJSONの検証など)
さいごに
ここでは誤解の多い入力バリデーションのみに焦点を当てていますが、入力バリデーションは効果的な対策であってもセキュリティ対策の1つに過ぎません。ソフトウエアのセキュリティで2番目に大切な物は「正確な出力」です。セキュリティ問題の多くは出力で「必要な部分だけ使えるようにする」ようになっていないことが原因です。ソフトウェアが誤作動しない、誤作動できない、正確な出力が行われていないため、セキュリティ問題が発生しています。
ソフトウェア開発を委託する場合、
- 出力先の入力仕様を正確に理解し、安全な出力がデフォルトとなるよう実装すること(例えば、デフォルトで安全な出力となるよう全ての出力を安全にエスケープ/エンコードする。またエスケープ仕様を明示すること)
- 出力先に安全に出力するAPIがある場合、APIの仕様(制限)を正確に理解しAPIを利用すること(APIの制限を明示すること)
- 出力のエスケープ、安全なAPIが利用できない場合、出力時に安全であることをバリデーションし、エラーの場合は処理を中止しログを記録すること
- 出力のセキュリティ対策は可能な限り境界(出力の直前)で行うこと
- 出力のセキュリティ対策を境界で行えない場合、セキュリティ仕様を明示すること(例えば、HTMLを含む出力が安全である理由を説明すること)
などを出力のセキュリティ対策を要求仕様に入れるとリスクを低減できます。
要約すると開発を委託する場合、開発会社にはSANS TOP 25の怪物的なセキュリティ対策くらいは最低限守ってもらいましょう。開発者は最低限これらは守りましょう。
入力、出力のセキュリティ対策が重要である理由は「開発者が入力/出力に無頓着でも、ソフトウェアは”正常系”のデータで意図通りに動作してしまう」ことにあります。発注者の立場なら、開発者が無頓着にならないような要求仕様にすれば確実に改善します。これは発注者を開発側のプロジェクトマネージャーに置き換えても成り立ちます。
テスト時に異常系のデータでテストすれば良いのでは?と考えるかも知れません。しかし、この方法ではブラックリスト型のセキュリティになってしまいます。ブラックリスト型のセキュリティはホワイトリスト型に比べ劣ります。ホワイトリスト型が利用できる場合、ホワイトリスト型を利用すべきです。
上手くまとまらなかったので、また書くことになると思います。安全なソフトウェアを開発してもらうための必須要求仕様、というテーマに絞った方が良かったように思いますが参考になれば幸いです。