🛡️

Rails アプリケーションのセキュリティ対策(CORS/CSP/HSTS)

2021/01/11に公開

はじめに

Rails のセキュリティ対策というと、まずは Rails セキュリティガイド を読んだ上で Rails Way にのっとって実装するのが基本だと思います。ただ、日々技術が進化しそれにあわせて新しい攻撃が生まれている中では「Rails のレールに乗っていれば大丈夫」とは言いきれません。アプリケーションの重要度によっては Rails のデフォルト設定だけでは不十分なこともあるかもしれません。

先日リリースされた『Web ブラウザセキュリティ』という本を読みはじめたので、登場したいくつかのレスポンスヘッダについて Rails ではどう対策されているのか(対策したらいいのか)を調べてみました。

デフォルトのレスポンスヘッダ

Rails セキュリティガイド にもある通り、Rails では以下のレスポンスヘッダがデフォルトで含まれるようになっています。

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"
}

https://github.com/rails/rails/blob/2afc9059c9eb509f47d94250be0a917059afa1ae/actionpack/lib/action_dispatch/railtie.rb#L28-L35

CORS (Cross-Origin Resource Sharing)

XMLHttpRequestFetch API で接続された異なる Origin にリソースを共有することをクロスオリジンリソースシェアリング (CORS) といいます。

CORS は、外部サービスのアクセス制限を行うためのもので、クライアントからサーバーにアクセスする権限を確認するための仕組みです。リソースの提供者が異なる Origin からアクセスされることを許可するためには Access-Control-Allow-Origin ヘッダを使います。

Rails では、app/controllers/application_controller.rb などで以下のようにヘッダを設定したり、rack-cors という gem もあるようです。

headers["Access-Control-Allow-Origin"]  = "*"
headers["Access-Control-Allow-Methods"] = %w[GET POST PUT DELETE].join(",")
headers["Access-Control-Allow-Headers"] = %w[X-My-Request-Header]

CSP (Content Security Policy)

XSS に対する防御策として、CSP (Content Security Policy) があります。

XSS は、Web ブラウザの入力フィールドや URL パラメータに、悪意のあるスクリプトを埋め込むことでリクエストを改ざんするという攻撃方法です。

CSP は Content-Security-Policy レスポンスヘッダを使って、Web ブラウザで使える機能を細かく設定ができる仕組みです。例えば、Content-Security-Policy: script-src 'self'; のように設定した場合、同じオリジンの JavaScript だけが実行可能となり、異なるオリジンのスクリプトは実行できなくなります。これにより、外部の悪意のある処理を実行させようとするスクリプトが埋め込まれた場合などの対策となります。

Rails では CSP を設定するための DSL が提供されています。(詳細は Rails セキュリティガイド - 9.1 Content Security Policy (CSP) を参照してください。)

汎用性があり強度が高い CSP の例として Google が推奨している CSP 設定 を参考に Rails で設定するとこんな感じでしょうか。

config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
  policy.object_src   :none
  policy.script_src   :unsafe_inline, :unsafe_eval, :strict_dynamic, :https, :http
  policy.base_uri     :none

  # Specify URI for violation reports
  policy.report_uri "/csp-violation-report-endpoint"
end

# If you are using UJS then enable automatic nonce generation
Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }

# Set the nonce only to specific directives
Rails.application.config.content_security_policy_nonce_directives = %w(script-src)

nonce の自動生成を有効にしたので、View で JavaScript を読み込んでいる部分に nonce: true を追加します。

app/views/layouts/application.html.erb
<%= csp_meta_tag %>

<%= javascript_include_tag 'application', nonce: true %>

<script> タグを使っている場合はこんな感じ。

<script nonce="<%= content_security_policy_nonce %>">
  ...
</script>

HSTS (HTTP Strict Transport Security)

HTTP は通信が暗号化されていない(平文通信)ため、中間者攻撃を受け盗聴される恐れがあります。攻撃を回避するために、暗号化された HTTPS 通信を利用するのが最近では一般的になっています。

HSTS は、サーバーからクライアントに対して「このホストに今後アクセスするときは HTTPS を利用してね」ということを伝える仕組みです。これは Strict-Transport-Security というレスポンスヘッダを付与することで行われます。

Rails では config/environments/production.rb などで config.force_ssl = true を設定すると、自動的にStrict-Transport-Security が付与されるようになっています。

デフォルトのオプションは

https://github.com/rails/rails/blob/5710128a84d5d03646dca6118db0e3a5715904ec/actionpack/lib/action_dispatch/middleware/ssl.rb#L57-L59

def self.default_hsts_options
  { expires: HSTS_EXPIRES_IN, subdomains: true, preload: false }
end

となっているので、通常は以下のような Strict-Transport-Security ヘッダが付与されるはずです。

Strict-Transport-Security: max-age=31536000; includeSubDomains

所感

Rails がセキュリティ対策の方法をいろいろ提供してくれているので、やっぱり Rails Way に乗っているのは大切ですね。そのまま使っているだけでも基本的な対策がされているのは本当に便利です。

とはいえ、セキュリティのことを理解しておくことは重要なので引き続き意識していきたいと思います。

参考

Discussion