Rails セキュリティー

#前提
本日学んだセキュリティーについて書いていきます。

#本題

#リダイレクトとファイル
セキュリティ上の脆弱性として検討したいのは、Webアプリケーションにおける「リダイレクトとファイル」。

##リダイレクト
Webアプリケーションにおけるリダイレクトは、過小評価されがちなクラッキングツール。
攻撃者はこれを使ってユーザーを危険なWebサイトに送り込んだり、Webサイト自体に罠を仕掛けたりすることもできる。

リダイレクト用のURL (の一部) を渡すことをユーザーに許すと、潜在的な脆弱性となる。
最もあからさまな攻撃方法としては、ユーザーを本物そっくりの偽Webサイトにリダイレクトすることが考えられる。
これは俗に「フィッシング(phishing)」や「釣り」などと呼ばれる攻撃手法。
具体的には、無害を装ったリンクを含むメールをユーザーに送りつけ、XSSを使ってそのリンクをWebアプリケーションに注入するか、リンクを外部サイトに配置する。
このリンクの冒頭部分はそのWebアプリケーションのURLなので、一見無害に見える。


##ファイルアップロード
ファイルがアップロードされたときに重要なファイルが上書きされることのないようにする。
また、メディアファイルの処理は非同期で行なう。

多くのWebアプリケーションでは、ユーザーがファイルをアップロードできるようになっている。
ユーザーが選択/入力できるファイル名 (またはその一部) は必ずフィルタする。
攻撃者が危険なファイル名をわざと使ってサーバーのファイルを上書きしようとする可能性があるため。
ファイルが /var/www/uploads ディレクトリにアップロードされ、そのときにファイル名が「../../../etc/passwd」と入力されていると、重要なファイルが上書きされてしまう可能性がある。
言うまでもなく、Rubyインタプリタにそれだけの実行権限が与えられていなければ、そのような上書きは実行できない。
Webサーバー、データベースサーバーなどのプログラムは、比較的低い権限を持つUnixユーザーとして実行されているのが普通。

さらにもう一つ注意。
ユーザーが入力したファイル名をフィルタするときに、ファイル名から危険な部分を取り除くアプローチを使わないこと。
Webアプリケーションがファイル名から「../」という文字を取り除くことができるとしても、今度は攻撃者が「....//」のようなその裏をかくパターンを使えば、やはり「../」という相対パスが通ってしまい、きりがない。
最も良いのは「ホワイトリスト」によるアプローチ。
これはファイル名が有効であるかどうか (指定された文字だけが使われているかどうか) をチェックするもの。
これは「ブラックリスト」アプローチと逆の手法であり、利用が許されてない文字を除去する。
ファイル名が無効の場合は、拒否するか、無効な文字を置き換えますが、取り除くわけではない。

##ファイルアップロードで実行可能なコードを送り込む
アップロードされたファイルに含まれるソースコードが特定のディレクトリに置かれると、ソースコードが実行可能になってしまう可能性がある。
Railsの/publicディレクトリがApacheのホームディレクトリになっている場合は、ここにアップロードファイルを置いてはいけない。

広く使われているApache WebサーバーにはDocumentRootというオプションがある。
これはWebサイトのホームディレクトリであり、このディレクトリツリーに置かれているものはすべてWebサーバーによって取り扱われる。
そこに置かれているファイルの名前に特定の拡張子が与えられていると、それに対してリクエストが送信された時に実行されてしまうことがある。
実行される可能性のある拡張子は、たとえばPHPやCGIなど。
攻撃者が「file.cgi」というファイルをアップロードし、その中に危険なコードが仕込まれているとする。
このファイルを誰かがダウンロードすると、このコードが実行される。

ApacheのDocumentRootがRailsの/publicディレクトリを指している場合、アップロードファイルをここに置かない。
少なくとも1階層上に保存する必要がある。


##ファイルのダウンロード
ユーザーが任意のファイルをダウンロードできる状態を作らないこと。

ファイルアップロード時にファイル名のフィルタが必要になるのと同様、ファイルのダウンロード時にもファイル名をフィルタする必要がある。
以下のsend_file()メソッドは、サーバーからクライアントにファイルを送信します。フィルタ処理されていないファイル名を使うと、ユーザーが任意のファイルをダウンロードできるようになってしまう。

```
send_file('/var/www/uploads/' + params[:filename])
```
「../../../etc/passwd」のようなファイル名を渡せば、サーバーのログイン情報をダウンロードできてしまう。
これに対するシンプルな対応策は、リクエストされたファイル名が、想定されているディレクトリの下にあるかどうかをチェックすること。

その他に、ファイル名をデータベースに保存しておき、データベースのidをサーバーのディスク上に置く実際のファイル名の代りに使う方法も併用できる。
この方法も、アップロードファイルが実行される可能性を回避する方法として優れている。
attachment_fuプラグインでも同様の手法が採用されている。


#イントラネットAdminのセキュリティ
イントラネットおよび管理画面インターフェイスは、強い権限が許されているため、何かと攻撃の目標にされがち。
イントラネットおよび管理画面インターフェイスには、他よりも手厚いセキュリティ対策が必要ですが、現実には逆にむしろこれらの方がセキュリティ対策が薄いということがしばしばある。

イントラネットや管理アプリケーションにとって最も脅威なのはXSSとCSRF。

XSS: 悪意のあるユーザーがイントラネットの外から入力したデータがWebアプリケーションで再表示されると、WebアプリケーションがXSS攻撃に対して脆弱になる。
ユーザー名、コメント、スパムレポート、注文フォームの住所のような情報すらXSS攻撃に使われることがある。

管理画面やイントラネットで1箇所でもサニタイズ漏れがあれば、アプリケーション全体が脆弱になる。
想定される攻撃としては、管理者のcookieの盗み出し、管理者パスワードを盗み出すためのiframe注入、管理者権限奪取のためにブラウザのセキュリティホールを経由して邪悪なソフトウェアをインストールする、などが考えられる。

CSRF: クロスサイトリクエストフォージェリ (Cross-Site Request Forgery) はクロスサイトリファレンスフォージェリ (XSRF: Cross-Site Reference Forgery) とも呼ばれ、非常に強力な攻撃手法。
この攻撃を受けると、管理者やイントラネットユーザーができることをすべて行えるようになってしまう。

RailsのURLはかなり構造が素直であるため、オープンソースの管理画面を使っていると構造を容易に推測できてしまう。
攻撃者は、ありそうなIDとパスワードの組み合わせを総当りで試す危険なImageタグを送り込むだけで、数千件ものまぐれ当たりを獲得することもある。

###その他予防策
管理画面は、多くの場合次のような作りになっている。www.example.com/admin のようなURLに置かれ、Userモデルのadminフラグがセットされている場合に限り、ここにアクセスできる。
ユーザー入力が管理画面で再表示されると、管理者の権限でどんなデータでも削除/追加/編集できてしまう。

