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

第28回 あまり語られないセキュリティの基本 ── トラストバウンダリ

この記事を読むのに必要な時間:およそ 4 分

プログラムにおけるセキュリティ対策とトラストバウンダリ

トラストバウンダリのコンセプトは非常に単純かつ明快です。しかし,この基本が正しく理解されていないことが多いのです。例えば,数年前に某大手ベンダーがセキュアプログラミングの動画を公開し,トラストバウンダリを使ってどのように安全性を確保するか解説していました。その動画ではなんとトラストバウンダリの中にデータベースが含まれており,前提条件無くデータベースが信頼できるシステムとして扱われていました。

図1 間違ったトラストバウンダリの考え方

図1 間違ったトラストバウンダリの考え方

この連載の読者の皆さんの多くは,データベースを「信頼できる」ものとして取り扱ってはならないことは十分理解していると思います。しかし,数年前まではセキュリティの専門家であってもこのような基本的な間違いをしていました。これでは何時まで経ってもSQLインジェションが無くならないのも当然です。

信頼できる,安全である,と確証が取れたもの以外はすべて信頼できないと考えるのがトラストバウンダリの考え方の基本です。

図2 正しいトラストバウンダリの概念

図2 正しいトラストバウンダリの概念

現実の世界でも同じですが,バウンダリの中にあるからといってすべて信頼できる人物や物ばかりではありません。食品工場であっても,殺虫剤のように危険な物を意図的に入れる場合もあるでしょう。危険物であっても正しく使えば有効な物を持ち込まなければならないケースがあることは実世界でもアプリケーションでも同じです。

例えば,かなり厳重に入力チェックをしているWebアプリでもクッキーをバリデーションしていないアプリは沢山あるでしょう。バリデーション済みのデータが「入力」としては問題がなくても,ブラウザやSQLデータベースに安全に出力できないデータも沢山あります。クッキーはセッションIDのみに使用している場合など,クッキーはチェックする意味があまりないため,あえてチェックしていないことが多いです。

厳重に入力チェックをしているはずアプリでも,危険なデータや危険かも知れないデータやプログラムもバウンダリの中に含まれている場合があることを忘れてはなりません。常に「データのソースはどこなのか?」⁠データは安全なのか?」を意識しながらプログラムを作らないと本当に安全なアプリは作れません。

実際のバウンダリ管理

図2にはライブラリやデータファイルなど一般的には「信頼できる」と考えられているものも含まれいます。そこまで信頼しなかったら何も作れないのでは? と思われた方もいるでしょう。

確かに何も信頼しなかったらシステム開発はできません。しかし,確認もせず,信頼できる状態も維持しないで無闇に信用しては,セキュリティを維持できません。信頼できるよう確認したり,信頼できる状態を維持してバウンダリ(境界)を管理する必要があります。

先程の図には信頼できない物としてOSが入っていません。これは「通常のシステム管理を行っていれば,OSに脆弱性がない状態で不正なプログラムは入っていない」という前提があるからです。実際にはルートキットなどの不正なプログラムがOSに組み込まれている場合,すべての入力どころか,プログラム中で書き込んだメモリでさえ信用できなくなります。このようなリスクは現実に存在しますが,これを考慮したシステム開発は不可能です。現実のトラストバウンダリの管理では,最低限必要なセキュリティ対策は取られており,安全性が維持されていることを前提にアプリケーション開発を行います。

既に述べたように,図2にOSは信頼できる状態に維持されていると「仮定」してあえて入れませんでした。OSが信頼できない状況は現実に起こり得るのですが,OSさえも信頼できないことを想定してのシステム開発無理です。しかし,先程の図にはライブラリやWeb開発フレームワークは入れています。これは実際にリスクがあり,アプリケーション開発者の対処が必要であるので載せています。

例えば,PythonのWebアプリ開発フレームワークのDjango 1.0系ではREMOTE_ADDRにX-FORWORDED-FORに設定されたIPアドレスがセットされます。これはリバースプロキシが導入された環境を考慮した仕様と思われますが,この仕様により簡単に外部のユーザがIPアドレスを偽装できてしまいます。新しいDjango 1.1以降ではこの仕様は削除されましたが,1.0系では修正されませんでした。Django 1.0系のWebアプリをメンテナンスする場合,この仕様を知らなければ大きなセキュリティホールを作る可能性があります。

「最低限必要なセキュリティ対策が取られている」ものを拡大しすぎて解釈してならない別の例として,文字エンコーディングベースの攻撃に対する脆弱性を紹介します。PythonのPostgreSQL用のAPIには文字エンコーディングベースのSQLインジェクション対策用のAPIが用意されていませんでした。PostgreSQLが文字エンコーディングベースのSQLインジェクション対策用のAPIを用意してから約5年遅れて脆弱性に対応したAPIが用意されました。XMLライブラリも文字エンコーディングベースの脆弱性が発見されてから何年も遅れて対策がとられています。

