• このページを del.icio.us へ追加
  • このエントリーを含むはてなブックマーク hateb

1. セキュリティガイドライン概要

セキュリティガイドラインとは

セキュリティガイドラインとは、Webアプリケーション開発におけるセキュリティ維持のために定められた、ロジック作成時のガイドラインです。このセキュリティガイドラインに従って開発を行うことで、担当者のプログラミング(または設計)レベルに依存することなく、一定水準以上のセキュリティを維持することを目的としています。

更新日 2007年12月19日
更新日 2008年3月5日
更新日 2008年3月10日 はてなブックマークのコメントを参考にいくつか修正
更新日 2008年10月29日 はてなブックマークのコメントを参考にSQLインジェクションの項目を修正

不正アクセス禁止法に抵触しないと推察される行為の例(参考)

情報セキュリティ早期警戒パートナーシップガイドライン(PDF)」によると、ユーザーによる下記の行為は不正アクセス禁止法に抵触しないと推察されています。

P.21
脆弱性の発見に最も関係が深い不正アクセス禁止法に対しては慎重な扱いが求められます。といっても脆弱性を発見する際に、必ずしも不正アクセス禁止法に抵触するとは限りません。以下に、不正アクセス禁止法に抵触しないと推察される行為の例を挙げます。
  1. ウェブアプリケーションの利用権者が、正規の手順でログインするなどして通常のアクセスをした際に、ブラウザとサーバとの通信の内容を観察したところ、それだけで脆弱性の存在を推定できた場合。
  2. ウェブページのデータ入力欄にHTML のタグを含む文字列を入力したところ、入力した文字列がそのまま表示された。この段階ではアクセス制御機能の制限を回避するに至らなかったが、悪意ある者に別の文字列を入力されれば、このサイトにセキュリティ上の問題が引き起こされかねないと予想できた場合。
  3. アクセス制御による制限を免れる目的ではなく、通常の自由なページ閲覧を目的として、日付やページ番号等を表すと推察されるURL中の数字列を、別の数字に差し替えてアクセスしてみたところ、社会通念上、本来は利用できてはならないはずと推定される結果が、偶発的に起きてしまった場合。(ただし、積極的に多数の数字列を変えて試す行為等は、制限を免れる目的とみなされる可能性があります。)

セキュリティ対策における7つの誤った思い込み

上記を踏まえ、開発の現場でプログラマが「セキュリティ対策である」と思い込んでしまう例をあげますので、注意してください。

