なぜPHPアプリにセキュリティホールが多いのか?

第45回入力バリデーションはセキュリティ対策

「入力バリデーションはセキュリティ対策である」は世界の常識です。少なくとも大多数の世界のセキュリティ専門家は「入力バリデーションはセキュリティ対策」は常識だと考えています。

もちろん入力バリデーション対策がすべてであるわけではなく、ほかのセキュリティ対策も重要ですが、入力バリデーションが最も重要な対策の1つである、と考えている人は沢山います。

入力のバリデーションはすべてのプログラムに必須の保護機能です。一部に「入力バリデーションはセキュリティ対策ではない」とする声もあるようですが、入力バリデーションは間違いなくセキュリティ対策です。入力バリデーションはセキュリティ対策ではない、とする考え方は、混乱を招くだけです。

入力バリデーションをセキュリティ対策としているのは筆者のみではありません。世界的に利用されているセキュリティ標準でも、入力バリデーションを非常に有効なセキュリティ対策としてとらえられています。

今回は入力のバリデーションが何故セキュリティ対策であるか考えてみます。

“バグ”“脆弱性”“セキュリティホール”

セキュリティ対策は、コードのみではなく施設のセキュリティ対策からバックアップ方法やその保管方法、要員のトレーニング、社員や関連業者との契約内容のセキュリティ対策など、多岐に渡ります。ここではコーディングにおけるバグと脆弱性とセキュリティホールの関係を解説します。

入力バリデーションが論理的にセキュリティ対策であることは、バグと脆弱性の関係から明らかです。

バグ:
意図しないプログラムの動作。コードの問題でなく設計の問題である場合もある。
脆弱性:
バグの中でセキュリティ維持に悪影響を与える可能性があるバグ[1]⁠。
セキュリティホール:
脆弱性の中で実際にダメージを与えるバグ[2]⁠。
図1 バグと脆弱性とセキュリティホールの関係
図1 バグと脆弱性とセキュリティホールの関係

入力のバリデーションは、バグも脆弱性もセキュリティホールも減らすための対策です。

入力バリデーションが脆弱性・セキュリティホールには効果がなく、バグを修正する効果しかないのであれば「セキュリティ対策ではない」と言うことも可能でしょう。実際バリデーションだけではそのような効果しか得られないケースもありますが、それ以上に入力バリデーションが脆弱性・セキュリティホールを防いだり緩和したりするケースは沢山あります。

「セキュリティ対策はバグ対策である」と言えますが、反対の「バグ対策はセキュリティ対策にならない」は成り立ちません。論理的にはすべてのバグをなくせば脆弱性もセキュリティホールも自動的になくなります。従ってバグを起こさないための対策だからセキュリティ対策ではないと定義することは間違いであると言えます。

セキュリティ対策標準であるPCI DSSとISOでは入力バリデーションがどのように定義されているか確認してみましょう。

PCI DSS(OWASP/SANS)

PCI DSS(Payment Company Industry Data Security Standard)とは、カード会社がカード決済をコンピュータ上で行っている企業に求めているセキュリティ標準です。大手ではこの標準に従わないとカード決済ができなくなるため、導入が進んでいます。PCI DSSの目標は明確で「カードデータを守る」ことです。目標が絞られているため比較的分かりやすく簡潔な標準になっています。このため、カード決済を行うシステム以外への適用を検討している企業も多いようです。

PCI DSSでは「必要ないデータは保存しない」⁠セキュリティパッチは1ヶ月以内に適用する」など、様々なセキュリティ対策を要求しています。それらの対策の1つにコードレビュー(ソースコードのセキュリティ検査)があります。PCI DSSの6.5には以下のような記述があります。

6.5 Develop applications based on secure coding guidelines. Prevent common coding vulnerabilities in software development processes, to include the following:

Note: The vulnerabilities listed at 6.5.1 through 6.5.9 were current with industry best practices when this version of PCI DSS was published. However, as industry best practices for vulnerability management are updated (for example, the OWASP Guide, SANS CWE Top 25, CERT Secure Coding, etc.), the current best practices must be used for these requirements.

筆者による訳(※現在、日本語版を選択しても英語版となるため)

