Files
fizzy/app/controllers/concerns/request_forgery_protection.rb
T
2025-11-26 13:16:44 +01:00

51 lines
1.5 KiB
Ruby

module RequestForgeryProtection
extend ActiveSupport::Concern
included do
after_action :append_sec_fetch_site_to_vary_header
end
private
def append_sec_fetch_site_to_vary_header
vary_header = response.headers["Vary"].to_s.split(",").map(&:strip).reject(&:blank?)
response.headers["Vary"] = (vary_header + [ "Sec-Fetch-Site" ]).join(",")
end
def verified_request?
return true if request.get? || request.head? || !protect_against_forgery?
origin = valid_request_origin?
token = any_authenticity_token_valid?
sec_fetch_site = safe_fetch_site?
report_on_forgery_protection_results(origin:, token:, sec_fetch_site:)
origin && token
end
SAFE_FETCH_SITES = %w[ same-origin same-site ]
def safe_fetch_site?
SAFE_FETCH_SITES.include?(sec_fetch_site_value)
end
def sec_fetch_site_value
request.headers["Sec-Fetch-Site"].to_s.downcase
end
def report_on_forgery_protection_results(origin:, token:, sec_fetch_site:)
results = { origin:, token:, sec_fetch_site: }
unless results.values.all?
info = results.transform_values { it ? "pass" : "fail" }
info[:origin] += " (#{request.origin})"
info[:sec_fetch_site] += " (#{sec_fetch_site_value})"
Rails.logger.info "CSRF protection check: " + info.map { it.join(" ") }.join(", ")
if (origin && token) != sec_fetch_site
Sentry.capture_message "CSRF protection mismatch", level: :info, extra: { info: info }
end
end
end
end