セキュリティ要件を定義しよう（Webアプリ編）

高いセキュリティレベルが要求されるシステムの開発を依頼される場合があります。
その場合には、システムをセキュアに作るだけではなく、お客様に「何が」「どう」セキュアなのか、説明する必要があったり、場合によっては担保する必要があります。

この場合、何を元に説明したり、担保したりすればよいのでしょうか？

ここで使われるのが**セキュリティ要件定義書**です。

OWASP（後述）のセキュリティ要件定義書をベースに、セキュリティ要件定義書の作り方、入れるべき内容を学びましょう！

#要件定義書に書くべきこと

セキュリティ要件に書くべきことは、セキュリティ対策のためにベンダーが「**具体的に何をやるか**」です。
どんな攻撃が想定されていて、どんな防御方法を取るか、セキュリティ要件定義書を使ってそれを説明し、方針についての合意を獲得します。

ただし、「どんな攻撃が想定されていて」の部分は、最終的な要件定義書には書かれていません。それはベンダーの責任範囲には関係が無いからです。セキュリティ要件定義書は、要件定義書のひとつですから、ベンダーの責任範囲を明確化します。ココで書いてあること**さえ**実施されていれば、未知の手法で攻撃を受けたとしてもそれはベンダーの責任ではありません（お客様との合意ができているのであれば）。瑕疵ではないので、瑕疵担保による修正・賠償の責任が無いのです。

#攻撃のパターン

セキュリティ要件定義書を作るには、まずは想定される攻撃パターンを知る必要があります。

Webに対する攻撃パターンは、ここ5年ほど新しい手法も出ておらず、既知の攻撃パターンに対する知識が非常に重要です。攻撃手法の詳細については、調べてみてください！

1. **SQLインジェクション**
SQL文の組み立て方が悪いために、任意のSQL文を実行されてDBの情報を引っこ抜かれたりするパターンの攻撃です。

1. **OSコマンドインジェクション**
OSコマンドの呼び出し方が悪いために、任意のコマンドを実行されたりするパターンです。つい先日のbashの脆弱性によるShellshockも、このパターンの攻撃になります。

1. **意図しないファイルへのアクセス・ディレクトリトラバーサル**
ファイルのアクセス制限等が不適切なために、パスワードファイルを直接開くことが出来てしまったりするパターンです。

1. **セッションを利用した攻撃**
    1. **セッションハイジャック**
        * 他のユーザのセッションを乗っ取って、そのユーザになりすましたりするパターン。
    1. **セッションフィクセーション**
        * 他のユーザに、自分が指定したセッションを使わせて、そのユーザになりすましたりするパターン。
1. **クロスサイト・スクリプティング(XSS)**
Webサイト上にjavascriptを含むコードを投稿するなどし、そのページを開いた他のユーザにコードを実行させるパターンの攻撃。