文字エンコーディングベースの攻撃手法は正しい文字エンコーディングが利用されていることを確認すれば防げる攻撃です。一部不備が後に発見され修正されましたが,PHPのmbstringモジュールは文字エンコーディングベースの攻撃に対する対策をいち早く取り入れ,脆弱性があるライブラリやAPIであってもWebアプリ内でmb_check_encoding関数を使用することにより攻撃できないように利用することが可能でした。

XML関連ライブラリ等の文字エンコーディングベースの脆弱性は最近でも時々見つかっています。いい加減なデータをライブラリや関数やクラスに送信しても,適当に正しく処理をしてくれる,と考えるのは脆弱性の原因になります。

セキュリティ対策用関数のセキュリティ問題

セキュリティ対策用の関数や機能にセキュリティ上の問題が見つかることがあります。稀にセキュリティ対策用の関数や機能を使っている場合のほうが危険であるケースもあります。例えば,mb_check_encoding関数にはDoSに対する脆弱性が発見されたこともあります。

このようなケースはPHPに限ったことではありません。Linux用の強制アクセス制御の仕組みであるSELinuxを有効にしている場合にのみ権限昇格が可能であったり,アンチウィルスソフトを導入しているために任意コードの実行が可能になるなど,類似の事例は数多くあります。

基本的にはセキュリティ対策用の関数や機能は,その関数や機能が仕様から間違っていない限り使うようにすべきである,と考えています。

一部のPHP関数には「仕様が間違っている」と分類されるべき,使ってはならない関数があります。

  • addslashes: 特にSQLデータベース用のエスケープへの利用は厳禁
  • mysql_escape_string: mysql_real_escape_stringを利用すべき
  • ereg: 安全に利用することも可能だが,通常はmbereg, pregを利用すべき

などがあります。

正しい出力の重要性

最近はフレームワークの普及もあって,入力を厳格にチェックするアプリケーションも増えてきました。しかし,入力チェックに比べあまり重要視されていないのが出力の安全性です。安全性と言うよりは出力の正しさ・正確性・妥当性と言ったほうが分かりやすいかもしれません。WebシステムはWebアプリだけで構成されていることはありません。データベースやメールを使わないシステムであっても,必ずWebブラウザがあります。

出力したデータを不具合なく処理するのは受け手側の責任だ,と考えることは間違いです。縫製工場が「服に針が入っていないか確認するのは消費者の責任だ」と言うことと変わりありません。

Webアプリは,Webアプリが出力するデータで周辺のシステムが問題なく動作する「安全なデータ」を出力する責任があります。WebブラウザはHTTPヘッダ,JavaScrirpt, CSS, HTML, XML,リレーショナルデータベースはSQL文,メールはメールヘッダが誤って解釈されないようデータを出力しなければならないのです。別の言い方すると,プログラマは出力先のシステムでプログラマが意図しない誤作動が起きないように出力を制御する責任があります。

正しい出力の基本の一つ目は「出力先で誤作動を起こさないよう,出力先の仕様に合わせて変数はすべてエスケープすること」です。二つ目は「エスケープできないもの」は本当に安全であるか十分に確認した上でエスケープしないで出力することです。

HTMLもSQLもCSSもJavaScriptも「エスケープできないものを除いてすべてエスケープする」という方針でコードを書くべきです。こうすれば安全性のチェックが容易になる上,⁠整数値であるはずだからエスケープは不必要なはず」など,単純な誤解によるエスケープ漏れを防止することができます。

まとめ

セキュリティ対策を考えるとシステム開発効率が落ちる,とよく耳にします。しかし,通常のシステム開発の必須要件の一つはセキュリティ対策です。効率のよいセキュリティ対策は,結局システムの開発効率と品質を向上させるのです。トラストバウンダリ管理でやるべきことは以下の2つです。

  • 入力チェック: 正しいデータであるか十分チェックする
  • 出力チェック: 出力先で誤作動なく処理できる出力であることを保証する

厳格な入力と出力の管理,厳格なトラストバウンダリの管理は,入出力データの安全性確認を容易にし,システムの開発効率と品質を向上させます。

セキュリティ問題とされる問題の多くはトラストバウンダリ管理に失敗していることが原因です。分かりやすく応用範囲が広いコンセプトですから,新人開発者だけでなく経験者の方も「トラストバウンダリを管理する」という考え方に基づくセキュリティ管理を行ってはいかがでしょうか?

著者プロフィール

大垣靖男(おおがきやすお)

University of Denver卒。同校にてコンピュータサイエンスとビジネスを学ぶ。株式会社シーエーシーを経て,エレクトロニック・サービス・イニシアチブ有限会社を設立。
オープンソース製品は比較的古くから利用し,Linuxは0.9xのころから利用している。オープンソースシステム開発への参加はエレクトロニック・サービス・イニシアチブ設立後から。PHPプロジェクトでは,PostgreSQLモジュールのメンテナンスを担当している。

URLhttp://blog.ohgaki.net/

著書