プログラムから「想定外」を無くす方法

(Last Updated On: 2019年2月7日)

ほとんどセキュリティ問題は「想定外の動作」が原因です。例えば、インジェクション攻撃は開発者にとって「想定外の動作」でしょう。ソフトウェア開発者が意図しない想定外の動作のほとんどは「想定外のデータ」が原因です。

この事実をから簡単に分かる「想定外」を無くす方法があります。たった2つの方法を”両方”行うだけで、現在あるアプリの9割程度の脆弱性が無くなります。

今まで出来る限り正確に「どうすれば想定外をなくせるか?」を紹介してきましたが、今回は出来る限り簡潔にどうすると良いのか、ダメなのか紹介したいと思います。

「想定外」の入力データを含むリクエストは完全に廃除する

「想定外な動作」を幅広く廃除する方法は「入力バリデーション」です。プログラムが期待する入力は、処理可能な

  • 正しい入力データ
  • 入力ミスのデータ

のみです。バリデーションを行い、これらだけ受け付ければ「想定外のデータ」により「想定外の動作」を起こす可能性がなくなります。1

これ以外は全て「想定外の”無効”なデータ」です。プログラムはクライアントが送信可能なデータのみを受け付けます。

参考図:入力値の種類は3種類しかない、より

例え1行のコードでも「想定外の”無効”なデータ」に対して何らかの処理することは意味がないどころか、攻撃者に付け入られる隙を作ることになります。「想定外の”無効”なデータは一切受け付けない」が「想定外」を無くす第一歩です。

「想定外のデータ」はFail Fast原則に則り、できる限り早く、1行でも少ないコード実行で済むように廃除しなけばなりません。「想定外のデータ」を「想定内のデータ」に変換するのはNGです。1つでも想定外のデータが存在する場合、1つでも想定内のデータが存在しない場合、そのリクエストは丸ごと無効として廃除します。2

  • 原則: 全ての入力を可能な限り早く、厳格に検証、「想定外のデータ」を廃除する
  • アンチプラクティス:入力検証なし、不十分な入力検証で「危い想定外データ」を適当に処理する

入力検証が甘いソフトウェアの場合、発見しづらい致命的な「想定外」が起きる可能性が高くなります。

参考:セキュアコーディングの第1原則 ー 入力をバリデーションする

「想定外」の出力データを絶対に送信しない

コード検査をしていると出力の無害化を省略しているコードをよく見かけます。「想定外のデータ」がプログラム内部で処理されないように、入力処理で全てのデータを検証され、廃除されいればこれでも”概ね”安全と言えます。しかし、現実のプログラムのほとんど全ては全てのデータを検証していないし、検証していても不十分です。

仮に”完璧”に入力検証を行っているとしても、出力対策は”独立”したセキュリティ対策なので、”出力の完全な無害化”は入力検証と無関係に実施します

「想定外の動作起こす」出力データを送信しない方法は簡単です。全ての出力データを無害化して送信するだけです。出力データの無害化はたった3つの方法を知るだけで完璧に行えます。

  • 原則: 全ての出力を出力対策の3原則に従い、出来る限り出力直前に、完全に無害化する
  • アンチプラクティス: APIを使っているから安全、検証済みデータだから、と高を括る

出力先は外部システムとは限りません。正規表現やXML処理など、複雑な処理を行うライブラリも含まれます。

参考:セキュアコーディングの第7原則 ー 他のシステムに送信するデータを無害化する。完全なSQLインジェクション対策。

まとめ

  • 「想定外」の入力データを含むリクエストは完全に廃除する
  • 「想定外」の出力データを絶対に送信しない

上記のセキュリティポリシーで構築されているWebアプリケーションは極少数です。

ソフトウェア実行時に発生する「不確実性」を可能な限り廃除するには、前提条件として上記のセキュリティポリシーが必要であることは明らかです。このためセキュリティガイドラインではベストプラクティスとして入力/出力の「確実性」を求めるモノになっています。しかし、現在、一般的なWebアプリケーションはアンチプラクティスを実践して作られています。