6.5 セキュアガイドラインに従ったアプリケーション開発を行わなければならない。以下のソフトウェア開発における一般的なコーディングにおける脆弱性を防ぐできである。

備考:6.5.1から6.5.9に記載された脆弱性対策はPCI DSSが公開された時点でのベストプラクティスである。しかし、ベストプラクティスは脆弱性対策とともに更新される(例:OWASPガイド、SANS CWE Top 25、CERTセキュアコーディング、など⁠⁠。これらの要求事項に適合させるためには現在のベストプラクティスを利用しなければならない。

※強調は筆者による

PCI DSSでは、OWASPガイド、SANS CWE Top 25(正式名称:CWE/SANS TOP 25 Most Dangerous Software Errors、以下SANS Top 25⁠⁠、CERTセキュアコーディングなどに従ったコードのセキュリティ検査を求めています。

OWASPガイド

OWASP(Open Web Application Security Project)はPCI DSS策定に深く関わっていることでよく知られています。OWASPのコードレビューガイド Ver 1.1の入力バリデーションの部分には、このように記載されています。

Input validation is one of the most effective application security technical controls. It can mitigate numerous vulnerabilities (but not all). Input validation is more than checking form field values.

訳:入力バリデーションは最も効果的なアプリケーションセキュリティ制御技術の1つです。入力バリデーションは(すべてではないが)おびただしい数の脆弱性の影響を緩和することができます。入力バリデーションはフィールド値をチェックするだけではありません。

入力バリデーションは最も効果的なアプリケーションセキュリティ制御技術の1つと、OWASPガイドでも入力バリデーションをセキュリティ対策の重要な機能だと位置づけていることが分かります。

SANS Top 25

SANSはセキュリティのトレーニング、セキュリティ専門家の認証やセキュリティ研究を行っている機関です。ITセキュリティの教育と研究を共同で行う機関として1989年に設立されました。CWE IDを管理しているMITRE社と協力関係にあることでも知られており、MITRE社のサイトでもSANS Top 25は公開されています

PCI DSSで参照されているSANS Top 25の中で「Monster Mitigations」(適切な翻訳を思いつきませんが、翻訳するなら「恐ろしく効果的な対策」くらいでしょうか)として挙げられている効果的な対策の第一番目が入力バリデーションです。

IDDescription
M1Establish and maintain control over all of your inputs.
M2Establish and maintain control over all of your outputs.
M3Lock down your environment.
M4Assume that external components can be subverted, and your code can be read by anyone.
M5Use industry-accepted security features instead of inventing your own.
GP1 (general)Use libraries and frameworks that make it easier to avoid introducing weaknesses.
GP2 (general)Integrate security into the entire software development lifecycle.
GP3 (general)Use a broad mix of methods to comprehensively find and prevent weaknesses.
GP4 (general)Allow locked-down clients to interact with your software.

ここでも入力バリデーションは非常に重要なセキュリティ対策の第一番目として取り上げられています。

2009年のSANS Top 25CWE/SANS最も危険なプログラミングエラーTOP 25:日本語版)では、第一位として不適切な入力バリデーションのCWE-20が上げられていました。

CWE-20: 不適切な入力の妥当性チェック

これは、健全なソフトウェアが犠牲となる原因の第一位であるため、所定の基準に従った入力を条件付けておかないと、自ら災難を招くことになる。

詳細: http://cwe.mitre.org/top25/#CWE-20

※強調は筆者による

SANSでも入力バリデーションを非常に重要なセキュリティ対策であるとしていることがわかります。

CERTセキュアコーディング

CERTセキュアコーディングはC/C++/Javaのコーディング指針として非常に参考になる文書です。しかし、項目ごとのコーディング指針であるため順位などはないので、どのように記載されているかは省略します。

ISO27000 (ISO27002/ISO17799)

ISO27000はISMS(Infomation Security Management System)を構成する国際セキュリティ標準です。ISMS認証の必須事項となるISO27001と、そのガイドラインとなるISO27002で構成されています。JIS標準にもなっている情報セキュリティ対策標準の基本中の基本と言える規格です。筆者はISO標準になる前のBS7799(英国の標準規格)の頃からセキュリティ対策の参考にしています。BS7799はISO17799として標準化されました。ISMS導入の際に番号の振り替えがあり、現在はBS7799−2がISO27001、ISO17799(BS7799−1)がISO27002となっています。この標準は世界中のセキュリティ対策のスペシャリストの指針と言っても構わない標準規格です。

ISO/IEC 27002:2005

12.2 業務用ソフトウェアでの正確な処理

12.2.1 入力データの妥当性確認

管理策

業務用システムに入力されるデータは、正確で適正であることを確実にするために、その妥当性を確認することが望ましい。

実施手引き

業務取引処理、常備データ(名前、住所、信用限度額、顧客参照番号)及びパラメタ(売価、通過交換レート、税率)の入力を検査することが望ましい。次の管理策を考慮することが望ましい。

  • a) 次の誤りを検出するための二重入力又はその他の入力検査。
    • 1) 範囲外の値。
    • 2) データフィールド中の無効文字。
    • 3) 入力漏れデータ又は不完全なデータ。
    • 4) データ量の上限及び下限からの超過。
    • 5) 認可されていない又は一環しない制御データ。
  • b) その妥当性及び完全性を確認する為に重要なフィールド又はデータファイルの内容の定期的見直し。
  • c) 入力データに認可されていない変更があるかどうかにすついての神に印刷した文書の点検(入力文書に対する変更はすべて、認可を得ることが望ましい。⁠⁠。
  • d) 妥当性確認の誤りに対応する手順。
  • e) 入力データのもっともらしさを試験する手順。
  • f) データ入力仮定に関わっているすべての要員の責任の明確な定義。
  • g) データの入力処理に関わっている作業ログの作成(10.10.1 参照)