常に最悪の事態を想定することは極めて重要。
「誰かが自分のcookieやユーザー情報を盗み出すことができたらどうなるか」。
管理画面にロール (role)を導入することで、攻撃者が行える操作の範囲を狭めることができる。
1人の管理者に全権を与えるのではなく、権限を複数管理者で分散する方法や、管理画面用に特別なログイン情報を別途設置するという方法もある。
一般ユーザーが登録されているUserモデルに管理者も登録し、管理者フラグで分類していると攻撃されやすいことから、これを避けるため。
極めて重要な操作では別途特殊なパスワードを要求する方法もある。

管理者は、必ずしも世界中どこからでもそのWebアプリケーションにアクセスできる必要性はないはず。
送信元IPアドレスを一定の範囲に制限するという方法。request.remote_ipメソッドを使えばユーザーのIPアドレスをチェックできる。
この方法は攻撃に対する直接の防御にはならないが、検問としては非常に有効。
ただし、プロキシを用いて送信元IPアドレスを偽る方法がある。

管理画面を特別なサブドメインに置き ( admin.application.com など)、さらに管理アプリケーションを独立させてユーザー管理を独自に行えるようにする。
このような構成にすることで、通常の www.application.com ドメインからの管理者cookieを盗み出すことは不可能。
ブラウザには同一生成元ポリシーがあるので www.application.com に注入されたXSSスクリプトからはadmin.application.comのcookieは読み出せず、逆についても同様に読み出し不可となる。

#ユーザー管理
認証 (authentication) と認可 (authorization) はほぼすべてのWebアプリケーションにおいて不可欠。
認証システムは自前で作るよりも、広く使われているプラグイン (訳注: 現在ならgem) を使うべき。
ただし、常に最新の状態にアップデートするようにする。

Railsでは多数の認証用プラグインを利用できる。
人気の高いdeviseやauthlogicなどの優れたプラグインは、パスワードを平文ではなく常に暗号化した状態で保存する。
Rails 3.1では、同様の機能を持つビルトインのhas_secure_passwordメソッドを使える。

新規ユーザーは必ずメール経由でアクティベーションコードを受け取り、メール内のリンク先でアカウントを有効にするようになっている。
アカウントが有効になると、データベース上のアクティベーションコードのカラムはNULLに設定される。
以下のようなURLをリクエストするユーザーは、データベースで見つかる最初に有効になったユーザーとしてWebサイトにログインできてしまう可能性がある。そしてそれがたまたま管理者である可能性もありえる。

```
http://localhost:3006/user/activate
http://localhost:3006/user/activate?id=
```
一部のサーバーでは、params[:id]で参照されるパラメータidがnilになってしまっていることがあるため、上のURLが通用してしまう可能性がある。アクティベーション操作中にこのことが敵に突き止められるまでの流れは以下のとおり。

```
User.find_by_activation_code(params[:id])
```
パラメータがnilの場合、以下のSQLが生成される。

```
SELECT * FROM users WHERE (users.activation_code IS NULL) LIMIT 1
```
この結果、データベースに実在する最初のユーザーが検索で見つかり、結果が返されてログインされてしまう。

##アカウントに対する総当たり攻撃
アカウントに対する総当たり攻撃 (Brute-force attack) とは、ログイン情報に対して試行錯誤を繰り返す攻撃。
エラーメッセージを具体的でない、より一般的なものにすることで回避可能 だが、CAPTCHA (相手がコンピュータでないことを確認するためのテスト) への情報入力の義務付けも必要。

Webアプリケーション用のユーザー名リスト (名簿) は、パスワードへの総当たり攻撃に悪用される可能性がある。
パスワードがユーザー名と同じなど、単純極まりないパスワードを使っている人が驚くほど多いため、総当たり攻撃にこうした名簿が利用されやすい。
辞書に載っている言葉に数字を混ぜた程度の弱いパスワードが使われていることもよくある。
従って、名簿と辞書を使って総当り攻撃を行なう自動化プログラムがあれば、ものの数分でパスワードを見破られている。

このような総当たり攻撃を少しでもかわすため、多くのWebアプリケーションではわざと具体的な情報を出さずに「ユーザー名またはパスワードが違います」という一般的なエラーメッセージを表示するようにしている。
ユーザー名とパスワードどちらが違っているのかという情報を表示しないことで、総当たり攻撃による推測を少しでも遅らせる。
「入力されたユーザー名は登録されていません」などという絶好の手がかりとなるメッセージを表示したら最後、攻撃者はすぐさまユーザー名リストを大量にかき集めて自動で巨大名簿を作成する。

しかし、Webアプリケーションのデザイナーがおろそかにしがちなのは、いわゆる「パスワードを忘れた場合」ページ。
こうしたページではよく「入力されたユーザー名またはメールアドレスは登録されていません」という情報が表示される。
こうした情報を表示してしまうと、攻撃者がアカウントへの総当り攻撃に使う有効なユーザー名一覧を作成するのに利用されてしまう。

これを少しでも緩和するには、「パスワードを忘れた場合」ページでも一般的なエラーメッセージを表示するようにする。
さらに特定のIPアドレスからのログインが一定回数以上失敗した場合には、CAPTCHAの入力をユーザーに義務付けるようにする。
もちろん、この程度では自動化された総当たり攻撃プログラムからの攻撃から完全に逃れることはできない。
こうしたプログラムは送信元IPアドレスを頻繁に変更するぐらいのことはやってのけるから。
しかしこの対策は攻撃に対するある程度の防御になることも確か。


##アカウントのハイジャック
多くのWebアプリケーションでは、ユーザーアカウントを簡単にハイジャックできてしまう。

###パスワード
攻撃者が、盗み出されたユーザーセッションcookieを手に入れ、それによってWebアプリケーションが標的ユーザーとの間で共用可能になった状態を考えてみる場合。
パスワードが簡単に変更できる画面設計(古いパスワードの入力が不要)であれば、攻撃者は数クリックするだけでアカウントをハイジャックできてしまう。
あるいは、パスワード変更画面がCSRF攻撃に対して脆弱な作りになっている場合、攻撃者は標的ユーザーを別のWebページに誘い込み、CSRFを実行するように仕込まれたimgタグを踏ませて、標的ユーザーのWebパスワードを変更する。
対応策としては、パスワード変更フォームがCSRF攻撃に対して脆弱にならないようにすること。
同時に、ユーザーにパスワードを変更させる場合は、古いパスワードを必ず入力させること。

###メール
しかし攻撃者は、登録されているメールアドレスを変更することでアカウントを乗っ取ろうとする可能性もある。
攻撃者は、メールアドレス変更に成功すると「パスワードを忘れた場合」ページに移動し、攻撃者の新しいメールアドレスに変更通知メールを送信する。
システムによってはこのメールに新しいパスワードが記載されていることもある。
対応策は、メールアドレスを変更する場合にもパスワード入力を必須にすること。

