In a recent sinatra app I’m working on I require the use of dynamic form helpers to generate html forms similar to rails.
After several attempts at trying out various options, I settled on Padrino as it has features similar to Rails and are well tested.
However to enable authenticity_token
within the forms, one has to jump
over several hoops to activate it properly.
Enable sessions and protect_from_csrf
You need to enable the following settings in Sinatra, especially
sessions and session_secret since the authenticity_token
in stored
within the session hash:
1
2
3
enable :sessions
set :session_secret, SecureRandom.hex(32)
set :protect_from_csrf, true
The protect_from_csrf
is required else it will fail with ‘protect_from_csrf not found’. Even if you don’t need to use the authenticity_token
feature, you can disable it completely (not recommended):
1
disable :protect_from_csrf
Enable Rack Middleware
Secondly, you need to activate the Rack middleware with ‘use’ to actually enable CSRF checking. Padrino uses either Rack::Protection::AuthenticityToken or Padrino::AuthenticityToken based on whether you need to filter out certain routes. This can be seen in the following source code:
1
builder.use(options[:except] ? Padrino::AuthenticityToken : Rack::Protection::AuthenticityToken, options)
Since I’m using only the Padrino helpers in a Sinatra app, I need to include the middleware. For a Padrino application, the builder will do the below for you automatically based on the settings above.
If you are allowing all routes to be checked for CSRF, you can just enable the following:
1
use Rack::Protection::AuthenticityToken
If you have API endpoints which do not require CSRF checking as there are no form submissions:
1
use Padrino::AuthenticityToken, :except => ["/api"]
The difference being Padrino::AuthenticityToken
subclasses Rack::Protection::AuthenticityToken
but in its call block it actually
checks to see if the :except
option is set and exclude those routes specified. The other routes get passed through to Rack::Protection::AuthenticityToken
in its super call.
This can be seen in the middleware itself:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def call(env)
if except?(env)
@app.call(env)
else
super
end
end
def except?(env)
return false unless @except
path_info = env['PATH_INFO']
@except.is_a?(Proc) ? @except.call(env) : @except.any?{|path|
path.is_a?(Regexp) ? path.match(path_info) : path == path_info }
end
I have to require "padrino-core/application/authenticity_token"
manually in addition to 'padrino-helpers'
but due to the modularity of Padrino, I only need to include what I need in my use case.
A complete config
The complete config is as so:
1
2
3
4
5
6
7
8
9
10
11
12
13
require "padrino-helpers"
require "padrino-core/application/authenticity_token"
class MyApp < Sinatra::Base
configure do
enable :sessions
set :session_secret, SecureRandom.hex(32)
# enable authenticity_token in forms
set :protect_from_csrf, true
# actual checks for csrf tokens from form submissions
use Padrino::AuthenticityToken, :except => ["/api"]
end
end
Happy Hacking!