関連情報

誤りによるリスクを低減させて、バッファオーバーフロー及びコードインジェクション(code injection、コード注入)を含めて標準的な攻撃を防ぐために、適用できる場合は、入力データの自動的な検査及び妥当性確認を考慮することができる。

「業務ソフトウェアの正確な処理」⁠入力データの妥当性確認」は必須事項であるISO27001の項目となっています。実施手引きは金融機関のシステムを念頭にいれた記述ですが、検出の要領などは一般のシステムにも適用できます。

最も重要なセキュリティ対策として扱っているPCI DSS関連のガイドラインほどではありませんが、入力のバリデーションをセキュリティ対策として行うように求めています。関連情報は多少分りづらいですが、バッファーオーバーフローによる任意コード実行防止策などがあるので、利用できる場合は考慮したほうがよいということでしょう。

このようにISO/JIS規格でも入力のバリデーションをセキュリティ対策の一環として実施するよう推奨しています。

セキュリティ対策とは何か?

機密性(Confidentiality⁠⁠、完全性(Integrity⁠⁠、可用性(Availability⁠⁠、責任追跡性(Accountability⁠⁠、真正性(Authenticity⁠⁠、信頼性(Reliability)※3を担保するための対策すべてがセキュリティ対策です。入力バリデーションはこれらすべてを担保するために役立つ対策です。

機密性許可されたユーザだけがデータを参照できること
完全性許可された方法だけでデータを操作できること
可用性サービス提供を行うべき時に提供できること
責任追跡性ユーザやサービスの動作を特定して追跡できること
真正性ユーザやプロセスなどがリソースなどを要求した場合に同一の物であることを
信頼性矛盾なく要求仕様通りの動作をおこなうこと

※4

例えばC言語で、整数の入力バリデーションを怠ると整数オーバーフローによるバッファオーバーフローが、入力の大きさのバリデーションを怠るとバッファオーバーフローで任意コード実行やメモリ内容を取得されたりシステムを停止させられたりする問題が発生します。任意コード実行は完全性、機密性、可用性、責任追跡性、真正性、信頼性、すべてに関わる問題です。メモリ内容の漏洩は機密性に関わります。システム停止は可用性、信頼性の問題です。

Webプログラマに身近な例で、入力バリデーションがセキュリティ対策として直接役立つ分かりやすい例は、SQL文のLIMIT句の値でしょう。

SELECT * FROM big_table LIMIT 9999999999999999;

のようなクエリが実行されるとリソースが枯渇し、DoS[5]状態になるシステムも少なくないと思われます。

念のためにバリデーションを行うことで脆弱性が回避される例も紹介します。数値IDであることを入力時にバリデーションしていれば、出力時にうっかりエスケープし忘れた、適切なヘルパーを使うことを忘れてしまったなどの脆弱なコードを書いた場合でも、JavaScriptインジェクションやSQLインジェクションなどの問題が回避できます。

役立たないセキュリティ対策はない

「セキュリティ対策で役立たないもの」はありません。例えば、HTTPベーシック認証はユーザ名とパスワードをBASE64エンコードしているだけなので安全ではないと言われています。HTTPベーシック認証ではパケットスニファなどでパケットをキャプチャされるとユーザ名とパスワードが丸分かりとなり、安全ではありません。しかし、SSLを使っているなら十分安全に利用できます。SSLを使っていなくても信頼できる経路でのみ利用するのであれば安全と言えます。HTTPベーシック認証で十分な環境であればセキュリティ対策として役に立つのです[6]⁠。

不十分・欠陥があるセキュリティ対策を「セキュリティ対策ではない」と切り捨てると、有意義な議論やシステム構築ができなくなってしまう場合があります。例えば、現在広く利用されているユーザ名とパスワードによる認証は、重大な欠陥があるセキュリティ対策です。ユーザ名とパスワードを何らかの方法で取得すれば誰でも認証してしまいます。近い将来、ユーザ名とパスワードによる認証はセキュリティ対策として不十分だと認識される日がくるとは思います。しかし、現在「ユーザ名とパスワードによる認証はセキュリティ対策ではない」などと議論しても有意義ではありません。たとえパスワードによるユーザ認証が不十分だと認識されるようになっても、それで十分であるシステムでは役立つ認証システムです。

「入力バリデーションは直接的なセキュリティ対策ではないことが多いからセキュリティ対策ではない」と主張することも可能かもしれません。入力バリデーションはセキュリティ問題に対する直接的な対策である場合もあれば、そうでない場合もあります。オーバーフローやリソース枯渇によるDoSなどを除けば、入力バリデーションは直接的なセキュリティ対策とは言いがたい場合もあります。OWASPガイドに書かれているように、数えきれないほどある脆弱性を緩和する対策のうちのひとつととらえるほうがよいでしょう。バグ対策や間接的なセキュリティ対策であっても、ITセキュリティに役立つものであればセキュリティ対策であることには変わりません。

「セキュリティ対策で役立たないものはない」と初めて聞いたのはアメリカのセキュリティ専門家のブログだったと思います。セキュリティ対策には優劣や欠陥があったり、適用できる場面が限定されていたりする場合がありますが、必要な要件を満たしていればどんなセキュリティ対策でも役に立つ場合があります。⁠セキュリティ対策で役立たないものはない」は、⁠○○はセキュリティ対策ではない」といった不毛な議論を起こしたり、よく理解していない開発者やユーザを混乱させないための発言だったと記憶しています。このせいか最近では「○○はセキュリティ対策ではない」といった議論はあまり聞かなくなったように思います。

セキュリティ対策とバグ対策を区別する理由

ITセキュリティ対策はシステムの機密性、完全性、可用性、責任追跡性、真正性、信頼性を担保するための対策で、システムに必要なセキュリティ対策を取り入れることは当然のことです。セキュリティ対策はバグのないシステムを目指した対策ではありませんが、バグのないシステムは自動的に機密性、完全性、可用性、真正性、信頼性も担保[7]できます。しかし、バグのないシステムを目指した対策を網羅しようとすると、すべての対策を網羅したセキュリティ対策の何倍もの対策と知識が必要になります。開発者への負担を軽減するために、セキュリティ上必要な対策とそうでない対策は区別するほうが分かりやすく、習得しやすくなります。

図2 セキュリティ対策とバグ対策
図2 セキュリティ対策とバグ対策

セキュリティ要件の厳しいプログラムでは「バグ対策=セキュリティ対策」となるようなものもありますが、一般的なプログラムであれば「バグ対策>セキュリティ対策」となり、バグ対策よりセキュリティ対策のほうが少なくなります。⁠バグ対策<セキュリティ対策」となることは論理上ありません。

言うまでもなく、バグのないプログラムであるほうがよいに決まっています。しかし、バグのないプログラムはほとんどありません。バグを作らないための知識は多岐に渡ります。例えば、浮動小数点型データの比較において、等価比較してはならないということを知らない開発者も多いでしょう。これは浮動小数点演算の標準であるIEEE754の仕様からの制限です。コンピュータサイエンスでは浮動小数点型の等価比較はよほど慎重に行わない限りやってはならないは常識であると思いますが、必ずしもすべてのエンジニアが知っているわけではないようです。

浮動小数点の比較による問題は多くのシステムでただのバグと分類される問題でしょう。Webアプリの開発者であっても浮動小数点演算の問題は理解しておくほうがよいですが、一般的なWebアプリの開発者がセキュリティ対策の知識として必ず身につけておくべき知識[8]とまでは言えないと思います。しかし、浮動小数点演算の標準を知らない開発者が金融機関や会計システムなどのシステム設計を担当している場合、この知識の欠如はシステムの完全性を脅かすセキュリティ問題です。このようにセキュリティ要求事項によっても必要となる知識が異なることがよくあります。

必要なセキュリティ対策はアプリケーションや要求仕様によって大きく異なります。一般的なWebアプリに必要とされるセキュリティ対策と一般的な金融機関の基幹アプリに必要とされるセキュリティ対策では大きな違いがあります。セキュリティ対策の項目は沢山あり範囲も広いです。しかし、バグを作らない対策はさらに膨大かつ広範囲です。特定のアプリケーションや要求仕様別に、セキュリティのCIA[9]を守るための対策を「セキュリティ対策」として区別することで、最低限必要な対策をコンパクトにすることができます。このような、セキュリティ対策とバグ対策を区別する考え方に異論を持つ方はほとんど居ないのではないでしょうか。

よい上司は部下に優先順位を明示して指示をします。これと同じでよい開発マネージャはセキュリティ対策とバグ対策を区別して指示を出します。セキュリティ対策とバグ対策では優先順位も範囲も異なるので、区分しないと適切な対応が難しくなります。

「優れたセキュリティ対策」ではなく、「必要なセキュリティ対策」を採用する

前回Rails標準のバリデーションには欠陥があると指摘しました。この欠陥は脆弱性ではありますがセキュリティホールではありません。Railsは元々DRY(Don't Repeat Yourself)を実現し、80%のWebアプリを素早く作ることを目標にしています。Railsの開発者はこの要求事項を達成するためにいろいろ余計なものを削除しています。Railsの開発者もこのバリデーション仕様の欠陥はよく理解していることでしょう。そして、バリデーション仕様の欠陥を指摘すると恐らく「8割のWebアプリはこの仕様で安全に作れるよね」と答えるでしょう。欠陥はあっても要求事項にあっているので、仕様的なセキュリティ脆弱性があってもセキュリティ問題だとはとらえないことはよくあります。前回の記事の反応からはほとんどのRailsユーザはこの仕様の欠陥を理解しているようでした[10]⁠。Railsに限らず同様の仕様になっているフレームワークの利用者はよく理解して使う必要があります。

「役立たないセキュリティ対策はない」で紹介したパスワード認証も、⁠優れたセキュリティ対策ではなく、必要なセキュリティ対策を採用する」※11よい例です。パスワード認証は非常に脆弱で欠陥だらけですが、要求事項にあっているのでセキュリティ問題だと考えられていないケースはよくあります。パスワード認証より優れた認証方式やパスワード認証を補強する対策は数多く存在しますが、安全保障に関わるシステムなどの高度なセキュリティ対策を要求されるシステムを除けば、これらの認証方式や対策を利用しないシステムを脆弱で使えないシステムだという人は居ないと思います。

入力バリデーションは未知の脆弱性を含む、非常に多くの脆弱性を緩和したり防止したりする効果があります。OWASPやSANSが非常に高く評価しているように、この意味では非常に優れたセキュリティ対策と言えます。一方、入力バリデーションはセキュリティ対策として多くの脆弱性の直接的な対策ではないとも言えます。この意味では優れた対策とは言えないかも知れません。仮に入力バリデーションを優れたセキュリティ対策としてとらえていなくても、入力バリデーションはパスワード認証と同様、その対策の優劣に関係なくセキュリティを担保するための重要なセキュリティ対策だと考えるべきでしょう。

まとめ

標準規格であっても、欠点があったり、不足している部分や間違いと言える欠陥があることもよくあります。標準規格は完全な存在ではありませんから、異論があっても当然です。しかし、論理的に矛盾が起きてしまうような主張や根本を覆すような主張は、無用な混乱を避けるために慎重に行うべきだと思います。

セキュリティ対策の優劣や欠陥、適用性について議論することには意味はあります。しかし、ITセキュリティを担保するある対策がセキュリティ対策であるかどうかを議論したり、考えたりすることには意味がありません。⁠セキュリティ対策」を独自に定義しているなら別ですが、国際的な合意としてITセキュリティ(機密性、完全性、可用性、責任追跡性、真正性、信頼性)を担保する対策がセキュリティ対策と考えられています。標準規格は円滑なコミュニケーションを行う土台としての意味もあります。このために規格では用語の定義を明確に定めています。⁠Webアプリセキュリティ対策入門」にも書いていますが、すべての開発者はセキュリティ標準規格を学ぶ機会を作るべきです。ISO27000や関連ISO規格、PCI DSS[12]も分量はそれほど多くありません。図書館に行ってその場ですべて読めるくらいの分量です。特にこれからセキュリティについて本格的に勉強しようと考えている方は必ず読むようおすすめします。

セキュリティ対策として議論すべきは完全性やコストなどの優劣、セキュリティ対策自体の欠陥、どのような場合に使うべきか、使えるかなどの適用性です。欠陥があるからといって、その対策は使いものにならないわけではないことには注意してください。広く使われている欠陥のある対策としては、例えば「ライセンス認証」です。難解にするだけのセキュリティ対策は通常は決して使ってはならないとされています。この考えは正しく、難解にする対策はセキュリティに欠陥がある対策です。しかし、今でもライセンス認証は広く有効な対策として利用されています。Railsのバリデーション仕様も同じです。欠陥はあっても要求仕様に合っていて合理的であれば十分使えるセキュリティ対策です。

以上からも分かるように、入力バリデーションはセキュリティ対策です。システムが意図通りに動作するように対策する入力バリデーションをセキュリティ対策ではないとするならば、システムが意図通りに動作するようエスケープしたりヘルパー関数(Viewヘルパーやプリペアードクエリ)を利用することもセキュリティ対策ではないということになってしまいます。入力バリデーションがセキュリティ対策か迷う余地はありません。

なぜこのようにおかしな議論になるのか?原因は簡単です。議論の出発点が間違っているからです。ITセキュリティを担保する対策がセキュリティ対策であり、入力バリデーションはITセキュリティを担保することができるセキュリティ対策だからです。

今回もPHPとは関係ないセキュリティの一般論になってしまいました。一部の方からは「なぜPHPアプリに」ではなく「なぜWebアプリに」とタイトルを変えたほうがよいのでは、と言われるくらいです。近いうちにPHPの話をするのでご容赦ください。

最後に、筆者が考えたセキュリティ対策の議論を有益な議論にするためのチェックリストを記載しておきます。

セキュリティ対策の議論を行う場合のチェックリスト
  • □ ITセキュリティを担保する対策であるかどうか?(真なら欠陥があってもセキュリティ対策)
  • □ どのような利点があるか?(利点は利便性・コストであることが多い)
  • □ どのような欠点があるか?(欠点はコスト・リスク要素であることが多い)
  • □ どのような場面に適用できるか?できないか?(ユースケース)
  • □ どれくらいのリスクが予想されるか?(欠点!=リスク)
  • □ 対象のアプリの要求事項に合っているか?合理的か?
  • □ 利点・欠点・リスク・ユースケースと要求事項を総合的に判断して妥当であるか?(ニーズに合うか、リスクを許容できるか)

このリストからも分かるように、セキュリティ対策はコンテクスト抜きに考えることはできません。次回はセキュリティ対策を考える上で欠かせないコンテクストについて考えてみたいと思います。

おすすめ記事

記事・ニュース一覧