入力データの妥当性保証の責任は入力処理コードにあります。出力処理の安全性保証は出力処理コードにあります。

  • アンチプラクティス:入力検証なし、不十分な入力検証で「危い想定外データ」を適当に処理する
  • アンチプラクティス: 出力に、APIを使っているから安全、検証済みデータだから、と高を括る

「不確実性」を廃除しないアンチプラクティスのせいで「APIの使い方を間違えた」などの「ちょっとした不注意」で攻撃可能な脆弱性ができる状態になっています。

「ちょっとした不注意」はアプリケーション開発者が書いたコードだけではなく、フレームワークやライブラリに在る場合も多いです。「フレームワーク/ライブラリの脆弱性はアプリ開発者の責任ではない!」とする意見もありますが、ユーザーから見ると「アプリで簡単に対応できる問題なのに未対応なのはアプリ開発者の責任だ!」となるでしょう。

そもそもプログラムには「不正な値」、「ちょっとした不注意」が無いことを前提、つまりセキュリティ対策無し、も少なくありません。

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

アンチプラクティスを廃除し、ベストプラクティスを実践するだけで、言語/フレームワークを問わず、今のWebアプリに存在する9割程度の攻撃可能な脆弱性が無くなります。

  • 原則: 全ての入力を可能な限り早く、厳格に検証、「想定外のデータ」を廃除する
  • 原則: 全ての出力を出力3原則に従い、出来る限り出力直前に、完全に無害化する(「想定外のデータ」であっても余計な命令を実行させない)

入力処理も出力処理も原則(出来る限り出力直前 3 )を無視し、アンチプラクティスで作られているモノが大多数です。当然の結果としてWeb脆弱性診断で簡単に脆弱性が見つかるWebアプリが多数存在します。「Web脆弱性診断で簡単に脆弱性が見つかる=本物の攻撃者も簡単に脆弱性を見つけられる」です。

参考:Web脆弱性診断で送信される無効なデータを記録/対応しないアプリは脆弱なアプリ、になりました。

入力処理と出力処理だけでは全ての「想定外」に対応できません。全てのセキュアコーディング原則を参考にしてください。最後の原則ですが「セキュアコーディング標準を導入する」は重要です!入力対策、出力対策、セキュアコーディング標準の構築でほとんどの「想定外」を無くすことができます

参考:セキュアーコーディング標準は出来合いの標準を導入する物、ではありません。セキュアコーディング標準はそれぞれの開発プロジェクト用に構築する物です。セキュアコーディング標準には確実な入力出力の取り扱い方法はもちろん 整合性が必要な場合にトランザクション分離レベルをシリアライザブルにするセキュアなAPIキーの取り扱い方法パスワードのハッシュ化方法、など、確実に正しい実行に必要なモノ、間違い易いモノをアンチプラクティスとベストプラクティスを対比する形式で記載します。

参考:「予期しない動作をしにない」=「正しく動作する」プログラムを作るには、正しい「コード」と「データ」の両方が必要です。

今さら聞けない「コード」と「データ」の話

バリデーションには3種類のバリデーションがある 〜 セキュアなアプリケーションの構造 〜

アプリとライブラリの「役割と責任」の違い – セキュリティの基礎


  1. 「想定外の動作」を入力処理で完全に廃除することはできません。正しいロジックと安全な出力、これらも必要です。 
  2. よくある不十分なバリデーションに余計なデータの許可があります。攻撃者は”debug”、”test”、”admin”フラグが使えないか?他のリクエストで使えるパラメーターが使えないか?パラメーターを省略して無効な操作が可能か?といった攻撃が可能か見つけようとします。 
  3. 「出来る限り出力直前に無害化」するのは開発者が出力コードを見ただけで完全に無害化できていることを確認できるようにする為です。しかし、HTMLの様にINPUTなど”HTML部品”をロジックの中で”出力”している場合があります出力の直前、とはビューの中やビューの直前に、という意味ではなく”文字通り出力の直前”です。HTML出力ヘルパーなどの様に”出力の直前”はロジックの中にある場合もあります。管理されない形でプログラムロジックの中で無害化してしまうと、出力が完全に無害化されているかどうか?検証が非常に困難になります。 

投稿者: yohgaki