Quantcast
Channel: ruby - Madogiwa Blog
Viewing all articles
Browse latest Browse all 42

Ruby on Rails: strict CSPを採用して管理するようにするメモ📝

$
0
0

CSPの設定を以下のStrict CSPを参考に基本的にはstrict-dynamicを利用して全てのscriptにnonceを付与し、信頼されたscriptから読み込まれたscriptのみを実行するように変更してみたのでメモ📝

web.dev

CSPの概要的な話は以下の記事を参考にして下さい。

madogiwa0124.hatenablog.com

以下が最終的な設定値です。(Vite及びVue.jsの完全ビルドを採用しているサービです)

Rails.application.configure do
  config.content_security_policy do |policy|
    policy.default_src :self, :https
    policy.font_src :self, :https
    policy.img_src :self, :https, :data
    policy.object_src :none# NOTE: Vue.jsの完全ビルドを動作されるのに必要なunsafe-evalは許可。
    policy.script_src :strict_dynamic, :unsafe_eval
    policy.style_src :self, :httpsifRails.env.development?
      # NOTE: 開発環境ではviteの開発サーバーとhttp/wsでの通信を許可する# Allow @vite/client to hot reload javascript changes in development
      policy.connect_src :ws, :http# NOTE: viteの制約上、unsafe-inlineを許可する必要がある# https://github.com/vitejs/vite/issues/11862
      policy.style_src(*policy.style_src, :unsafe_inline)
    endend# Generate session nonces for permitted importmap and inline scripts
  config.content_security_policy_nonce_generator = ->(request) {
    # NOTE: テスト実行時に以下となりSessionが取得できずnonceが空文字になりCSP違反が発生してしまうので強制的にロードする# #<ActionDispatch::Request::Session:0xd610 not yet loaded># https://github.com/rails/rails/issues/10813#issuecomment-297204965
    request.session[:init] = true
    request.session.id.to_s
  }
  config.content_security_policy_nonce_directives = %w[script-src]# Report CSP violations to a specified URI. See:# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only# config.content_security_policy_report_only = trueend

Strict CSP を参考にstrict-dynamicでnonce付与されたスクリプト自体 + そこからロードされるスクリプトを許可する。

strict-dynamicを利用すると以下の通り、nonceを付与したスクリプト及び、それによって読み込まれる全てのスクリプトを許可することができます。

'strict-dynamic'ソース式は、マークアップ中のスクリプトに明示的に与えられた信頼が、ノンスやハッシュを伴って、そのルートスクリプトによって読み込まれるすべてのスクリプトに伝搬されることを指定します。同時に、 'self'や 'unsafe-inline'のようなホワイトリストやソース表現は無視されます。 https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#strict-dynamic

strict-dynamicは設定するとscript-srchttpsといったホスト指定、unsafe-inlineself`のキーワード指定は無視されるようです。

host-source and scheme-source expressions, as well as the "'unsafe-inline'" and "'self' keyword-sources will be ignored when loading script. hash-source and nonce-source expressions will be honored. https://www.w3.org/TR/CSP3/#strict-dynamic-usage

あくまでscriptの読み込みに対してのみ有効なので、画像やCSS等の読み込みには引き続き明示的に許可を与える必要がある点には注意です。

What about CSS or Images Loaded via strict-dynamic JavaScript? If one of your trusted scripts loads JavaScript using a safe method (document.createElement), then it can load arbitrary scripts with strict-dynamic. However if your trusted scripts attempt to load an image, a stylesheet, etc., then the img-src, and style-src directives still apply. https://content-security-policy.com/strict-dynamic/

そのためstrict-dynamicで読み込まれたinline styleを許可する場合には、style-srcunsafe-inlineを追加する必要があります。

strict-dynamicはブラウザポートの懸念があり、GoogleのStrict CSPのページにもサポート状態を考慮した指定が記載されていますが、現在ではモダンブラウザであれば基本サポートされているため問題なさそうに思いました。

caniuse.com

(微妙かも?)stlict_dynamicを有効にした場合にhttp通信も許可されないようにhttpsへアップグレードする

strict_dynamicを利用する場合nonceで許可したスクリプトから動的に読み込まれたスクリプトも許可されるが、 その場合にhttp通信が入るとcookie漏洩や盗聴等のリスクがありそうだなぁと思っていたが、 upgrade_insecure_requestsを有効化するとブラウザにhttp通信をhttps通信にアップグレードさせることができる。

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Security-Policy/upgrade-insecure-requests

https通信できない場合のリソース取得は以下の通り失敗しブロックされる。

GET https://myapp.com:3000/packs/assets/Vue-DZm-d5Zi.js net::ERR_SSL_PROTOCOL_ERROR

これらの URL は、リクエストが行われる前に書き直されます。つまり、安全でないリクエストがネットワークに侵入しないようにします。なお、リクエストされたリソースが実際に HTTPS経由で利用可能ではない場合、リクエストは HTTP で代替されずに失敗することに注意してください。 https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Security-Policy/upgrade-insecure-requests

上記の通りCSPのエラーに乗らず普通にリクエストが失敗してしまうので困っていたが、Content-Security-Policy-Report-Onlyヘッダーとreport-uriディレクティブを利用してhttpでのリクエストを検知することが可能っぽい。(自分が使っているエラー通知サービス(Rollbar)によしなに通知されないかな〜と思ったけどダメだった)

安全ではないリクエストの発見 Content-Security-Policy-Report-Only ヘッダーと report-uriディレクティブを利用して、強制ポリシーと報告されたポリシーを次のように設定することができます。 https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Security-Policy/upgrade-insecure-requests#%E5%AE%89%E5%85%A8%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%84%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%81%AE%E7%99%BA%E8%A6%8B

ただし、Railscontent_security_policyではreport_onlyがbool値での指定のみのサポートで、レポートかエラーかの切り替えはできないっぽいので、この方式は難しそう😢

あと、upgrade_insecure_requestslocalhostでもsafariでは有効になってしまう問題があるのと、自分の環境で試した場合にContent-Security-Policy-Report-Onlyにしてもhttpsへの接続に切り替えてしまうような挙動がありそうだったので、ちょっと本番活用するには安定感が不安かもしれない。。。

github.com

参考

tech.classi.jp

inside.pixiv.blog

www.suzukikenichi.com


Viewing all articles
Browse latest Browse all 42

Trending Articles