###その他
Webアプリケーションの構成によっては、ユーザーアカウントをハイジャックする方法が他にも潜んでいる可能性がある。
多くの場合、CSRFとXSSが原因となる。
GMailのCSRF脆弱性で紹介されている例をとりあげる。
同記事の概念実証によると、この攻撃を受けた場合、標的ユーザーは攻撃者が支配するWebサイトに誘い込まれる。
そのサイトのImgタグには仕掛けがあり、GMailのフィルタ設定を変更するHTTP GETリクエストがそこから送信される。
この標的ユーザーがGMailにログインしていた場合、フィルタ設定が攻撃者によって変更され、この場合はすべてのメールが攻撃者に転送されるようになる。
この状態は、アカウント全体がハイジャックされたのと同じぐらいに有害。
対応策は、アプリケーションのロジックを見なおしてXSSやCSRF脆弱性を完全に排除すること。

##CAPTCHA
CAPTCHAとは、コンピュータによる自動応答でないことを確認するためのチャレンジ-レスポンス式テスト。
コメント入力欄などで、歪んだ画像に表示されている文字を入力させることで、入力者が自動スパムボットでないことを確認する場合によく使われる。
ネガティブCAPTCHAという手法を使えば、入力者に自分が人間であることを証明させるかわりに、ボットを罠にはめて正体を暴くことができる。

CAPTCHAのAPIとしてはreCAPTCHAが有名。
これは古書から引用した単語を歪んだ画像として表示する。
初期のCAPTCHAでは背景を歪めたり文字を曲げたりしていましたが、後者は突破されたため、現在では文字の上に曲線も書き加えて強化している。
なお、reCAPTCHAは古書のデジタル化にも使える。
ReCAPTCHAはRailsのプラグインにもなっており、APIとして同じ名前が使われている。

このAPIからは公開鍵と秘密鍵の2つの鍵を受け取る。
これらはRailsの環境に置く必要がある。
それにより、ビューでrecaptcha_tagsメソッドを、コントローラではverify_recaptchaメソッドをそれぞれ利用できる。
検証に失敗するとverify_recaptchaからfalseが返される。

CAPTCHAの問題は、ユーザーエクスペリエンスを多少損ねること。
さらに、弱視など視力に問題のあるユーザーはCAPTCHAの歪んだ画像をうまく読めないこともある。
なおポジティブCAPTCHAは、ボットによるあらゆるフォーム自動送信を防ぐ優れた方法のひとつ。

ほとんどのボットは、単にWebページをクロールしてフォームを見つけてはスパム文を入力するだけのお粗末なもの。
ネガティブCAPTCHAではこれを逆手に取り、フォームに「ハニーポット」フィールドを置いておく。
これは、CSSやJavaScriptを用いて人間には表示されないように設定されたダミーのフィールド。

ネガティブCAPTCHAが効果を発揮するのはWebをクロールする自動ボットからの保護のみであり、重要なサイトに狙いを定めるボットを防ぐのには不向き。
しかしネガティブCAPTCHAとポジティブCAPTCHAをうまく組み合わせればパフォーマンスを改善できることがある。
たとえば「ハニーポット」フィールドに何か入力された（=ボットが検出された）場合はポジティブCAPTCHAの検証は不要になり、レスポンス処理の前にGoogle ReCapchaにHTTPSリクエストを送信せずに済む。

JavaScriptやCSSを用いてハニーポットフィールドを人間から隠す方法。

ハニーポットフィールドを画面の外に追いやってユーザーから見えないようにする
フィールドを目に見えないくらい小さくしたり、背景と同じ色にしたりする
ハニーポットフィールドをあえて隠さず、「このフィールドには何も入力しないでください」と表示する
最もシンプルなネガティブCAPTCHAは、「ハニーポット」フィールドを1つ使う。
このフィールドはサーバー側でチェックする。
フィールドに何か書き込まれていれば、入力者はボットであると判定できる。
後はフォームの内容を無視するなり、通常通りメッセージを表示する(データベースには保存しない)などすればよい。
通常のメッセージをもっともらしく表示しておけば、ボットは書き込み失敗に気が付かないまま満足して次の獲物を探す。

Ned Batchelderのブログ記事には、さらに洗練されたネガティブCAPTCHA手法がいくつか紹介されている。

現在のUTCタイムスタンプを含めたフィールドをフォームに含めておき、サーバー側でこのフィールドをチェックする。
フィールドの時刻が遠い過去や未来の時刻であれば、そのフォームは無効。
フィールド名をランダムに変更します
送信ボタンを含むあらゆる型の数だけハニーポットフィールドを複数用意。
この方法で防御できるのは自動ボットだけであり、狙いを定めて特別に仕立てられたボットは防げない。
つまり、ネガティブキャプチャはログインフォームの保護には必ずしも向いているとは限らない。

##ログ出力
パスワードをRailsのログに出力しないこと。

デフォルトでは、RailsのログにはWebアプリケーションへのリクエストがすべて出力される。
しかしログファイルにはログイン情報、クレジットカード番号などの情報が含まれていることがあるため、重大なセキュリティ問題の原因になることがある。
Webアプリケーションのセキュリティコンセプトを設計するときには、攻撃者がWebサーバーへのフルアクセスに成功してしまった場合のことも必ず考慮に含めておく必要がある。
パスワードや機密情報をログファイルに平文のまま出力してしまうと、データベース上でこれらの情報を暗号化する意味がなくなってしまう。
Railsアプリケーションの設定ファイル config.filter_parameters に特定のリクエストパラメータをログ出力時にフィルタする設定を追加できる。
フィルタされたパラメータはログ内で[FILTERED]という文字に置き換えられる。

```
config.filter_parameters << :password
```
指定したパラメータは正規表現の「部分マッチ」によって除外される。
Railsはデフォルトで:passwordを適切なイニシャライザ（initializers/filter_parameter_logging.rb）に追加し、アプリケーションの典型的なpasswordパラメータやpassword_confirmationパラメータに配慮する。

##正規表現
Rubyの正規表現で落とし穴になりやすいのは、より安全な\Aや\zがあることを知らずに危険な^や$を使ってしまうこと。

Rubyの正規表現では、文字列の冒頭や末尾にマッチさせる方法が他の言語と若干異なる。
このため、多くのRuby本やRails本でもこの点について間違った記載がある。
たとえば、URL形式になっているかどうかをざっくりと検証するために、以下のような単純な正規表現を使ったとする。

```
/^https?:\/\/[^\n]+$/i
```
これは一部の言語では正常に動作する。
しかし、Rubyでは^や$は、入力全体の冒頭と末尾ではなく、「 行の」冒頭と末尾にマッチしてしまう。
従って、この場合以下のような毒入りURLはフィルタを通過してしまう。

```
javascript:exploit_code();/*
http://hi.com
*/
```
上のURLがフィルタに引っかからないのは、入力の2行目にマッチしてしまうため。
従って、1行目と3行目にどんな文字列があってもフィルタを通過してしまう。
フィルタをすり抜けてしまったURLが、今度はビューの以下の箇所で表示されたとする。

```
link_to "Homepage", @user.homepage
```
表示されるリンクは一見無害に見えますが、クリックすると、攻撃者が送り込んだ邪悪なJavaScript関数を初めとするJavaScriptコードが実行されてしまう。

これらの正規表現に含まれる危険な^や$は、安全な\Aや\zに置き換える必要がある。