パラメータ(引数)が書き換えられないという思い込み
特定のプログラム(URL)に引き渡される引数はプログラマの意図とはまったく関係なく、ありとあらゆる入力があると考えなくてはなりません。例えラジオボタンやプルダウンでの選択形式でユーザーに入力を促したり、maxlengthなどで入力文字数に制限を付けた場合でも、ユーザーは自由にパラメータを変更してアクセスすることができます。メソッドがGETであればURLに表示されている部分を変更するだけといういとも簡単な操作で可能になります。つまり、どんな入力に対してもサーバサイドのプログラムで必ず入力チェックをかけなければなりません。
POSTデータが書き換えられないという思い込み
GETデータはURLのアドレス欄を書き換えるだけで自由にパラメータを変更できますが、それをPOSTにすることではまったく対策になっていません。POSTデータもユーザーは簡単に(GETよりは少し難しいかもしれないが)データを作ることができます。例えば、入力ページのHTMLソースを一度ローカルPCに保存して、action先を絶対URLに書き換えてhiddenの値なども自由に書き換えることでも簡単に実現できます。少し高度な方法では、コマンドラインからのアクセスやPerlのLWPのアクセスなどもあります。
Referer による制限が万全であるという思い込み
上記のPOSTデータの改ざん手順を踏まえて、Refferer(アクセス元) でチェックを行う方法が考えられますが、残念ながらこれも意味がありません。例えばアクセス元を確認画面だけに制限することでローカルで編集されたPOSTデータをはじくという方法がよさそうですが、ブラウザによってはRefferer を自由に変更できるものもあります。また、ブラウザからRefferer データを送るかどうかは本来ユーザーの任意ですので、それをユーザーに強要するというところにも問題があります。
Cookieのデータが書き換えられないという思い込み
今までの手順を考えた後、「それではCookieにデータをセットして引き渡せば?」と考えるかもしれませんが、Cookieもユーザーは自由に改変できます。そもそもCookieはユーザーのPC上にデータが保存されるものですので、当然といえば当然です。Cookieファイルを編集するだけで簡単にデータを作ることができます。また、有効期限を「ブラウザを閉じるまで」と設定したCookieはファイルが作成されずメモリ上に生成されるのですが、これもブラウザによっては改変することができてしまいます。つまり、ユーザーが変更できないデータというのは、セッション変数に関連付けられたサーバ側で保持しているデータだけです。
User-Agent による制限が万全であるという思い込み
アクセス元の端末が携帯以外をはじきたい場合などにUser-Agent で判別する方法をとる場合が多いと思いますが、携帯以外をはじくのがクリティカルな要件の場合はこれでは完璧とはいえません。User-AgentもReffererと同様にユーザー側で変更することができます。最近のブラウザではUser-Agent変更機能が実装されているものも多くなってきております。携帯かどうかを本当に識別する場合は、キャリアのゲートウェイを通ってきたかどうか、アクセス元のIPアドレスで制御するしか方法はありません。
確認画面でチェックをすれば完了画面でのチェックは不要であるとの思い込み
確認画面に遷移する際に入力情報のチェックをして、完了画面でチェックを実装しないというのも問題です。今まで見てきたように、ユーザーは入力情報を自由に変えることができます。つまり、確認画面から完了画面に遷移する際に自由に情報を変更できるのです。結局、完了画面でも必ず確認画面と同様のチェックをしなければなりません。
通信中のデータは簡単には閲覧されないという思い込み
通信中のデータはSSLで暗号化されない限り簡単に閲覧できます。パケットキャプチャソフトを使えば、通信中のデータのやり取りは簡単に閲覧できます。特に、メール、FTP、HTTPでの通信には最新の注意を払う必要があります。せっかくサイトにSSLをかけていても、個人情報をメールで飛ばしたり、FTPでダウンロードしていてはまったく対策の意味がありません。必ず暗号化された環境でダウンロードまたは閲覧を行うようにしなければなりません。

2. XSS(クロスサイトスクリプティング)対策

XSS脆弱性とは

XSS脆弱性をついた攻撃というのは手順が複雑です。詳細はIPAのサイトのクロスサイトスクリプティングの項を確認してください。その攻撃手順の途中で、標的となったサイトで任意のJavaScriptを実行させられてしまうという部分が問題の本質です。これはスクリプト混入問題と同一視してしまうかもしれませんが、正確にはXSS脆弱性はスクリプト混入問題の一部でしかありません。

XSS脆弱性対策(1) - エスケープ処理

基本的な対策方法はエスケープ処理です。エスケープ処理とはスクリプトとして意味を持つ文字を無効化することです。

  • & → &
  • < → &lt;
  • > → &gt;
  • " → &quot;
  • ' → &#39;

PHPの場合はhtmlspecialchars関数でエスケープ処理しても良いです。

エスケープ処理のタイミングは入力データのチェック時ではなく、HTMLの生成時(出力時)に行うべきです。こうしておけば、将来的にデータベースへの書き込み手段として、メールによる投稿などの新しい方法が導入された場合でも、なんら手を加えることなく、いろんな入力源から入り込んでくるデータを漏れなくエスケープ処理できることになります。

XSS脆弱性対策(2) - URL属性部分の対応

Aタグのhref属性やIMGタグのsrc属性はURLを指定するタグ属性です。URL属性部分に入力データを埋め込む場合,単純な置換処理では対応できません。

例えば下記のような場合:
<a href="$input">テスト</a>

下記のコードが$input に入ってきた場合は上記のエスケープ処理をしても実行されてしまいます。
javascript:alert('hello');
vbscript:MsgBox('hello');
about:<script>alert('hello');</script>

<a href="javascript:alert('hello');">テスト</a>

こういったケースの場合、許可するスキームを(https, http, mailto)などを定義し、それと一致するかどうかでチェックをする。しかし最も良い対策は、URL属性にあたるところを、ユーザーからの入力に依存するような作りにしないことです。

