module Roda::RodaPlugins::SecFetchSiteCsrf

  1. lib/roda/plugins/sec_fetch_site_csrf.rb

The sec_fetch_site plugin allows for CSRF protection using the Sec-Fetch-Site header added in modern browsers. It allows for CSRF protection without the use of CSRF tokens, which can simplify form creation.

The protection offered by the sec_fetch_site plugin is weaker than the protection offered by the route_csrf plugin with default settings, since it doesn’t support per-request tokens. Be aware you are trading security for simplicity when using the sec_fetch_site plugin instead of the route_csrf plugin. Other caveats in using the sec_fetch_site plugin:

  • Not all browsers set the Sec-Fetch-Site header. Some browsers didn’t start setting the header until 2023. In these cases, you need to decide how to handle the request. The default is to deny the request, though you can use the :allow_missing option to allow it.

  • Sec-Fetch-Site headers are not set for http requests, only https requests, so this doesn’t offer protection for http requests.

  • It isn’t possible to share a CSRF secret between applications in different origins to allow cross-site requests between the applications.

This plugin adds the check_sec_fetch_site! method to the routing block scope. You should call this method at the appropriate place in the routing tree to enforce the CSRF protection. The method can accept a block to override the :csrf_failure plugin option behavior on a per-call basis.

When loading the plugin with no options:

plugin :sec_fetch_site_csrf

Only same-origin requests are allowed by default.

This plugin supports the following options:

:allow_missing

Whether to allow requests lacking the Sec-Fetch-Site header (false by default).

:allow_none

Whether to allow requests where Sec-Fetch-Value is none (false by default).

:allow_same_site

Whether to allow requests where Sec-Fetch-Value is same-site (false by default)

:check_request_methods

Which request methods require CSRF protection (default: ['POST', 'DELETE', 'PATCH', 'PUT'])

:csrf_failure

The action to taken if a request does not have a valid header (default: :raise). Options:

:raise

raise a Roda::RodaPlugins::SecFetchSiteCsrf::CsrfFailure exception

:empty_403

return a blank 403 page

:clear_session

clear the current session

The plugin also supports a block, in which case failures will call the block as a routing block (the block should accept the request object).

Methods

Public Class

  1. configure

Constants

DEFAULTS = { :csrf_failure => :raise, :check_request_methods => %w'POST DELETE PATCH PUT'.freeze.each(&:freeze) }.freeze  

Public Class methods

configure(app, opts=OPTS, &block)
[show source]
   # File lib/roda/plugins/sec_fetch_site_csrf.rb
71 def self.configure(app, opts=OPTS, &block)
72   options = app.opts[:sec_fetch_site_csrf] = (app.opts[:sec_fetch_site_csrf] || DEFAULTS).merge(opts)
73 
74   allowed_values = options[:allowed_values] = ["same-origin"]
75   allowed_values << "same-site" if opts[:allow_same_site]
76   allowed_values << "none" if opts[:allow_none]
77   allowed_values << nil if opts[:allow_missing]
78   allowed_values.freeze
79 
80   if block
81     options[:csrf_failure] = :method
82     app.define_roda_method(:_roda_sec_fetch_site_csrf_failure, 1, &app.send(:convert_route_block, block))
83   end
84 
85   case options[:csrf_failure]
86   when :raise, :empty_403, :clear_session, :method
87     # nothing
88   else
89     raise RodaError, "Unsupported :csrf_failure plugin option: #{options[:csrf_failure].inspect}"
90   end
91 
92   options.freeze
93 end