```
/\Ahttps?:\/\/[^\n]+\z/i
```
^や$をうっかり使ってしまうミスが頻発したため、Railsのフォーマットバリデータ(validates_format_of) では、正規表現の冒頭の^や末尾の$に対して例外を発生するようになった。
めったにないと思われるが、\Aや\zの代りに^や$をどうしても使いたい場合は、:multilineオプションをtrueに設定することもできる。

```
# この文字列のどの行にも"Meanwhile"という文字が含まれている必要がある
validates :content, format: { with: /^Meanwhile$/, multiline: true }
```
この機能は、フォーマットバリデータ利用時に起きがちなミスから保護するだけのものであり、それ以上のものではない点にご注意。
^や$はRubyでは 1つの行 に対してマッチし、文字列全体にはマッチしないということを開発者が十分理解しておくことが重要。

##権限昇格
パラメータが1つ変更されただけでも、ユーザーが不正な権限でアクセスできるようになってしまうことがある。
パラメータは、たとえどれほど難読化し、隠蔽したとしても、変更される可能性が常にあることを肝に銘じる。

改ざんされる可能性が高いパラメータといえばid。http://www.domain.com/project/1の1がid。
このidはコントローラのparamsを経由して取得できる。
コントローラ内では多くの場合、次のようなコードが使われている可能性がある。

```
@project = Project.find(params[:id])
```
このコードで問題がないWebアプリケーションもあるにはあるが、そのユーザーがすべてのビューを参照する権限を持っていない場合には問題となる。
このユーザーがURLのidを42に変更し、本来のidでは表示できないページを表示できてしまうため。
このようなことにならないよう、ユーザーのアクセス権も必ずクエリに含める。

```
@project = @current_user.projects.find(params[:id])
```
Webアプリケーションによっては、ユーザーが改ざん可能なパラメータが他にも潜んでいる可能性がある。
要するに、安全確認が終わっていないユーザー入力が安全である可能性はゼロであり、ユーザーから送信されるいかなるパラメータであっても、何らかの操作が加えられている可能性が常にあるということ。

難読化とJavaScriptによる検証のセキュリティだけでお茶を濁してはいけない。
ブラウザのWeb Developer Toolbarを使えば、フォームの隠しフィールドを見つけて変更することもできる。
JavaScriptを使ってユーザーの入力データを検証することはできても、攻撃者が想定外の値を与えて邪悪なリクエストを送信することは阻止しようがない。
Mozilla Firefox用のFirebugアドオンを使えば、すべてのリクエストをログに記録して、リクエストを繰り返し送信することも、リクエストを変更することもできてしまう。
さらに、JavaScriptによる検証はブラウザのJavaScriptをオフにするだけで簡単にバイパスできてしまう。
さらに、クライアントやインターネットのあらゆるリクエストやレスポンスを密かに傍受するプロキシがクライアント側に潜んでいる可能性すらある。



#インジェクション
インジェクション (注入) とは、Webアプリケーションに邪悪なコードやパラメータを導入して、そのときのセキュリティ権限で実行させること。
XSS (クロスサイトスクリプティング) やSQLインジェクションはインジェクションの顕著な例。

インジェクションによって注入されるコードやパラメータは、あるコンテキストではきわめて有害であっても、それ以外のほとんどのコンテキストでは無害。
その意味で、インジェクションは非常にトリッキーであると言える。
ここでいうコンテキストとは、スクリプティング、クエリ、プログラミング言語、シェル、RubyやRailsのメソッドなどがある。

##ホワイトリスト方式とブラックブラックリスト方式
通常、サニタイズや保護や検証では、ブラックリスト方式よりもホワイトリスト方式が望ましい方法。

ブラックリストに使われるのは、有害なメールアドレス、publicでないアクション、邪悪なHTMLタグなど。
ホワイトリストはこれと真逆で、有害ではないメールアドレス、publicなアクション、無害なHTMLタグなどがホワイトリストになる。
スパムフィルタなど、対象によってはホワイトリストを作成しようがないこともあるが、基本的にホワイトリスト方式を使う。

セキュリティに関連するbefore_actionでは、except: [...]ではなくonly: [...]を使う。
なぜなら将来コントローラにアクションを追加するときにセキュリティチェックを忘れずに済むため。
クロスサイトスクリプティング (XSS) 対策として<script>を削除するブラックリスト方式ではなく、たとえば<strong>だけを許可するホワイトリスト方式にする。
ユーザー入力データをブラックリスト方式で訂正してはいけない。
そのようなことをすると、たとえば"<sc<script>ript>".gsub("<script>", "")という攻撃にやられてしまう。
有害な入力は断固拒否する。
特定の項目だけを許可するホワイトリスト方式は、ブラックリストへの追加漏れのようなヒューマンエラーに強いのも望ましい点。

##SQLインジェクション
さまざまなよい手法が出現したおかげで、SQLインジェクションがRailsアプリケーションで問題になることはめったになくなった。
しかしSQLインジェクションはひとたび発生すれば壊滅的な打撃を受ける可能性があり、Webアプリケーションに対する一般的な攻撃方法でもあるため、この問題を十分に理解することが重要。

SQLインジェクションは、Webアプリケーションのパラメータを操作してデータベースクエリに影響を与えることを目的とした攻撃手法。
SQLインジェクションは、認証をバイパスする目的でよく使われる。
他にも、データを操作したり任意のデータを読み出したりする目的にも使われる。
クエリのユーザー入力データをそのまま使わずに改ざんする方法。

```
Project.where("name = '#{params[:name]}'")
```
上記は検索用のアクションなどで使われるものであり、ユーザーは検索したいプロジェクト名を入力。
ここで、悪意のあるユーザーが' OR 1 --という文字列を入力すると、以下のSQLクエリが生成される。

```
SELECT * FROM projects WHERE name = '' OR 1 --'
```
2つのダッシュ「--」が末尾に置かれると、以後に追加されるクエリがすべてコメントと見なされてしまい、実行されなくなる。
そのため、projectsテーブルからすべてのレコードが取り出される。
これらは通常のユーザーからは参照できないはずのもの。
これは、クエリですべての条件がtrueになっているために発生している。

###認証のバイパス
Webアプリケーションでは何らかの形でアクセス制御が行われるのが普通。
ユーザーがログイン情報を入力すると、Webアプリケーションはユーザーテーブルに登録されているレコードとマッチするかどうかを調べる。
既存のレコードとマッチする場合、アプリケーションはアクセスを許可する。
しかし、攻撃者がSQLインジェクションでこの認証をすり抜けてしまう可能性がある。
以下はRailsにおける典型的なデータベースクエリ。
ユーザーが入力したログイン情報パラメータとマッチするUserテーブル上の最初のレコードを返す。

```
User.find_by("login = '#{params[:name]}' AND password = '#{params[:password]}'")
```
ここで攻撃者が「' OR '1'='1」という文字列を名前フィールドに入力し、「' OR '2'>'1」をパスワードフィールドに入力すると以下のSQLクエリが生成される。

```
SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIMIT 1
```
マッチする最初のレコードがこのクエリによって取得され、ユーザーにアクセスが許可されてしまう。


