改めて Railsガイドの セキュリティガイド を読んで、気になった点や知らなかった点をまとめた。
bin/rails secret で一意な秘密鍵を生成できる
cookies.encrypted[:foo] = "bar"cookies.signed[:foo] = "bar"cookies.permanent[:foo] = "bar" というのもある。デフォルトで期限が20年になる例えば、署名済みcookieのダイジェストをSHA1からSHA256に変更する場合
Rails.application.config.action_dispatch.signed_cookie_digest = "SHA256"
後は古いSHA1ダイジェストのローテーションを追加すれば、既存のcookieが自動的にSHA256ダイジェストにアップグレードされる
Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
cookies.rotate :signed, digest: "SHA1"
end
<img> の src にターゲットのアプリケーションに対するURLを埋め込むことができてしまう<img src="http://www.webapp.com/project/1/destroy">before_action に only: [...] ではなく except: [...] を指定すること。その方が将来コントローラにアクションを追加するときにセキュリティチェックを忘れずに済む<script> を削除する禁止リスト方式ではなく、たとえば <strong> だけを許可する許可リスト方式を使うこと"<sc<script>ript>".gsub("<script>", "")HttpOnly フラグをつけることで、その cookie は JavaScript の document.cookie APIにはアクセスできない。これは XSS を緩和する
cookies[] で設定するクッキーは、デフォルトで httponly: true がセットされる<div style="background:url('javascript:alert(1)')">system() の複数パラメータでの呼び出しを使う対策がある
system() は引数が一つの文字列の場合は、シェル経由(/bin/sh -c "...")で実行するsystem() は引数が複数の文字列の場合は、シェル経由ではなく、文字列をシェルとして解釈せずに実行するKernel#open は、| で始まる引数を渡すとOSコマンドを実行できる
puts open('| ls') { |f| f.read }File.open, IO.open, URI#open を使う例えば、以下のように指定のアドレスにリダイレクトする実装があるとする
redirect_to params[:referer]
悪意のあるユーザーが以下のように Location ヘッダーを注入する
http://www.yourapplication.com/controller/action?referer=path/at/your/app%0d%0aLocation:+http://www.malicious.tld
%0d%0a は \r\n がURLエンコードされたものなので、以下のように解釈されてリダイレクトしてしまう
HTTP/1.1 302 Moved Temporarily
(...)
Location: http://www.malicious.tld
redirect_to メソッドの Location フィールドからこれらの文字をエスケープするようになったCRLF 文字のエスケープが必要Host: evil.example.com の Host を、システム側がそのまま信用して使うことで起こるrequest.host を使って構築していると、攻撃者が被害者のメールアドレスを使って Host: evil.example.com を含むリクエストをリセット機能に送信すると、被害者には https://evil.example.com/reset_password?token=token のような URL を含むメールが送信されてしまうRails.application.config.hosts << "product.com" を設定することで、不正な Host のリクエストを 403 で拒否することができるHost フィールドが必須。HTTP/2,HTTP/3 では必須ではなくなったが、代わりに :authority に同等の情報が必要であり、依然として問題が発生する可能があるunless params[:token].nil?
user = User.find_by_token(params[:token])
user.reset_password!
end
params[:token] が [nil], [nil, nil, ...], ['foo', nil] のとき、nil チェックがバイパスされ、SQLに IS NULL や IN ('foo', NULL) が混入する可能性があるconfig.perform_deep_munge オプションが有効になっているdeep_munge は次のように、一部の値を nil に置き換える| JSON | パラメータ |
|---|---|
{ "person": null } |
{ :person => nil } |
{ "person": [] } |
{ :person => [] } |
{ "person": [null] } |
{ :person => [] } |
{ "person": [null, null, ...] } |
{ :person => [] } |
{ "person": ["foo", null] } |
{ :person => ["foo"] } |
<frame> <iframe> <embed> または <object> タグでレンダリングしてよいかどうかを示すSAMEORIGIN に設定されており、同一ドメイン内でのみフレームのレンダリングを許可nosniff で、ブラウザがコンテンツのMIMEタイプを推測してレンダリングしないように指示するconfig.force_ssl = true にすると、このヘッダーがレスポンスに追加されるRails.application.config.permissions_policy do |policy|
policy.camera :none
policy.gyroscope :none
policy.microphone :none
policy.usb :none
policy.fullscreen :self
policy.payment :self, "https://secure.example.com"
end