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

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

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

出力

「出力のエスケープ」は脆弱性を作らないという観点からは最も重要な対策ですが,見落とされていることが多い部分です。これにはセキュリティ対策を考慮していないコーディングのベストプラクティス以外に,誤ったセキュリティ対策が完全である,との思い込みも含まれているからだと考えています。出力する場合は「出力先のシステムが誤作動しないように正しい出力を行う」ようにします。出力処理を行う場合の基本的なセキュリティ対策もたった3つです。

  1. 出力先のシステムが誤作動しないようにすべてのパラメータをエスケープして出力する
  2. 出力先のシステム用にヘルパー関数などがある場合,ヘルパー関数の仕様を理解して利用する
  3. 1と2の対策が不可能な場合は出力時にバリデーション処理を行う

1で重要なのは「多重のセキュリティ対策」の原則です。入力処理と出力処理でセキュリティ対策が重複していても「必ず」エスケープして出力しなければなりません。

2は非常に誤解が多い部分です。プリペアードクエリさえ利用していれば安全,Viewのヘルパーさえ利用していれば安全,などと間違った思い込みから脆弱なアプリケーションを作ってしまう可能性を排除できていない開発者が多くいます。データベース管理用のアプリケーションでなくてもユーザ入力を使ったテーブルやフィールドの操作が必要となる場合もあります。例えば,アプリケーションのインストーラで必要となることは多いです。この場合,プリペアードクエリであっても識別子(テーブル,フィールド名)はパラメータになります。基本1の通りエスケープするか1)3のバリデーションを行わなければなりません。

Viewヘルパーを利用すれば安全,と考えるのは危険です。Railsアプリケーションのソースコード検査も仕事でしていますが,Railsで開発している方でもViewヘルパーがすべてのパラメータをエスケープ処理してくれないことを知りません。これはRailsだけの問題でなくすべてのWebアプリケーションフレームワーク共通の問題だと思ってください。

プリペアードクエリもViewヘルパーも,あくまで出力の基本1のエスケープ処理を補助してくれる機能に過ぎません。プリペアードクエリは元々 SQL実行を効率化する仕組みであり,完全なセキュリティ対策を行う仕組みとして設計されていません。クエリヘルパーだ,くらいに思っているほうがよいでしょう。

最後にパラメータであってもエスケープもできず,安全なヘルパーも使えない場合は3で記述したようにバリデーションするしかありません。ここに書いた通りにすべての入力をバリデーションしていればそのまま出力しても安全なはずですが,出力時にもバリデーションを必ず行うのが「多重のセキュリティの原則」です。

「出力先のシステムが誤作動しないように正しい出力を行う」と心掛けることは重要です。例えば,ここで解説した3の出力セキュリティ対策には正しいHTTPレスポンスを送る場合に必要な情報がありません。しかし,⁠出力先のシステムが誤作動しないように正しい出力を行う」を心掛けていればHTTPレスポンスで文字エンコーディン指定も含めた正しいコンテントタイプを設定しなければならない,という決まりごとに気付くことができます。

「出力先のシステムが誤作動しないように正しい出力」とは何か?把握した上で出力処理を行えば安全な出力を行うコードを書けるようになります。

誤解が無いように書いておきます。この記事はセキュリティ対策を主題に書いています。エスケープ機能を使い,ヘルパー機能を使うな,という意図で書いているわけではありません。セキュリティ対策の基本と一般的によいとされるコードの書き方では重要視すべき順序が異なるだけです。出力では「出力先のシステムが誤作動しないように出力する」のが安全な出力の基本であり,エスケープ処理がその柱です。

Viewヘルパーなどの機能は出力先のシステムが誤作動しないように補助する機能で不完全なことも多くあります。ヘルパー機能がエスケープ処理なしに正しく出力できるか?プログラマは理解してから使う必要があります。安全な出力を行えるかチェックするためにも,プログラマはまずエスケープ処理を正しく理解していなければなりません。

まとめ

特にプログラミングをはじめたばかりの方は,効率がよいコードを書くことに注力し過ぎているのではないでしょうか? セキュリティと効率は相反する部分があると理解し,効率化のためにセキュリティ対策を怠るのは悪いプラクティスである,と理解すればよりよいコードが書けるようになると考えています。

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

少なくともこの2つを理解して開発すれば,初心者であっても基本的な部分で脆弱性があるコードを書いてしまう可能性が低くなるでしょう。この2つを理解した上で,個々のセキュリティ対策を理解していけば自然に安全なアプリケーションが作れるようになると思います。

最後に「何が正しいのか?」常に考えるようにしてください。一見,正しいように見えても実は間違っている場合もあります。例えば,SQL文を作成する場合にリテラル(パラメータ)を文字列としてエスケープすると浮動小数点型のデータが正しく処理されないデータベースがあります。これは,SQLインジェクション対策の基本の1つである「すべてのリテラルを文字列として処理」することが間違っているのでしょうか? それとも「文字列として渡された浮動小数点リテラルを正しく処理できない」処理系が間違っているのでしょうか?

データベースはデータを安全かつ確実に保存するためのアプリケーションです。浮動小数点データがSQL文のトークンとして渡された場合でも文字リテラルとして渡された場合でも「データを安全かつ確実に保存すべきデータベース」には正しく処理できることが求められます。本来はSQLパーサでパースしても,文字リテラルパーサでパースしても,同じ結果となって当然です。つまり,浮動小数点文字リテラルが正しく処理できない処理系が間違っている,と考えるほうが筋が通っています。

このようなシステムに対応するための知識は実務的なノウハウとしては必要かつ重要ですが,正しいセキュリティ対策の指針を変更したりするようなことではありません。あるエスケープ関数にバグがあるからエスケープ関数は利用せず独自にエスケープする,と同類の,必要な知識だけれども広く適用すべきではないバッドノウハウと言えます。

今回はセキュリティ対策の基本中の基本だけれども,何故か確実に実施されていない理由と基本的な考え方を紹介しました。

著者プロフィール

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

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

URLhttp://blog.ohgaki.net/

著書