###不正なデータ読み出し
UNION文は2つのSQLクエリをつなぎ、1つのセットとしてデータを返す。
攻撃者がUNIONでデータベースから任意のデータを読み出す可能性がある。

```
Project.where("name = '#{params[:name]}'")
```
ここで、UNION文を用いて以下の文字列を注入したとする。

```
') UNION SELECT id,login AS name,password AS description,1,1,1 FROM users --
```
これによって以下のSQLが生成される。

```
SELECT * FROM projects WHERE (name = '') UNION
  SELECT id,login AS name,password AS description,1,1,1 FROM users --'
```
このクエリで得られるのはプロジェクトのリストではなく(名前が空欄のプロジェクトはないため)、ユーザー名とパスワードのリスト。
データベース上のパスワードが暗号化されていればまだ最悪の事態は避けられる。
一方、攻撃者にとっての問題は、両方のクエリでカラムの数を同じにしなければならないことだけ。
この攻撃用文字列では、そのために2番目のクエリに「1」を連続して配置している。
これらの値は常に1になるため、1番目のクエリのカラム数と一致する。

同様に、2番目のクエリではカラム名をASでリネームしている。
これにより、ユーザーテーブルから取り出した値がWebアプリケーション上で表示される。

###対応策
Ruby on Railsには、特殊なSQL文字をフィルタが組み込まれており、「'」「"」「NULL」「改行」をエスケープする。
Model.find(id)やModel.find_by_なんちゃら(かんちゃら)といったクエリでは自動的にこの対応策が適用される。
ただし、SQLフラグメント、特に条件フラグメント (where("..."))、connection.execute()またはModel.find_by_sql()メソッドについては手動でエスケープする必要がある。

条件オプションに文字列を直接渡す代りに、以下のように配列を渡すことで、汚染された文字列をサニタイズすることもできる。

Model.where("login = ? AND password = ?", entered_user_name, entered_password).first
上に示したように、配列の最初の部分がSQLフラグメントになっており、その中に疑問符「?」が含まれている。サニタイズされた変数は、配列の後半に置かれており、フラグメント内の疑問符を置き換える。
ハッシュを渡して同じ結果を得ることもできる。

Model.where(login: entered_user_name, password: entered_password).first
モデルのインスタンスでは、配列またはハッシュのみを利用。
それ以外の場所ではsanitize_sql()を使うのもよい。
SQLで外部の文字列をサニタイズせずに使うと、セキュリティ上重大な結果がもたらされる可能性があることに普段から注意する習慣が必要。

##クロスサイトスクリプティング（XSS）
XSSは最も発生しやすいWebセキュリティ上の脆弱性であり、ひとたび発生すると壊滅的な影響が生じる可能性がある。
XSSを利用した悪意のある攻撃が行われると、クライアント側のコンピュータに実行可能なコードが注入されてしまう。
Railsには、このような攻撃をかわすためのヘルパーメソッドが用意されている。

###攻撃点
攻撃点 (entry point) とは、攻撃者が攻撃を向ける対象となる、脆弱なURLおよびパラメータのこと。

攻撃点として最も選ばれやすいのはメッセージ投稿、ユーザーコメント、ゲストブックだが、プロジェクトタイトル、ドキュメント名、検索結果ページなども同様に脆弱性を抱えていたことがあった。
ユーザーがデータを入力可能なあらゆる部分が攻撃点になる可能性がある。
ただし、攻撃者がデータを入力するのはWebサイト上の入力ボックスとは限らない。
URLに含まれているパラメータ、URLに直接含まれていないが利用可能な「隠れた」パラメータ、URLに含まれない内部パラメータ、どれも攻撃者がデータを入力する可能性がある。
攻撃者がすべてのトラフィックを傍受している可能性を常に考慮に入れる必要がある。
アプリケーションプロキシやクライアント側プロキシを悪用すれば、リクエストを簡単に改ざんできる。

XSS攻撃は次のように行われる。
攻撃者が何らかのコードをWebアプリケーションに注入し、後に標的ユーザーのWebページ上に表示される。
XSS事例の多くは警告ボックスを表示する程度のものですが、実際はもっと凶悪。
XSSを使うことで、cookieの盗み出し、セッションのハイジャック、標的ユーザーを偽のWebサイトに誘い込む、攻撃者の利益になるような広告を表示する、Webサイトの要素を書き換えてユーザー情報を盗み出す、あるいはWebブラウザのセキュリティ・ホールを経由して邪悪なソフトウェアをインストールすることもできる。

###HTML/JavaScriptインジェクション
XSS攻撃に利用されやすい言語は、言うまでもなくクライアント側で最も普及している言語であるJavaScriptであり、しばしばHTMLと組み合わせて攻撃に利用される。
攻撃を避けるにはユーザー入力をエスケープする必要がある。

XSSをチェックする最も簡単なテスト。

```
<script>alert('Hello');</script>
```
このJavaScriptコードを実行すると、警告ボックスが1つ表示されるだけ。
次の例では、見かけの動作はまったく同じだが、通常ではありえない場所にコードが置かれている。

```
<img src=javascript:alert('Hello')>
<table background="javascript:alert('Hello')">
```

###Cookie窃盗
今度は攻撃者がユーザーのcookieを盗み出す手法について書いていきます。
JavaScriptでは、document.cookieプロパティを利用してドキュメントのcookieを読み書きできる。
JavaScriptでは同一生成元ポリシーが強制的に適用される。
これは、あるドメインから送り込まれたスクリプトからは、別のドメインのcookieにアクセスできないようにするポリシー。
document.cookieプロパティには、生成元webサーバーのcookieが保存されている。
しかし、HTMLドキュメントに直接コードを埋め込むと(XSSによってこれが生じることがあります)、このプロパティを読み書きできてしまう。
このコードを自分のWebアプリケーションの適当な場所に手動で注入してみれば、そのページに含まれている自身のcookieが表示されることを確認できる。

```
<script>document.write(document.cookie);</script>
```
もちろん、攻撃者にしてみれば標的ユーザーが自分で自分のcookieを表示したところで何の意味もない。

この攻撃をある程度緩和するためにはhttpOnlyフラグをcookieに追加する。
これにより、document.cookieをJavaScriptで読み出せなくなる。
HTTP only cookieはIE v6.SP1、Firefox v2.0.0.5、Opera 9.5以降で利用できる。
Safariはまだこのフラグを検討中であり、このオプションは無視される。
ただしWebTVやMac版IE 5.5などの古いブラウザでは、ページ上での読み込みに失敗する。
なお、Ajaxを使うとcookieが表示可能になることにも注意。

###Webページの汚損
Webページを書き換える (汚損) ことで、偽の情報を表示したり、標的ユーザーを攻撃者の偽サイトに誘い込んでcookieやログイン情報などの重要データを盗み出すなどのさまざまな攻撃が可能になる。
最も多い攻撃は、iframeタグを悪用して外部のコードをWebページに含める方法。

