Module: Roda::RodaPlugins::SecFetchSiteCsrf

Defined in:
lib/roda/plugins/sec_fetch_site_csrf.rb

Overview

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).

Defined Under Namespace

Modules: InstanceMethods Classes: CsrfFailure

Constant Summary collapse

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

Class Method Summary collapse

Class Method Details

.configure(app, opts = OPTS, &block) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/roda/plugins/sec_fetch_site_csrf.rb', line 71

def self.configure(app, opts=OPTS, &block)
  options = app.opts[:sec_fetch_site_csrf] = (app.opts[:sec_fetch_site_csrf] || DEFAULTS).merge(opts)

  allowed_values = options[:allowed_values] = ["same-origin"]
  allowed_values << "same-site" if opts[:allow_same_site]
  allowed_values << "none" if opts[:allow_none]
  allowed_values << nil if opts[:allow_missing]
  allowed_values.freeze

  if block
    options[:csrf_failure] = :method
    app.define_roda_method(:_roda_sec_fetch_site_csrf_failure, 1, &app.send(:convert_route_block, block))
  end

  case options[:csrf_failure]
  when :raise, :empty_403, :clear_session, :method
    # nothing
  else
    raise RodaError, "Unsupported :csrf_failure plugin option: #{options[:csrf_failure].inspect}"
  end

  options.freeze
end