XSS脆弱性対策(3) - その他の注意すべき点

下記のイベントハンドラ属性に注意しなければなりません。

  • onmouseover
  • onchange
  • onload
  • onfocus

など。

例えば下記のような場合:
<span onmouseover="$input">テスト</span>

下記のコードが$input に入ってきた場合は上記のエスケープ処理をしても実行されてしまいます。
alert('hello');

<span onmouseover="alert('hello');">テスト</span>

こういう場合は、埋め込みたいスクリプト関数名のリストを予め用意しておき、入力データに対応するスクリプト関数名を埋め込む形にしてください。入力データをそのままイベントハンドラ属性部分に埋め込んではいけません。その他にもこれと同様に気をつけるなければならないところがありますので、下記に列挙します。

■script タグで囲まれている部分
<SCRIPT src="external.js"></SCRIPT>

■style 属性
<br style=left:expression(eval('document.location="http://www.ipa.go.jp/";'))>
<style type="text/javascript">document.location="http://www.ipa.go.jp/";</style>

■外部スタイルシートへのリンク
<link rel="stylesheet" href="javascript:alert('hello');">

これらのJavaScriptが埋め込まれている部分は、動的に出力しないようにしてください。

3. SQLインジェクション対策

SQL インジェクション脆弱性とは

悪意のユーザーが、開発者の想定していない、または任意のSQLを実行されてしまう脆弱性です。

具体例

次のようなソースコードでSQLを実装しているとしましょう。
$SQL="SELECT * FROM user WHERE userid='$input_userid' AND password='$input_password'";

この場合、ユーザーから下記のようなデータを入力されると、
$input_userid に dummy
$input_password に 'or'A'='A
こんな↓SQLが実行されることになります。
SELECT * FROM user WHERE userid='dummy' AND password=''or'A'='A'
(※SQLはORよりANDの方が先に評価されます。)

他にも下記のようなデータが入力されると、
$input_userid に dummy
$input_password に ';DELETE from user WHERE 'A'='A
ユーザーデータは全て消去されてしまいます。
SELECT * FROM user WHERE userid='dummy' AND password='';
DELETE from user WHERE 'A'='A'

SQL インジェクション脆弱性対策(1) - エスケープ処理

基本的な対策方法は、XSS対策と同じくエスケープ処理です。しかし、XSS対策と異なるところがいくつかあります。まず、エスケープ処理が必要な文字は下記の通りです。

  • "
  • '
  • \

そのほかにもSQLとして意味を持つ文字はいくつかありますが、ここでは省略します。無効化の方法もXSSの場合とは異なり、これらの文字の直前に「\」をつけることで対応します。「\」でエスケープできるのは一部のRDBMSのみでした。一般には、「'」を「''」や「"」を「""」とするのが普通とのことでした。さらに、一部の「\」がエスケープ文字として使用されるDBでのみ「\\」としなくてはいけません。PHPの場合はaddslashes() 関数で対応することも可能です(※文字コードがSHIFT-JISの場合注意が必要です)addslashesによるエスケープは「\」をつけるだけですので、やはり同様にRDBMSに依存してしまいます。正しいエスケープ処理は、DBMS固有のエスケープ関数(mysql_escape_string()など)を使うようにしましょう。どちらにせよSHIFT_JIS環境では正しくエスケープすることは難しいようです。

エスケープ方法の参考

また、エスケープ処理のタイミングもXSS対策ではではデータ出力時であったのに対して、SQLインジェクション対策では当然データの入力時SQLを実行する直前になります。(入力時にエスケープ処理をしても、その後SQLを実行するまでの間に書き換わってしまうと意味が無いため)

SQL インジェクション脆弱性対策(2) - 入力チェック

当然のことですが入力チェックを厳密にしましょう。また、入力チェックを行いやすいようにインターフェースを変更するのも手段のひとつとなります。

フォームの例
例えば電話番号の入力ボックスの場合、数字かどうかというチェックをかけることができれば、悪意のあるコードはまず実行されません。そのため、上記のように1つのフォームではなく複数のフォームに分ける方法も考えられます。