```
<iframe name="StatPage" src="http://58.xx.xxx.xxx" width=5 height=5 style="display:none"></iframe>
```
このコードによって、外部にある任意のHTMLやJavaScriptが読み込まれ、Webサイトの一部として埋め込まれる。
上のiframeは、Mpack攻撃フレームワークを使ってイタリアにあるWebサイトへの攻撃で実際に用いられたもの。
MpackはWebブラウザのセキュリティホールを介して邪悪なソフトウェアをインストールしようとする。
そして攻撃の成功率は50%を誇っている。

さらに特殊な攻撃としては、Webサイト全体を上に重ねて表示したりログインフォームを表示したりするという手口がある。
これらは元のサイトと一見そっくりだが、入力されたユーザー名とパスワードを密かに攻撃者のサイトに送信する。
あるいは、CSSやJavaScriptを駆使してWebアプリケーション上の本物のリンクを隠して別のリンクを表示し、ユーザーを偽のサイトにリダイレクトするという手法もある。

リフレクションインジェクション (Reflected injection) 攻撃も同様の攻撃。
後で標的ユーザーに表示するペイロードを実際には保存せず、代わりにURLに長大な文字列として仕込んでおく手法。
特に検索フォームで検索文字列のエスケープに失敗する。

###対応策
悪意のある入力をフィルタすることがきわめて重要。Webアプリケーションの出力をエスケープすることも同様に重要。

特にXSSの場合、ブラックリスト方式ではなくホワイトリスト方式で入力をフィルタすることが絶対重要。
ホワイトリストフィルタでは特定の値のみが許可され、それ以外の値はすべて拒否される。
ブラックリスト方式を元にしている限り、必ず将来漏れが生じる。

ユーザー入力から「script」という文字を除去するのに使われているブラックリストがあるとする。
それなら攻撃者は次には「<scrscriptipt>」という文字を入力する。
この文字がフィルタされると「<script>」という文字がそっくり残ってしまう。
以前のRailsではstrip_tags()、strip_links()、sanitize()メソッドでブラックリスト的アプローチが使われていたため、当時は以下のような攻撃が可能。

```
strip_tags("some<<b>script>alert('hello')<</b>/script>")
```
フィルタから返される「some<script>alert('hello')</script>」という文字列の攻撃能力は失われていない。
だからこそ、ホワイトリストを用いるフィルタリングをおすすめする。
ホワイトリストによるフィルタは、Rails 2でアップデートされたsanitize()メソッドで使われている。

```
tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p)
s = sanitize(user_input, tags: tags, attributes: %w(href title))
```
この方法なら指定されたタグのみが許可されるため、あらゆる攻撃方法や邪悪なタグに対してフィルタが健全に機能する。

第2段階として、Webアプリケーションからの出力をもれなくエスケープすることが優れた対策。これは特に、ユーザー入力の段階でフィルタされなかった文字列がWeb画面に再表示されてしまうようなことがあった場合に有効。escapeHTML() (または別名のh()) メソッドを用いて、HTML入力文字「&」「"」「<」「>」を、無害なHTML表現形式(&amp;、&quot;、&lt;、&gt;) に置き換える。

###攻撃の難読化とエンコーディングインジェクション
従来のネットワークトラフィックは西欧文化圏のアルファベットがほとんどであったが、それ以外の言語を伝えるためにUnicodeなどの新しいエンコード方式が使われるようになってきた。
しかしこれはWebアプリケーションにとっては新たな脅威となるかもしれない。
異なるコードでエンコードされた中に、ブラウザでは処理可能だがサーバーでは処理されないような悪意のあるコードが潜んでいるかもしれないため。UTF-8による攻撃方法の例。

```
<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;
  &#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>
```
上の例を実行するとメッセージボックスが表示される。
なお、これは上のsanitize()フィルタで認識される。
Hackvertorは文字列の難読化とエンコードを行なう優れたツールであり、「敵を知る」のに最適。
Railsのsanitize()メソッドは、このようなエンコーディング攻撃をかわす。

##CSSインジェクション
CSSインジェクションは実際にはJavaScriptのインジェクション。

MySpace Samyワームは、攻撃者であるSamyのプロファイルページを開くだけで自動的にSamyに友達リクエストを送信するというもの。

MySpaceでは多くのタグをブロックしていたが、CSSについては禁止していなかったため、ワームの作者はCSSに以下のようなJavaScriptを仕込んだ。

```
<div style="background:url('javascript:alert(1)')">
```
ここでスクリプトの正味の部分(ペイロード)はstyle属性に置かれる。
一重引用符と二重引用符が既に両方使われているので、このペイロードでは引用符を使えない。
しかしJavaScriptにはどんな文字列もコードとして実行できてしまう便利なeval()関数がある。
この関数は強力だが危険。

```
<div id="mycode" expr="alert('hah!')" style="background:url('javascript:eval(document.all.mycode.expr)')">
```
eval()関数はブラックリスト方式の入力フィルタを実装した開発者にとってはまさに悪夢。
この関数を使われてしまうと、たとえば以下のように「innerHTML」という単語をstyle属性に隠しておくことができてしまうため。

```
alert(eval('document.body.inne' + 'rHTML'));
```
次は、MySpaceは"javascript"という単語をフィルタしていたにもかかわらず、「java<NEWLINE>script」と書くことでこのフィルタを突破された。

```
<div id="mycode" expr="alert('hah!')" style="background:url('java
 script:eval(document.all.mycode.expr)')">
```
さらに次は、ワームの作者がCSRFセキュリティトークンを利用していた。
ワームの作者は、ユーザーが追加される直前にページに送信されたGETリクエストの結果を解析してCSRFトークンを手に入れていた。

最終的に4KBサイズのワームができあがり、作者は自分のプロファイルページにこれを注入。

moz-bindingというCSSプロパティは、FirefoxなどのGeckoベースのブラウザではCSS経由でJavaScriptを注入する手段に使われる可能性があることが判明。

###対応策
ブラックリストによる完璧なフィルタは決して作れません。
しかしWebアプリケーションでカスタムCSSを使える機能はめったにないため、これを効果的にフィルタできるホワイトリストCSSフィルタを見つけるのは難しい。
Webアプリケーションの色や画像をカスタマイズできるようにしたいのであれば、ユーザーに色や画像を選ばせ、Webアプリケーションの側でCSSをビルドするようにする。
ユーザーがCSSを直接カスタマイズできるような作りにはしない。
どうしても必要であれば、ホワイトリストベースのCSSフィルタとしてRailsのsanitize()メソッドを使う。

##テキスタイルインジェクション（Textile Injection）
セキュリティ上の理由からHTML以外のテキストフォーマット機能を提供するのであれば、何らかのマークアップ言語を採用し、それをサーバー側でHTMLに変換するようにする。
RedClothはRuby用に開発されたマークアップ言語の一種だが、注意して使わないとXSSに対しても脆弱になる。

###対応策
RedClothは必ずホワイトリストフィルタと組み合わせて使う。


##Ajaxインクジェクション
Ajaxでも、通常のWebアプリケーション開発上で必要となるセキュリティ上の注意と同様の注意が必要。
1つ例外がある。
ページヘの出力は、アクションがビューをレンダリングしない場合であってもエスケープが必要。

