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

第44回 セキュリティ対策が確実に実施されない2つの理由

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

セキュリティ対策は言語やアプリケーションを問わず非常に重要です。しかし,取るべきセキュリティ対策が確実に実施されないケースが広く見受けられます。

最近の例では次のような物があります。

上のURLの脆弱性も対策が簡単なものが多いですが,対策が簡単なSQLインジェクションの脆弱性も数多く見つかっています。

なぜ簡単な対策で防げる脆弱性でもセキュリティ対策が確実に実施されないのか?それには理由があります。

その理由とはこの2つではないでしょうか。

  • セキュリティ対策とコーディングのベストプラクティスは相反することを理解していない
  • セキュリティ対策の基本中の基本を理解していない

セキュリティ対策とコーディングのベストプラクティスは相反する

セキュリティ対策とコーディングのベストプラクティスは真っ向から対立している部分があります。

  • 多重のセキュリティ対策を実施 vs 重複処理を排除した効率よいコード記述

このセキュリティ対策とコーディングのベストプラクティスは対立する考え方で,どちらも「正しい」です。これが問題を大きくしている一因だと思われます。

セキュリティ対策の基本の1つとして「多重のセキュリティ対策」があります。これはセキュリティ対策の原則とも言われている重要な考え方です。セキュアなコードでは入力でバリデーションした値であっても出力する際には必ずエスケープを行います。しかし,これはコーディングのベストプラクティスである「重複処理を排除した効率よいコード記述」に反します。このため,既にチェック済み,これは数値だからエスケープ処理は不要,などとの誤った判断からエスケープ処理を行っていないコードで溢れています。

セキュリティ対策とコーディングのベストプラクティスは対立する考え方ですが,セキュリティ対策が必要な部分ではコーディングのベストプラクティスは忘れて,セキュリティ対策のベストプラクティスを取り入れるべきです。

よいコードとは次のようなコードです。

  • 必要なセキュリティ対策コードを書いた上で,重複などを排除した効率がよいコード

よいコードの必要条件は「必要なセキュリティ対策コードが含まれてること」です。この考え方を持たないプログラマが冒頭で紹介したような脆弱なコードを書いているのではないかと考えています。

セキュリティ対策の基本中の基本

セキュリティ対策の基本中の基本はどこでセキュリティ対策を行うかです。これが理解されていないケースも多くあります。プログラマから見たセキュリティ対策を行うべき箇所は多くありません。多くないどころかたったの3つです。

  • 入力(入力をバリデーション)
  • ロジック
  • 出力(出力をエスケープ,バリデーション)

この3つしかありません。この3つに分けて考えてセキュリティ対策を行えば,どのような対策をすべきであるか,初心者プログラマであっても間違える可能性はかなり低くなります。

入力

入力のチェックは必ず入力を受け入れた直後に,すべての入力パラメータに対して行います。これを怠るとセキュリティ上の問題の原因になります。入力のチェック(バリデーション)は必ず「すべての入力パラメータが正しいかチェックする」ようにします。入力チェックの基本もたった3つです。

  1. 期待している文字で構成されているか?
  2. 期待している範囲(長さ,値)のパラメータか?
  3. 期待している形式のパラメータか?

入力チェックのポイントは正しいものだけを受け入れることです。悪いもの(不正な文字など)を除去するブラックリスト的な考え方ではなく,正しいものだけを受け入れるホワイトリストの方針でチェックしなければなりません。別の言い方をすると「サニタイズはせず,バリデーションをする」ようにします。

広く利用されているアプリケーションやフレームワークだからといってベストプラクティスが採用されているわけではありません。例えば,Railsの入力のセキュリティ対策はセキュアであるとは言えません。Railsのバリデーションは「データベースにデータが保存される前」に行われます。データベースにデータを保存する必要がないようなアプリケーションの場合,入力のバリデーションをフレームワークとして行う仕組みになっていません。本来入力はデータベース利用の有無に関わらず入力を受け入れた直後に行うべきです。多くのフレームワークがRailsの影響を受け同様の仕様となっています。Railsが脆弱な仕様を採用したことは不幸なことだったと思います。

広く利用されているアプリケーションでも確実なセキュリティ対策が行われているわけではない,と理解しながら参考にしたり利用しなければなりません。

ロジック

ロジックの部分は認証(ログイン・ログアウト処理)や排他制御,フォームの正当性確認(CSRF対策など)⁠アクセス制御などです。これらのロジックはそれぞれベストプラクティスがあります。フレームワークで実装されている場合も多くあります。初心者プログラマであってもこれらがしっかりしたフレームワークを利用していれば不適切な処理をしてしまうことは少なくなるでしょう。

言語や関数の仕様を知ることもロジックの部分に入ります。言語や関数の仕様を知らずに正しいロジックが書けるはずがありません。もし自分が利用している正規表現関数の仕様を調べたことがない方がいらしたら,ぜひ一度調べてみてください。もしかすると思わぬ発見ができるかも知れません。少なくとも入力,出力,バリデーションに関係する関数の仕様は確実に把握するようにしなければなりません。

入力処理や出力処理もアプリケーション処理のロジックと言えます。入力と出力は多重のセキュリティを取り入れる部分なので独立した処理として作ります。入出力処理はセキュリティ対策の要であり9割のアプケーション脆弱性は入出力にある,と思ってよいでしょう。セキュアなアプリケーションを構築するためには,入出力とほかのロジックとは別に独立したセキュリティ対策を行う箇所であると考えたほうが安全なコードを書けるようになります。

著者プロフィール

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

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

URLhttp://blog.ohgaki.net/

著書

コメント

コメントの記入