4. OSコマンドインジェクション

OSコマンドインジェクション脆弱性とは

Webアプリケーションを通じて任意のOSコマンドを実行されてしまう脆弱性です。PHPでもPerlでもOSコマンドを実行できる関数がありますので、これらの使用に注意する必要があります。

PHP

  • exec()
  • system()
  • shell_exec()
  • passthru()
  • popen()
  • `command`

Perl

  • exec()
  • system()
  • `command`
  • open() ※要注意:「|」で渡すことで実行できます。

OSコマンドインジェクション脆弱性対策(1) - エスケープ処理

基本的な脆弱性対策はXSSやSQLインジェクションの対策と同様に入力値のチェックとエスケープ処理です。しかし、OSコマンドインジェクションはOSの仕様によって危険文字が異なるため、チェック文字の洗い漏れが起こりやすいので、別の対策を採るほうが良いです。

OSコマンドインジェクション脆弱性対策(2) - 限定

上記で説明した関数へ引数を渡すときは、変数をそのまま渡すのではなくある程度制限された形で渡すようにしてください。例えば実行すべきOSコマンドのリストを配列としてソースコードの中に持ち、引数によってそれを選択して実行するようにしてください。

5. セッションハイジャック対策

セッションハイジャックとは

Webアプリケーションは、HTTP(またはHTTPS)というセッションステートレスなプロトコルの仕様上、開発者がセッションを維持する仕組みを作らなければなりません。そのために、一般的なWebアプリケーションではページからページへセッションIDを引き渡すことでセッションを維持する方法が採られています。このセッションIDを盗まれることがセッションハイジャックです。セッションを盗まれると、そのユーザーに成りすましていろいろな操作が行えるようになってしまいます。

セッションIDの生成

セッションIDは、機能の面だけを考えるとユーザーを特定できるキーとなる文字列であればなんでも良いのですが、セキュリティのことを考慮すると予測が十分に困難なランダムな文字列にする必要があります。

悪い例(1) - ユーザーID

tobe
okamoto
kubota

ログインの際にせっかくユーザーIDとパスワードで認証をしても、その後のセッション管理にユーザーIDを使用してしまっては意味がありません。簡単に類推できるユーザーIDを自由に使われてしまいます。

悪い例(2) - 番号のみ

105963
105965
105966
105968
105969
105971

このサイトではセッションIDに連番を使っているのではないか、という推測ができてしまいます。

悪い例(3) - 適当な暗号化

MTM2ODcyNjIxMzY5Cg
MzI0ODYwMTAxMjI1Cg
NDQ4ODU0NDQxMTU2Cg
NzU2ODQ0MzIxMDI0Cg
OTQwODM5ODYwOTYxCg
MzIzODk1NjA4NDEK

これは(2)の数値にBase64Encodeなどを使った特定の操作を加えて生成させたものです。これは安全そうに(見破られないように)見えます。しかし、これでは見破られないかもしれないが見破られるかもしれないという不安な状態です。セッションIDというのは万が一にも見破られてはならないのです。

良い例 - 乱数とハッシュによる生成

<?php
list($usec, $sec) = explode(' ', microtime());
mt_srand((float) $sec + ((float) $usec * 100000));
print md5(mt_rand()) ;
?>

これが乱数とハッシュを使ってセッションIDを生成するプログラム例です。
1f10ba97635b888fe4f6a43753ff8a5b
85226f534db05f6ac5a0a72aaafe94bc
384a8e93a876caae6e3e4bdbc1163992
0d1c51b8cbf13a44ab7881c819b2cda2
2e5544ac6d6508cf51f4446036ce9df2

こうすることで万が一にも見破られることのないセッションIDを生成することができます。
ただし、IDを生成するための種になる値は、ユーザー特有の情報だけを利用するのは避けてください。その情報が変わらなければ何度ハッシュを計算しても同じハッシュ値しか生成されないからです。

また、類推できない方法で生成したIDでも、桁数が短いと総当り攻撃(ブルートフォースアタック)で解読されてしまいます。数字6桁(100万通り)くらいであれば数時間以内に当てられてしまいます。

セッションIDの渡し方