in_place_editorプラグインや、ビューをレンダリングする代りに文字列を返すようなアクションを使う場合は、アクションで返される値を確実にエスケープする必要がある。
もしXSSで汚染された文字列が戻り値に含まれていると、ブラウザで表示されたときに悪意のあるコードが実行されてしまう。
入力値はすべてh()メソッドでエスケープする。

##コマンドラインインクジェクション
ユーザーが入力したデータをコマンドラインのオプションに使う場合は十分に注意が必要。

Webアプリケーションが背後のOSコマンドを実行しなければならない場合、Rubyにはexec(コマンド)、syscall(コマンド)、system(コマンド)、そしてバッククォート記法という方法が用意されている。
特に、これらのコマンド全体または一部を入力できる可能性に注意が必要。
ほとんどのシェルでは、コマンドにセミコロン;や垂直バー|を追加して別のコマンドを簡単に結合できてしまう。

対応策は、コマンドラインのパラメータを安全に渡せるsystem(コマンド, パラメータ)メソッドを使うこと。

```
system("/bin/echo","hello; rm *")
# "hello; rm *"を実行してもファイルは削除されない
```

##ヘッダーインクジェクション
HTTPヘッダは動的に生成されるものであり、特定の状況ではヘッダにユーザー入力が注入されることがある。
これを使って、にせのリダイレクト、XSS、HTTPレスポンス分割攻撃が行われる可能性がある。

HTTPリクエストヘッダで使われているフィールドの中にはReferer、User-Agent (クライアント側ソフトウェア)、Cookieフィールドがありまる。
Responseヘッダーには、たとえばステータスコード、Cookieフィールド、Locationフィールド (リダイレクト先を表す) がある。
これらのフィールド情報はユーザー側から提供されるものであり、さほど手間をかけずに操作できてしまう。
これらのフィールドもエスケープする。
エスケープが必要になるのは、管理画面でUser-Agentヘッダを表示する場合などが考えられる。

さらに、ユーザー入力の一部を取り入れたレスポンスヘッダを生成する場合は、何が行われているのかを正確に把握することが重要。
たとえば、ユーザーを特定のページにリダイレクトしてから元のページに戻したいとする。
このとき、refererフィールドをフォームに導入して、指定のアドレスにリダイレクトしたとする。

```
redirect_to params[:referer]
```
このとき、Railsはその文字列をLocationヘッダフィールドに入れて302(リダイレクト)ステータスをブラウザに送信する。
悪意のあるユーザーがこのとき最初に行なうのは、以下のような操作。

```
http://www.yourapplication.com/controller/action?referer=http://www.malicious.tld
```
Rails 2.1.2より前のバージョン(およびRuby)に含まれるバグが原因で、ハッカーが以下のように任意のヘッダを注入できてしまう可能性がある。

```
http://www.yourapplication.com/controller/action?referer=http://www.malicious.tld%0d%0aX-Header:+Hi!
http://www.yourapplication.com/controller/action?referer=path/at/your/app%0d%0aLocation:+http://www.malicious.tld
```
上のURLにおける%0d%0aは\r\nがURLエンコードされたものであり、RubyにおけるCRLF文字。
2番目の例では2つ目のLocationヘッダーフィールドが1つ目のものを上書きするため、以下のようなHTTPヘッダーが生成される。

```
HTTP/1.1 302 Moved Temporarily
(...)
Location: http://www.malicious.tld
```
ヘッダーインジェクションにおける攻撃方法とは、ヘッダーにCRLF文字を注入すること。
攻撃者は偽のリダイレクトでどんなことができてしまうのか。
攻撃者は、ユーザーをフィッシングサイトにリダイレクトし(フィッシングサイトの見た目は本物そっくりに作っておきます)、ユーザーを再度ログインさせてそのログイン情報を攻撃者に送信する可能性がある。
あるいは、フィッシングサイトからブラウザのセキュリティホールを経由して邪悪なソフトウェアを注入するかもしれない。
Rails 2.1.2ではredirect_toメソッドのLocationフィールドからこれらの文字をエスケープするようになった。
ユーザー入力を用いて通常以外のヘッダーフィールドを作成する場合には、CRLFのエスケープを必ず自分で実装する。

###レスポンス分割
ヘッダーインジェクションが実行可能になってしまっている場合、レスポンス分割(response splitting)攻撃も同様に実行可能になっている可能性がある。
HTTPのヘッダーブロックの後ろには2つのCRLFが置かれてヘッダーブロックの終了を示し、その後ろに実際のデータ(通常はHTML)が置かれる。
レスポンス分割とは、ヘッダーフィールドに2つのCRLFを注入し、その後ろに邪悪なHTMLを配置するという手法。
このときのレスポンスは以下のようになります。

```
HTTP/1.1 302 Found [最初は通常の302レスポンス]
Date: Tue, 12 Apr 2005 22:09:07 GMT
Location: Content-Type: text/html
 
 
HTTP/1.1 200 OK [ここより下は攻撃者によって作成された次の新しいレスポンス]
Content-Type: text/html
 
 
&lt;html&gt;&lt;font color=red&gt;hey&lt;/font&gt;&lt;/html&gt; [任意の邪悪な入力が
Keep-Alive: timeout=15, max=100         リダイレクト先のページとして表示される]
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html
```
特定の条件下で、この邪悪なHTMLが標的ユーザーのブラウザで表示されることがある。
ただし、おそらくKeep-Alive接続が有効になっていないとこの攻撃は効かない。
多くのブラウザはワンタイム接続を使っているため。
かといって、Keep-Aliveが無効になっていることを当てにするわけにはいかない。
これはいずれにしろ重大なバグであり、ヘッダーインジェクションとレスポンス分割の可能性を排除するため、Railsを2.0.5または2.1.2にアップグレードする必要がある。

#安全ではないクエリ生成
Rackがクエリパラメータを解析(parse)する方法とActive Recordがパラメータを解釈する方法の組み合わせに問題があり、where句がIS NULLのデータベースクエリを本来の意図に反して生成することが可能になってしまう。
(CVE-2012-2660、CVE-2012-2694 および CVE-2013-0155) のセキュリティ問題への対応として、Railsの動作をデフォルトでセキュアにするためにdeep_mungeメソッドが導入された。

以下は、deep_mungeが実行されなかった場合に攻撃者に利用される可能性のある脆弱なコードの例。

```
unless params[:token].nil?
  user = User.find_by_token(params[:token])
  user.reset_password!
end
```
params[:token]が[nil]、[nil, nil, ...]、['foo', nil]のいずれかの場合、nilチェックをパスするにもかかわらず、where句がIS NULLまたはIN ('foo', NULL)になってSQLクエリに追加されてしまう。

Railsをデフォルトでセキュアにするために、deep_mungeメソッドは一部の値をnilに置き換える。
リクエストで送信されたJSONベースのパラメータがどのように見えるかを以下に表示。

```
JSON	Parameters
{ "person": null }	{ :person => nil }
{ "person": [] }	{ :person => [] }
{ "person": [null] }	{ :person => [] }
{ "person": [null, null, ...] }	{ :person => [] }
{ "person": ["foo", null] }	{ :person => ["foo"] }
```
リスクと取扱い上の注意を十分理解している場合に限り、deep_mungeをオフにしてアプリケーションを従来の動作に戻すことができる。