1. **クロスサイト・リクエスト・フォージェリ(CSRF)**
悪意のあるタグの記述で、サイト上で特定の動作、例えば日記の投稿や物品の購入などをさせるパターン。[ぼくはまちちゃん事件](http://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%82%B9%E3%82%B5%E3%82%A4%E3%83%88%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%AA#.E3.80.8C.E3.81.BC.E3.81.8F.E3.81.AF.E3.81.BE.E3.81.A1.E3.81.A1.E3.82.83.E3.82.93.E3.80.8D_.E9.A8.92.E5.8B.95)が有名。

1. **HTTPヘッダインジェクション**
HTTPヘッダ（Set-CookieやLocationなど）の生成方法などが不適切なために、偽ページを表示させられたり、任意のコードを実行されたりするパターン。

1. **メールヘッダインジェクション**
フォームのメール送信先の設定方法が不適切なために、任意の宛先にメールを送られてしまったりするパターン。

1. **アクセス制御の欠落**
ログインした状態でしか表示してはいけないようなものが、未ログイン状態で出てしまうパターン。

#セキュリティ要件でカバーする！

##OWASP Japanのセキュリティ要件定義書
[OWASP Japan](https://www.owasp.org/index.php/Japan)

OWASPとは、Open Web Application Security Projectの略です。
その活動の一環として、標準的なセキュリティ要件定義書を作っています。

[セキュリティ要件定義書](https://www.owasp.org/images/8/88/Web_application_security_requirements.pdf)

この要件定義書は、下記の条件に従う限り、自由に利用することが出来ます。

* 表示：あなたは原著作者のクレジットを表示しなければなりません。
* 継承：もしあなたがこの作品を改変、変形または加工した場合、あなたはその結果生じた作品をこの作品と同一の許諾条件の下でのみ頒布することができます。

基本的には、**この要件定義書をベースに、案件に合わせた内容でセキュリティ要件を取り決める**のが最も簡単かつ漏れが出にくいやり方になると思います。早速、この要件定義書の中身を見て行きたいと思います。

##OWASPセキュリティ要件定義書の中身を見る

引用部分が要件定義書の中身です。
「※」付きの項目は、必須ではないものです。
要求されるセキュリティレベルに合わせて、対応しましょう。

### 1. 認証

> 1.1. 以下の箇所では、認証を実施すること 
> 
> * 認証済みユーザーのみに表示・実行を許可すべき画面や機能
> * 上記画面に含まれる画像やファイルなどの個別のコンテンツ（非公開にすべきデータは直接 URL で指定できる公開ディレクトリに配置しない）
> * 管理者用画面

**アクセス制御の欠落**に対する対応です。
「認証済みユーザーのみに表示・実行を許可すべき画面や機能」に該当する部分がどこなのか、は**要件定義や画面定義でしっかり明示**する必要があります。

> 1.2 以下の箇所では、認証済みの場合でも再認証を実施すること ※
> 
> * 個人情報や機密情報を表示するページに遷移する際
> * パスワード変更や決済処理などの重要な機能を実行する際

**クロスサイト・リクエスト・フォージェリ(CSRF)**に対する対応となります。
また、**セッションハイジャック**された際にも、攻撃者が重要な情報にアクセス出来なくなるため、高度なセキュリティを要求される場合には対応に入れましょう。

> 1.3 パスワードについて 
> 
> * パスワード文字列は大小英字と数字の両方を含み、最低 7 文字以上であること
> * 画面（hidden パラメーターなどのソースコード内も含む）にパスワード文字列を表示しないこと
> * パスワード文字列の入力フォームは input type="password"で指定すること
> * ユーザーが入力したパスワード文字列を次画面以降で表示しないこと
> * パスワード文字列は「パスワード文字列＋salt（ユーザー毎に異なるランダムな文字列）」をハッシュ化したものと salt のみを保存すること
> * ユーザー自身がパスワードを変更できる機能を用意すること ※

基本的に、認証形式としてはパスワードを使用することが多いかと思います。
そのうち、Web上でも生体認証とか使えるようになるといいですねー。
ソルトが「ユーザ毎に異なる」は、落としがちです。（固定文字列でやってしまうなど）

また、保存するハッシュについては**ストレッチング**を行うことが望ましいとされることがあります。ストレッチングとは、生成したハッシュに、ソルトとパスワードをさらに繋げてもう一度（また更に同様に繰り返し）ハッシュ関数に流す、というものです。が、ソルトを適切に使用していれば、また、適切なハッシュ関数を使っているのであれば、ストレッチングにハッシュ関数の耐性を上げる効果はありません。逆に、ソルトが無い時などは、少なくとも1回のストレッチングをしないと、ハッシュが漏れた場合に、**レインボーテーブル**などを使って攻撃を受ける可能性があります。

> 1.4 アカウントロック機能について 
> 
> * 認証時に無効なパスワードで 10 回試行があった場合、最小 30 分間はユーザーがロックアウトされた状態にすること ※
> * ロックアウトは自動解除を基本とし、手動での解除は管理者のみ実施可能とすること ※

システム的に、ログインの試行が簡単（1リクエストで完結するなど）な場合、システム的にあらゆるパスワードでのログイン試行を実施し、認証を突破する**総当たり攻撃**（ブルートフォースアタック）や**辞書攻撃**（辞書にある単語を総当り的に投げる）を受ける可能性があります。アカウントロックを行うことで、それを防ぐことが出来ます。

### 2. 認可（アクセス制御）

> 2.1 Webページや機能、データへのアクセスは認証情報・状態を元にして判別すること

正しく認証情報を参照していますか？ということですね。まあ、そりゃそうです。

### 3. セッション管理

> 3.1 セッションの破棄について 
> 
> * 認証済みのセッションが一定時間以上アイドル状態にあるときはセッションタイムアウトとし、サーバー側でセッションを破棄しログアウトすること（時間はサービスの内容に応じて調整すること）
> * ログアウト機能を用意し、ログアウト実行時にはサーバー側でセッションを破棄すること

セッションがいつまでも残った状態になっていると、**セッション・ハイジャック**の危険が高まります。過去にキャプチャしたパケットを使っての攻撃などが容易になるので、ターゲットを絞った攻撃が出来ます。

> 3.2 セッション ID について 
> 
> * Web アプリケーション開発ツールが提供するセッション管理機能を使用すること
> * 上記のセッション管理機能の使用が困難な場合、セッション ID は 80 ビット（使用する文字が 16 進数の場合 20 文字）以上の文字列を使用すること
> * 上記のセッション管理機能の使用が困難な場合、セッション ID はログインごとに乱数により生成すること
> * セッション ID をクライアントと受け渡しする際は Cookie にのみ格納すること ※
> * セッション ID の発行は認証成功後とすること ※
> * 認証済みユーザーの特定はセッション ID でのみ行うこと

セッションIDはランダムでないと推測が可能になってしまいます。**セッション・ハイジャック**や**セッション・フィクセーション**を防ぐためには、推測や漏洩、盗聴などが出来ないようにすることが基本の対策です。通常、アプリケーションサーバの標準機能を使うのがGoodです。まあ、たまにランダム性が十分でなかったとかで脆弱性情報が出たりするんですけどね。。。

セッションIDは基本的にはログイン成功時に振る、もしくは振り直す必要があります。出来れば、セッションIDのやりとりはHTTPSの時のみに絞り、CookieにもSecure属性をつけておくのがベスト・プラクティスです。そうでなければ、ログイン後に使えるセッションIDが平文で流れるタイミングを作ることになり、盗聴による漏洩の可能性が出てくるためです。

> 3.3 CSRF（クロスサイトリクエストフォージェリー）対策の実施について 
> 
> * CSRF対策を実施すべき箇所では再認証の実施すること
> * CSRF対策を実施すべき箇所で再認証の実施を提供することが困難な場合にはアクセスを POSTに限定し秘密情報の埋め込みと確認を実施すること

基本的には、再認証さえ入れてあれば問題ないという形になります。これによって、1発で処理が終わるというのを避けられるので、いきなり投稿されたり、購入させられたり、といったことは回避出来ます。2項めの「秘密情報」とは、ワンタイムのランダムなキーです。正しいページ遷移の時だけ、処理がされるようにします。

原理としては、ワンタイムであることよりも、ユーザに固有であることが重要なので、セッションIDで代用することも出来るのですが、ここがワンタイムでなければ、セッションIDが推測可能だった場合などには結局CSRFに対して脆弱になります。また、**リプレイ攻撃**については防御が出来ません。（リプレイ攻撃に対する防御は、セキュリティ設計の中で一番面倒くさいもののひとつです。）

また、**RefererをチェックすればCSRFを防げる**と書いてあるページなどもあったりしますが、**防げません**。HTTPヘッダはいくらでも偽装できるので、信用してはいけません。

### 4. パラメーター

> 4.1 URL パラメーターにユーザーID やパスワードなどの秘密情報を格納しないこと

**Referer**などで**漏洩**する可能性があるので、厳にNGです。

> 4.2 パラメーターにパス名を含めないこと

パラメータに、参照するべきファイルのパスを含めたりしないように、ということです。通常しないと思います。ただ、「これはもしかするとファイル名に使われているかも」という部分があれば、攻撃を受ける可能性があります。そのパラメータに「../../../../../../../../../etc/passwd」などを指定して**ディレクトリ・トラバーサル**を受けるかもしれません。基本的には、パラメータをそのままファイルのパスに入れるということ自体がNGです。

> 4.3 アプリケーション要件に基づいて入力値の文字種や文字列長の検証を行うこと

サーバの脆弱性を突く（**バッファ・オーバーフロー**など）ために、非常に長い文字列を送りつけたり、ヌル文字を含む文字列を送りつけたりということが行われる事があります。事前に文字列のチェックを行い、**パラメータをそのまま内部の関数に送ったりしない**ということが必要です。

### 5. 文字列処理

> 5.1 クロスサイトスクリプティング（XSS）対策を講じること 
> 
> * すべての HTML 出力で特殊文字（< > " ' &）をエスケープすること（ ' のエスケープはオプション扱い）
> * ユーザーの入力など、外部から入力した URL を出力するときは「http://」や「https://」で始まるもののみを許可すること
> * <script>...</script>要素の内容やイベントハンドラ（onmouseover=””など）を動的に生成しないようにすること ※
> * スタイルシートを外部サイトから取り込めないようにすること

ユーザが入力した文字が、HTMLとして解釈されたり、JavaScriptとして解釈されたりすることを禁止する必要があります。

> 5.2 HTML タグの属性値を「"」で囲うこと

value="ユーザ入力文字列"という属性を付与することはよくあることだと思いますが、ここに「"」がない場合、ユーザ入力として「**dummy id=hoge**」という文字列が入ってきた時に、5.1のエスケープ処理をしていても「**value=dummy id=hoge**」になってしまい、ページの動作を変えることが出来ます。

> 5.3 SQL コマンドを組み立てる際にプレースホルダを使用すること

SQLインジェクションの標準的対策です。

> 5.4 HTTP レスポンスヘッダーの Content-Type に文字コードを指定すること

**意図的に壊れたエンコーディングのデータをHTMLに埋め込ませる**ことで、HTMLの解釈を誤らせ、Scriptタグの埋め込みを可能にしたりする攻撃があります。

### 6. HTTPS

> 6.1 https://で指定すべき画面を特定すること
> 
> * 入力フォームのある画面
> * 入力フォームデータの送信先
> * 重要情報や問い合わせ先など改ざんや偽のページが表示されては困る画面

理屈で言えば、「入力フォームデータの送信先」がHTTPSでありさえすれば、入力フォームのある画面自体がHTTPであっても、入力フォームの内容は守られます。ただ、送信先がHTTPSであるかどうかをユーザに確認させる（ソースを見てもらう？）のは無理がありますし、今見ているページがそもそも改ざんされていないか？も問題です（フォームの送信先が外部の悪意あるサイトになっている可能性があるため）。

同じ理由で、FRAMEやIFRAMEで中身にフォームがある場合、外枠のアクセスもHTTPSにするべきです。

> 6.2 サーバー証明書はアクセス時に警告が出ないものを使用すること

ユーザが検証可能な安全性が担保されていなければなりません。

> 6.3 SSL3.0 以前のものを無効にすること

ここは、オリジナルの文言では「SSL2.0 を無効にすること」となっております。が、POODLE脆弱性の問題が発覚していますし、SSL3.0も使用を禁止するべきです。

### 7. Cookie

> 7.1 HTTPS 利用時の Cookie には secure 属性を付けること 

基本的に、情報の漏洩を防ぐため、HTTPSの時に扱っている情報は**すべて**、HTTPでは扱われないようにしなければなりません。Cookieにはその仕組みがあるので、必ず使うようにしてください。逆に、Cookie以外では、HTTPで秘密の情報が取り扱われていないかはコード的に確認するしかありません！

> 7.2 Cookie の値には機密情報を格納しないこと ※

漏洩の危険の対策です。他の項目が適切に実施されていれば、機密情報が入れられていても安全といえば安全です。ただ、7.1などが完全に守られていなかったりすると、平文で機密情報が流れる危険性があるため、オプションではありますが可能な限りそうすべきです。

> 7.3 Cookie に HttpOnly 属性を付けること ※

これは、secure属性に比べるとマイナーな属性です。この属性が付いているCookieは**JavaScriptからアクセスすることが出来なく**なります。基本的には、JavaScriptからCookieを操作するのでなければ、すべてのCookieに付いていても問題ないはずです。

### 8. 画面設計

> 8.1 利用者の Web ブラウザ環境を操作せずデフォルト状態で動作すること
> 
> * ユーザーに指示してセキュリティ設定の変更をさせない
> * アドレスバーやステータスバーを隠すなど画面の変更を行わない

高木先生怒りの実例（古いですが）

* 「署名済みActiveXコントロールのダウンロード」を有効にしてください
* 「スクリプトを実行しても安全だとマークされていないActiveXコントロールの初期化とスクリプトの実行」を有効にしてください
* 「未署名のActiveXコントロールのダウンロード」を有効にしてください
* 「スクリプトによる貼り付け処理の許可」を有効にしてください
* 「無効なサイト証明書について警告する」をオフにしてください
* 「このゾーンのサイトにはすべてサーバーの確認(https:)を必要とする」のチェックを 外してください かつ 信頼済みサイトゾーンのセキュリティレベルは「低」に
* 「混在したコンテンツを表示する」を有効にしてください
* 「SSL 2.0を使用する」を有効にしてください
* 「サードパーティのCookie」を受け入れるにしてください
* 「ActiveXコントロールに対して自動的にダイアログを表示」を有効にしてください
* 「ファイルのダウンロード時に自動的にダイアログを表示」を有効にしてください
* 「ダウンロードしたプログラムの署名を確認する」をオフにしてください
* 「ポップアップブロックの使用」を無効にしてください
* 「スクリプトを実行しても安全だとマークされていないActiveXコントロールの初期化とスクリプトの実行」をダイアログにしてください
* 「未署名のActiveXコントロールのダウンロード」をダイアログにしてください

> 8.2 フレーム、IFRAME を使用しないこと ※

ユーザの検証可能性を下げるので、基本的には避けたほうが良い実装です。

### 9. その他

> 9.1 エラーメッセージでユーザーID や登録メールアドレスなどの存在証明をしないこと

「パスワードが間違っています」
→「おっ、このIDあるやんけ」

「IDもしくはパスワードが間違っています」なら、情報を与えません。

> 9.2 プログラム上で OS コマンドやアプリケーションなどのコマンド、シェル、eval()などによるコマンドの実行を呼び出して使用しないこと

**OSコマンドインジェクション**を回避する方法は、コマンド呼び出しを使わないことです。ただ、Shellshockの時のように、環境の脆弱性によってOSコマンドインジェクションが可能になることもあり、100%防げる訳ではありません。

> 9.3 ハッシュ関数、暗号アルゴリズムは『電子政府における調達のために参照すべき暗号のリスト（[CRYPTREC暗号リスト](http://www.cryptrec.go.jp/list.html)）』に記載のものを使用すること

リンクを見てもらえれば、「使える」方式が一覧になっています。
気をつけなくてはいけないのですが、**SHA-1**は既に「**運用監視暗号リスト**」に入っており、**非推奨**扱いです。
SHA-256など、より安全性の高いものを使うことが推奨されています。

> 9.4 鍵や秘密情報などに使用する乱数的性質を持つ値を必要とする場合には、暗号学的な強度を持った疑似乱数生成系を使用すること

**[暗号論的擬似乱数生成器(CSPRNG)](http://ja.wikipedia.org/wiki/%E6%9A%97%E5%8F%B7%E8%AB%96%E7%9A%84%E6%93%AC%E4%BC%BC%E4%B9%B1%E6%95%B0%E7%94%9F%E6%88%90%E5%99%A8)**により生成された乱数列を使う、ということです。**CSPRNG**とは、次のような性質を持っているものを指します。

* 乱数列の最初の k ビットを与えられたとき、k+1 番目のビットの値を多項式時間で2分の1をこえる確率で予測するアルゴリズムが存在しない
* その状態の一部または全部が明らかになっても（あるいは正しく推測されても）、生成される乱数列を別の場所で再現できない。さらに、実行中にエントロピーの入力がある場合、その入力を知っていてもCSPRNGの将来の状態を予測できない。

（エントロピーの入力とは、「何らかの入力」という理解でOKです。エントロピーとは情報量です。）
Wikipediaから引きますと、
> 標準規格化されたCSPRNGとして、以下のものがある。
> FIPS 186-2
> NIST SP 800-90: Hash_DRBG, HMAC_DRBG, CTR_DRBG, Dual_EC_DRBG
> ANSI X9.17-1985 Appendix C
> ANSI X9.31-1998 Appendix A.2.4
> ANSI X9.62-1998 Annex A.4, obsoleted by ANSI X9.62-2005, Annex D (HMAC_DRBG)

この辺りの実装を入れ込んだCSPRNGが、SecureRandomなどの名前で既にライブラリに入っていることが多いです。

> 9.5 データファイルを公開ディレクトリに格納しない

昔、**アクセスログが公開ディレクトリに置かれていた**という事故がありましてね。。。

> 9.6 パラメーターに任意の URL を指定することで自動的に画面を遷移できるオープンリダイレクタを設けないこと

自動リダイレクト先をパラメータで指定するようなページがあると、**悪意あるページに自動的にジャンプする**ようなリンクを作ることが出来てしまいます。

> 9.7 メール送信処理には専用のライブラリを使用すること

**メールヘッダインジェクション**への対策です。
ただし、ライブラリなら無条件に信用していい、という訳ではないのは当然です。
実績があるものであっても、脆弱性が後から発見されるのはよくあることですので、セキュリティ情報には常にアンテナを張る必要があります。

> 9.8 HTTP ヘッダーフィールドの生成には専用のライブラリを使用すること

**HTTPヘッダインジェクション**への対策です。
基本的には、環境に標準のHTTPヘッダ設定方法があると思います。それを使用するのが基本です。

> 9.9 基盤ソフトウェアはアプリケーションの稼働年限以上のものを選定すること

運用期間中に保守期間が終了した場合、脆弱性が修正されない状態になる可能性があります。そのような場合には、保守期間内の別の基盤に移行する必要があります。

### 10. 提出物

> 10.1 提出物として下記を用意すること 
> 
> * サイトマップ
> * 画面遷移図
> * ディレクトリツリー

これにより、セキュリティを保護するべき範囲を明確にすることが出来ます。HTTPSになっているべきページが、すべてそうなっているか、顧客が自分で確認することが出来るようになり、ベンダーもエンドもお互いにハッピーになります。要件定義という意味で言えば、「**保護すると言ったのはこの範囲ですよ**」と明確にすることで、揉めた時の線引きの1つになります。

# まとめ
要件定義書を利用することで、開発側が「何をやるのか」が明確になります。逆に言えば、ここに書いていないことはやらない訳なので、「何をやらないのか」も明確になります。

セキュリティ要件は、「こういう攻撃を100%防ぎます」という形には**出来ません**。Shellshockでも分かるように、既知の攻撃パターンであっても、未知の脆弱性を突いて実行されることがあるからです。**100%防げない**ことは、お客様に説明して**納得をしてもらう**必要があります。

それはつまり、日々発見される未知の脆弱性については、一つ一つ対応していかなければセキュリティレベルが保たれないということでもあります。**脆弱性が発見された時の対応ポリシー**についても、是非文書化しましょう！　これはOWASPのセキュリティ要件定義書ではカバー**されていません！**

是非、初期の要件定義で、**セキュリティ要件についても文書化**しましょう！

#参考資料

安全なウェブサイトの作り方
https://www.ipa.go.jp/security/vuln/websecurity.html