SSL で暗号化されていない通信では、パケット盗聴されてしまうとセッションIDを盗まれてしまうので、セッション管理が必要なサイトではSSL通信を前提として考えます。セッションIDの渡し方は下記の3通りあります。

  • URL(GETアクションのパラメタ)
  • hiddenなINPUT(POSTアクションのパラメタ)
  • cookie

この中でGETというのは、アドレスバーに見えてしまっている部分ですので一番不安な渡し方です。アドレスバーに見えていても、SSL通信であれば経路上は暗号化されているので安全であると考えるかもしれません。確かに通信系路上は安全なのですが、他の問題があります。表示中のページのURLはReferer機能によってリンク先に送出されてしまうのです。例えば、GETでセッション管理を行っているようなコミュニティがあった場合、悪意のある会員ユーザーが「このサイト面白いよ」などと、自分のサーバにあるページへ遷移を促し、アクセスログからRefereを見てセッションIDを盗むことができてしまいます。GETで渡す情報はページ番号や商品番号など、見えてもかまわない情報に限定することにしてください。

セッションは基本的にはcookie場合によっては(CSRF対策を参照)hiddenなINPUTとして渡すようにしてください。また、cookieは必ずsecure属性をつけて発行してください。

セッションの受け渡しとSSLに関する補足

実際問題は、セッションの受け渡しを全てSSL環境下でやるというのは、コストの面でも現実的ではありません。hatena やmixi でもログインの部分だけはSSL(非SSLも可能)ですが、サイト内の画面遷移はSSLではありません。SSLはCPU負荷が高く、大規模なサイトであればインフラに与えるインパクトが大きいためこのようになっているのです。セキュリティの観点から考えるとこれは良い考え方とは言えないのですが、ビジネスを成立させるためには仕方がないことだといえます。セッションを非SSLで受け渡す場合は、最低限個人情報にアクセスできるところ(パスワードの変更画面など)では、再度パスワードの入力を求めたり、セッションの有効期限を短めに取ったりと、他の対応も必要になります。

6. CSRF(クロスサイトリクエストフォージェリ)対策

CSRF脆弱性とは

正しいセッションを経由したユーザーに「投稿」や「削除」、「購入」、「退会」などのコマンドを実行するURLへ誘導し、意図しないコマンドを実行させる攻撃。URLへの誘導は単にリンクをクリックさせるだけのものから、bodyのonload に仕込む方法まで多岐にわたります。設定(mixiでのはまちちゃん騒動が有名な事例)。セッションの追跡をcookieだけで行っている場合、もしくはBasic認証だけで行っている場合に起こってしまいます。次に、セッション管理を行っている場合のCSRF対策を説明します。

CSRF脆弱性対策(1) - hidden対応

「登録」や「削除」などのDB上のデータを書き換える最後の操作をPOSTアクションとし、hiddenなinputにセッションIDが入っていないと実行しないようにしてください。これが最も自然でかつ的確な対策となります。

CSRF脆弱性対策(2) - パスワードの再入力

きわどい操作の直前で再度パスワードを入力させる処理をいれる方法があります。ユーザービリティは少し下がるものの、対策としては問題ありません。通常は(1)の方法を採るべきですが、既存システムの修正などで緊急対応が必要な場合にはこの方法を採用することも検討してください。

CSRF脆弱性対策(3) - Refererチェック

Refererが正しいリンク元かどうかをチェックする方法もあります。これは対策としては意味がないと解説されているところもありますが、それは正しくありません(詳細は後述します)。この方法でもCSRF対策は可能です。しかし、Refererを送出しないブラウザ(Refererを送出するかしないかはユーザーの自由)ではサービスそのものが利用できなくなってしまうという弊害が生じます。

CSRFに関するよくある間違い