```
config.action_dispatch.perform_deep_munge = false
```

#デフォルトのヘッダー
Railsアプリケーションから受け取るすべてのHTTPレスポンスには、以下のセキュリティヘッダーがデフォルトで含まれている。

```
config.action_dispatch.default_headers = {
  'X-Frame-Options' => 'SAMEORIGIN',
  'X-XSS-Protection' => '1; mode=block',
  'X-Content-Type-Options' => 'nosniff',
  'X-Download-Options' => 'noopen',
  'X-Permitted-Cross-Domain-Policies' => 'none',
  'Referrer-Policy' => 'strict-origin-when-cross-origin'
}
```
デフォルトのヘッダー設定はconfig/application.rbで変更できる。

```
config.action_dispatch.default_headers = {
  'Header-Name' => 'Header-Value',
  'X-Frame-Options' => 'DENY'
}
```
以下のようにヘッダーを除去することもできる。

```
config.action_dispatch.default_headers.clear
```
よく使われるヘッダーのリストを以下に示す。

X-Frame-Options: Railsではデフォルトで'SAMEORIGIN'が指定される。
このヘッダーは、同一ドメインでのフレーミングを許可。
'DENY'を指定するとすべてのフレーミングが不許可になる。
すべてのWebサイトについてフレーミングを許可するには'ALLOWALL'を指定。
X-XSS-Protection: Railsではデフォルトで'1; mode=block'が指定される。
XSS攻撃が検出された場合は、XSS Auditorとブロックページを使う。
XSS Auditorをオフにしたい場合は'0;'を指定します(レスポンスがリクエストパラメータからのスクリプトを含んでいる場合に便利)。
X-Content-Type-Options: 'nosniff'はRailsではデフォルト。
このヘッダーは、ブラウザがファイルのMIMEタイプを推測しないようにする。
X-Content-Security-Policy: このヘッダーは、コンテンツタイプを読み込む元のサイトを制御するための強力なメカニズム。
Access-Control-Allow-Origin: このヘッダーは、同一生成元ポリシーのバイパスとクロスオリジン(cross-origin)リクエストをサイトごとに許可する。
Strict-Transport-Security: このヘッダーは、ブラウザからサイトへの接続をセキュアなものに限って許可するかどうかを指定。

###Content Security Policy（CSP）
Railsでは、アプリケーションでContent Security Policy（CSP）を設定するためのDSLが提供されている。
グローバルなデフォルトポリシーを設定し、それをリソースごとにオーバーライドすることも、lambdaを用いてリクエストごとに値をヘッダーに注入することもできる（マルチテナントのアプリケーションにおけるアカウントのサブドメインなど）。

以下はグローバルなポリシーの例。

```
# config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
  policy.default_src :self, :https
  policy.font_src    :self, :https, :data
  policy.img_src     :self, :https, :data
  policy.object_src  :none
  policy.script_src  :self, :https
  policy.style_src   :self, :https
  # 違反レポートの対象URIを指定する
  policy.report_uri "/csp-violation-report-endpoint"
end
```
以下はコントローラでオーバーライドするコード例。

```
# ポリシーをインラインでオーバーライドする場合
class PostsController < ApplicationController
  content_security_policy do |p|
    p.upgrade_insecure_requests true
  end
end
# リテラル値を使う場合
class PostsController < ApplicationController
  content_security_policy do |p|
    p.base_uri "https://www.example.com"
  end
end
# 静的値と動的値を両方使う場合
class PostsController < ApplicationController
  content_security_policy do |p|
    p.base_uri :self, -> { "https://#{current_user.domain}.example.com" }
  end
end
# グローバルCSPをオフにする場合
class LegacyPagesController < ApplicationController
  content_security_policy false, only: :index
end
```
レガシーなコンテンツを移行するときにコンテンツの違反だけをレポートしたい場合は、設定でcontent_security_policy_report_only属性を用いてContent-Security-Policy-Report-Onlyを設定。

```
# config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy_report_only = true
# コントローラでオーバーライドする場合
class PostsController < ApplicationController
  content_security_policy_report_only only: :index
end
```
以下の方法でnonceの自動生成を有効にできる。

```
# config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
  policy.script_src :self, :https
end
Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
```
後は以下のようにhtml_optionsの中でnonce: trueを渡せばnonce値が自動的に追加される。

```
<%= javascript_tag nonce: true do -%>
  alert('Hello, World!');
<% end -%>
```
javascript_include_tagでも同じことができる。

```
<%= javascript_include_tag "script", nonce: true %>
```
セッションごとにインライン<script>タグを許可するnonce値を含むcsp-nonceメタタグを生成するには、csp_meta_tagヘルパーを使う。

```
<head>
  <%= csp_meta_tag %>
</head>
```
これは、動的に読み込まれるインライン<script>要素をRails UJSヘルパーが生成するのに使われる。

#利用環境のセキュリティ
config/database.ymlなどに置かれるデータベース接続設定のセキュリティや、config/secrets.ymlなどに置かれるサーバーサイドの秘密鍵のセキュリティは保つようにする。
これらのファイルや、その他重要な情報を含む可能性のあるファイルを、環境に合わせて複数のバージョンを使い分けることで、さらにアクセス制限をかけられる。

###独自のcredential
Railsはcredentialファイルconfig/credentials.yml.encに秘密鍵を保存する。
このファイルは暗号化されているため直接編集することはできない。
Railsはcredentialファイルを暗号化するためのマスターキーにconfig/master.keyか環境変数ENV["RAILS_MASTER_KEY"]を使用する。
credentialファイルは、マスターキーが安全に保存されている場合に限り、バージョン管理システムに登録することができる。

新しい秘密鍵をcredentialファイルに追加するには、まずrails secretを実行して新しい秘密鍵を生成し、続いてrails credentials:editでcredentialを編集して秘密鍵を追加する。credentials:editを実行すると、credentialファイルとマスターキーがまだ存在してなければ新たに作成する。

このファイルには、アプリケーションのsecret_key_baseがデフォルトで含まれますが、外部API向けのアクセスキーなどのcredentialを含めることもできる。

credentialファイル内の秘密情報にはRails.application.credentialsでアクセスできる。
たとえば、復号したconfig/credentials.yml.encファイルに以下があるとする。

```
secret_key_base: 3b7cd727ee24e8444053437c36cc66c3
some_api_key: SOMEKEY
```
どの環境でもRails.application.credentials.some_api_keyからSOMEKEYが返される。

キーが空の場合に例外を発生させるには、!を付ける。

```
Rails.application.credentials.some_api_key! # => raises KeyError: :some_api_key is blank
```
マスターキーは安全な場所に保管する。
マスターキーをコミットに含めてはいけない。

#依存関係の管理とCVEについて
新しいバージョンの利用を推進するためだけの理由で依存関係を変更することはない。
アプリケーションのオーナーが手動でgemを更新する必要があるため。
脆弱な依存関係を安全に更新するには、bundle update --conservative gem_nameを使う。