GETを使わずにPOSTを使え
JavaScriptなどで自動POSTすることが可能であるため、対策にはなりません。
実行の前に確認画面を挟め
確認画面は関係なく、完了画面に直接誘導させられては対策になりません。
Refererは偽装できるので対策にならない
セッションハイジャックやなりすましアクセス防止のためであれば、Refererのチェックでは対策になりませんが、攻撃者が被害者の送信するRefererを書き換えることはできないため、CSRFに限って言えばReferer偽装の問題は関係ありません。ただし、Refererを送出しないユーザーのアクセスを制限してしまうことにはなりますので、あまり推奨はできません。
ワンタイムトークンを使わなくてはならない
もともとあるセッション追跡用のID(通常の遷移ではcookieにセットしているセッションID)を利用すれば問題ありません。

7. ディレクトリトラバーサル対策

ディレクトリトラバーサル脆弱性とは

「../」などの相対パスの利用を許可してしまい、本来アクセスを許可していないディレクトリにアクセスされてしまう脆弱性。

8. エラー出力ガイド

エラー出力に関する考え方

画面に出るエラー出力はユーザーにわかりやすいものにする必要がありますが、必要以上の情報を与えることで、かえってセキュリティ的な穴を作ることにもなるということを意識してください。メールアドレスとパスワードでログインを行う仕組みで、ログインに失敗したときのエラーメッセージの出力例は下記のようになります。

悪い例

(1)「ご入力いただきましたメールアドレスは登録されておりません。」
(2)「パスワードに誤りがございます。」

これでは、メールアドレスの存在チェックに悪用されてしまいますし、なによりもメールアドレス⇒パスワードと順番に破られてしまう可能性もあります。

良い例

「パスワードまたはメールアドレスが間違っております。」

この例は比較的わかりやすい例で、注意することも多いと思いますが、パスワードリマインダーなどにおけるケースにも気をつけてください。パスワードリマインダーで、メールアドレスを入力すると登録されているメールアドレス宛にパスワード(または一時仮パスワード)を発行する仕組みの場合の例です。

悪い例

「ご入力いただきましたメールアドレスは登録されておりません。」

良い例

「ご入力いただきましたメールアドレス宛にパスワードをお知らせいたしました。もし、メールが届かない場合には、入力されたメールアドレスが間違っているかまだ登録されていない可能性があります。」

と言うメッセージを表示し、メールアドレスが登録されていない場合には内部的に無視します。

この例も非常に重要です。この悪い例は現在でもいろいろなWebアプリケーションで見受けられます。これを利用すると、あるメールアドレスを持つ人ががこのサービスを利用しているかどうかのチェックを行えてしまったりします。これはクライアントにおいても認識が低い場合が多いので、問題点をしっかりと説明してあげる必要があります。

9. Phishing(フィッシング)詐欺対策

フィッシング詐欺に関しては本来Webアプリケーションのレベルで対策できるものではないのですが、Webアプリケーションにおける対策で、少しでもフィッシング詐欺をされづらくすることができます。

  • アドレスバーを隠さない
  • 信頼しやすいドメイン名を採用する
  • XSS脆弱性を排除する

URL中のパラメタ部を弄られたくないため、セキュリティ対策のひとつとしてアドレスバーを隠すという対策を依頼されるケースがありますが、これにはまったく意味がありません。ユーザーはアドレスバーが隠されていてもURL中のパラメタを自由に閲覧することが出来ますし、それを変更することができます。また、同様の理由で右クリックの無効化も意義がありません。

ログイン時のリダイレクト設定に注意

ログインさせるWebアプリケーションの場合、ログイン後にログインしたページに戻るように、リダイレクト設定されている場合も多くあります。
「backurl」というようなパラメータで、ログイン時のページURLを渡すことでリダイレクトするのですが、「backurl」に相当する値を任意のURLに設定できてしまうのであれば、フィッシングの危険性があります。

フィッシング詐欺師が、「backurl」に相当する値に罠を仕込んだURLをセットしたリンクをどこかに設置したとします。
訪問者がそのリンクから正しいログインをしたとしても、ログイン後は罠が仕込まれたページに飛ぶことになります。
そこにログインエラーを装った偽のログインページが、本物のログインページと同じデザインだったら、訪問者はログインエラーを疑うことなく、再びログインをするためにIDとパスワードを打ち込んでしまうでしょう。

これを防ぐには、「backurl」に相当する値に外部サイトが指定されていた場合は、エラー画面に飛ばすというリダイレクト設定をしておくことです。

10. 参考サイト一覧