roda 3.9.0 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +18 -0
- data/README.rdoc +43 -23
- data/doc/release_notes/3.10.0.txt +132 -0
- data/lib/roda.rb +3 -3
- data/lib/roda/plugins/assets.rb +2 -2
- data/lib/roda/plugins/flash.rb +8 -2
- data/lib/roda/plugins/json.rb +1 -3
- data/lib/roda/plugins/json_parser.rb +1 -2
- data/lib/roda/plugins/middleware.rb +12 -3
- data/lib/roda/plugins/route_csrf.rb +34 -32
- data/lib/roda/plugins/sessions.rb +451 -0
- data/lib/roda/plugins/typecast_params.rb +15 -2
- data/lib/roda/session_middleware.rb +175 -0
- data/lib/roda/version.rb +1 -1
- data/spec/plugin/csrf_spec.rb +2 -2
- data/spec/plugin/flash_spec.rb +17 -23
- data/spec/plugin/heartbeat_spec.rb +1 -1
- data/spec/plugin/middleware_spec.rb +15 -0
- data/spec/plugin/route_csrf_spec.rb +3 -2
- data/spec/plugin/sessions_spec.rb +371 -0
- data/spec/plugin/typecast_params_spec.rb +11 -0
- data/spec/session_middleware_spec.rb +129 -0
- data/spec/session_spec.rb +2 -2
- data/spec/spec_helper.rb +10 -1
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0fa178463d549cd2a8826a8601eccc793e2a3b5a6bb34293ce8511fb8d357128
|
4
|
+
data.tar.gz: 5b099180432b7efc3bc4839883367a454c876d9a2e6809f533f970e83bd32d0d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7ec971ff829c784c21c9660b7964f79755841501449c6372260510a367482a9dde5131bf6b488eadefdd24403f7a19576c8d1176d8d8306fecc861388fb9f7f
|
7
|
+
data.tar.gz: 7fe25ba12cceccf1b59d9993d4972c0ffb2df2b78b68716f7708ecc58d131bc0858d9b7a1b87ac34bb88a05b57aeb6f0fb68beac648bbdfe2f7c0c3c6933def9
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
= 3.10.0 (2018-07-18)
|
2
|
+
|
3
|
+
* Remove flash key from session if new flash is empty when rotating flash (jeremyevans)
|
4
|
+
|
5
|
+
* Speed up RodaRequest initialization by avoiding 1-2 method calls (jeremyevans)
|
6
|
+
|
7
|
+
* Add roda/session_middleware (RodaSessionMiddleware), usable as a middleware by any Rack app to use Roda's session support (jeremyevans)
|
8
|
+
|
9
|
+
* Add sessions plugin for more secure (encrypted+signed) sessions (jeremyevans)
|
10
|
+
|
11
|
+
* Support :json_parser and :json_serializer application options as default implementations for parsing/serializing JSON (jeremyevans)
|
12
|
+
|
13
|
+
* Add :handle_result option to middleware plugin for modifying rack result before returning it (jeremyevans)
|
14
|
+
|
15
|
+
* Make the flash plugin work correctly when sessions are serialized with JSON (jeremyevans)
|
16
|
+
|
17
|
+
* Make Integer in typecast_params handle Numeric input, and require that Numeric input not have fractional parts (jeremyevans) (#146)
|
18
|
+
|
1
19
|
= 3.9.0 (2018-06-11)
|
2
20
|
|
3
21
|
* Add route_csrf plugin for CSRF protection, offering more control, better security, and request-specific tokens compared to rack_csrf (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -699,8 +699,15 @@ The following options are respected by the default library or multiple plugins:
|
|
699
699
|
:add_script_name :: Prepend the SCRIPT_NAME for the request to paths. This is
|
700
700
|
useful if you mount the app as a path under another app.
|
701
701
|
:freeze_middleware :: Whether to freeze all middleware when building the rack app.
|
702
|
+
:json_parser :: A callable for parsing JSON (+JSON.parse+ in general used by
|
703
|
+
default).
|
704
|
+
:json_serializer :: A callable for serializing JSON (+to_json+ in general used
|
705
|
+
by default).
|
702
706
|
:root :: Set the root path for the app. This defaults to the current working
|
703
707
|
directory of the process.
|
708
|
+
:sessions_convert_symbols :: This should be set to +true+ if the sessions in use
|
709
|
+
do not support roundtripping of symbols (for
|
710
|
+
example, when sessions are serialized via JSON).
|
704
711
|
|
705
712
|
There may be other options supported by individual plugins, if so it will be
|
706
713
|
mentioned in the documentation for the plugin.
|
@@ -753,38 +760,51 @@ You can override the default rendering options by passing a hash to the plugin:
|
|
753
760
|
template_opts: {default_encoding: 'UTF-8'} # Default template options
|
754
761
|
end
|
755
762
|
|
756
|
-
==
|
763
|
+
== Security
|
764
|
+
|
765
|
+
Web application security is a very large topic,
|
766
|
+
but here are some things you can do with Roda
|
767
|
+
to prevent some common web application vulnerabilities.
|
768
|
+
|
769
|
+
=== Session Security
|
757
770
|
|
758
|
-
By default, Roda doesn't turn on sessions,
|
759
|
-
|
760
|
-
|
761
|
-
that comes with Rack:
|
771
|
+
By default, Roda doesn't turn on sessions, and if you don't need sessions, you can
|
772
|
+
skip this section. If you do need sessions, Roda offers two recommended ways to
|
773
|
+
implement cookie-based sessions.
|
762
774
|
|
763
|
-
|
775
|
+
If you do not need any session support in middleware, and only need session support
|
776
|
+
in the Roda application, then use the sessions plugin:
|
764
777
|
|
778
|
+
require 'roda'
|
765
779
|
class App < Roda
|
766
|
-
|
780
|
+
plugin :sessions, secret: ENV['SESSION_SECRET']
|
767
781
|
end
|
768
782
|
|
769
|
-
|
783
|
+
The +:secret+ option should be a randomly generated string of at least 64 bytes.
|
770
784
|
|
771
|
-
|
772
|
-
|
773
|
-
to prevent some common web application vulnerabilities.
|
785
|
+
If you have middleware that need access to sessions, then use the +RodaSessionMiddleware+
|
786
|
+
that ships with Roda:
|
774
787
|
|
775
|
-
|
788
|
+
require 'roda'
|
789
|
+
require 'roda/session_middleware'
|
790
|
+
class App < Roda
|
791
|
+
use RodaSessionMiddleware, secret: ENV['SESSION_SECRET']
|
792
|
+
end
|
776
793
|
|
777
|
-
If you
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
794
|
+
If you need non-cookie based sessions (such as sessions stored in a database), you
|
795
|
+
should use an appropriate external middleware.
|
796
|
+
|
797
|
+
It is possible to use other session cookie middleware such as
|
798
|
+
<tt>Rack::Session::Cookie</tt>, but other middleware may not have the same security
|
799
|
+
features that Roda's session support does. For example, the session cookies used by
|
800
|
+
the <tt>Rack::Session::Cookie</tt> middleware are not encrypted, just signed to
|
801
|
+
prevent tampering. This means you should not store any secret data in the session
|
802
|
+
when using <tt>Rack::Session::Cookie</tt>.
|
803
|
+
|
804
|
+
For any cookie-based sessions, make sure that the necessary secrets (+:secret+ option)
|
805
|
+
are not disclosed to an attacker. Knowledge of the
|
806
|
+
secret(s) can allow an attacker to inject arbitrary session values. In the case of
|
807
|
+
<tt>Rack::Session::Cookie</tt>, that can also lead remote code execution.
|
788
808
|
|
789
809
|
=== Cross Site Request Forgery (CSRF)
|
790
810
|
|
@@ -0,0 +1,132 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A sessions plugin has been added that supports encrypted and
|
4
|
+
signed sessions. This plugin is now the recommended way to
|
5
|
+
implement sessions, replacing the previously recommended
|
6
|
+
Rack::Session::Cookie middleware.
|
7
|
+
|
8
|
+
The sessions plugin encrypts session data using the AES-256-CTR
|
9
|
+
cipher, and then signs the encrypted data with HMAC-SHA-256. By
|
10
|
+
doing this, attackers must be able to forge a valid HMAC before
|
11
|
+
they can try to exploit possible weaknesses in the encryption,
|
12
|
+
such as timing attacks during decryption that are dependent on
|
13
|
+
attacker chosen initialization vectors or ciphertext.
|
14
|
+
|
15
|
+
In addition to encryption and a stronger default signature
|
16
|
+
algorithm compared to Rack::Session::Cookie, the sessions
|
17
|
+
plugin has the following benefits:
|
18
|
+
|
19
|
+
* Built in session expiration enabeld by default, to mitigate
|
20
|
+
possible session replay issues (default: 30 days since session
|
21
|
+
creation, 7 days since last update).
|
22
|
+
|
23
|
+
* Padding by default to minimize information leakage due to
|
24
|
+
differing session data sizes (session data padded to
|
25
|
+
a multiple of 32 bytes by default before encryption).
|
26
|
+
|
27
|
+
* Automatic deflate compression of large sessions before
|
28
|
+
encryption (by default if session data is over 128 bytes).
|
29
|
+
|
30
|
+
* JSON is used for serialization instead of Marshal, preventing
|
31
|
+
remote code execution vulnerabilities if the session secret
|
32
|
+
is disclosed. Note that this means that many ruby types do not
|
33
|
+
round trip in the session, such as Symbol and Time instances.
|
34
|
+
This will probably be the largest barrier to adoption, as you
|
35
|
+
need to make sure your application only uses types that
|
36
|
+
round-trip through JSON before you start using the sessions
|
37
|
+
plugin.
|
38
|
+
|
39
|
+
* A plain hash is used for the session, instead of a hash-like
|
40
|
+
object. One consequence of this is that keys in the session
|
41
|
+
are not automatically converted to strings.
|
42
|
+
Rack::Session::Cookie converts session keys to strings for
|
43
|
+
keys at the top level, but not for keys in subhashes.
|
44
|
+
|
45
|
+
* In general sessions are smaller even if deflate compression is not
|
46
|
+
used, despite requiring 16 bytes for the cipher initialization
|
47
|
+
vector. The main reason for this is that the sessions plugin does
|
48
|
+
not set a session id, since one is not needed for cookie sessions.
|
49
|
+
|
50
|
+
* The sessions plugin requires a :secret option be set that is
|
51
|
+
at least 64 bytes, so that users have to make a determined
|
52
|
+
effort to use weak secrets.
|
53
|
+
|
54
|
+
* The HMAC calculation considers the cookie key, so that if the
|
55
|
+
same session secret is used for multiple applications with
|
56
|
+
different cookie keys, an attacker cannot use the session from
|
57
|
+
one application in a different application.
|
58
|
+
|
59
|
+
The sessions plugin ties into the Roda#session method instead
|
60
|
+
of being a rack middleware. This makes it about twice as
|
61
|
+
fast as Rack::Session::Cookie if the session is not accessed.
|
62
|
+
If the session is accessed, the sessions plugin is roughly
|
63
|
+
as fast as Rack::Session::Cookie, even though it uses a
|
64
|
+
stronger HMAC and has to encrypt and decrypt the session.
|
65
|
+
|
66
|
+
Because the sessions plugin is not a middleware, it does not
|
67
|
+
offer session support to other middleware, only to the app
|
68
|
+
itself. If you would like to use the same approach as the
|
69
|
+
sessions plugin uses but would like support for middleware to
|
70
|
+
access the sessions, a roda/session_middleware file has been
|
71
|
+
added. This file contains RodaSessionMiddleware, which is a
|
72
|
+
middleware that can be used by any other Rack app for session
|
73
|
+
support, and which uses a SessionHash class similar to the one used
|
74
|
+
by Rack::Session::Cookie.
|
75
|
+
|
76
|
+
To integrate with other plugins that can optionally use symbols
|
77
|
+
or strings in sessions, the sessions plugin sets the
|
78
|
+
:sessions_convert_symbols application option to true. Other plugins
|
79
|
+
can check for this application option, and if set, should use
|
80
|
+
strings instead of symbols in the session.
|
81
|
+
|
82
|
+
The sessions plugin should be loaded after the flash plugin if both
|
83
|
+
are used in the same application, so that the flash is rotated
|
84
|
+
correctly in the session.
|
85
|
+
|
86
|
+
* The middleware plugin now supports a :handle_result option, which
|
87
|
+
can be any callable object. If set, this object is called with the
|
88
|
+
environment of the request and the rack response after either the
|
89
|
+
Roda app or next middleware returns the rack response. The rack
|
90
|
+
response can be modified by the callable object, and the response
|
91
|
+
(after possible modification) will be returned to the previous
|
92
|
+
middleware. Example:
|
93
|
+
|
94
|
+
plugin :middleware, :handle_result=>(proc do |env, res|
|
95
|
+
res[1]['MyHeader'] = 'HeaderValue'
|
96
|
+
end)
|
97
|
+
|
98
|
+
* The :json_parser and :json_serializer application options are now
|
99
|
+
supported. If set, these options are used for parsing and
|
100
|
+
serializing JSON instead of the default of JSON.parse and .to_json.
|
101
|
+
|
102
|
+
= Other Improvements
|
103
|
+
|
104
|
+
* RodaRequest initialization is now faster by avoiding 1-2 method
|
105
|
+
calls.
|
106
|
+
|
107
|
+
* typecast_params.Integer in the typecast_params plugin now handles
|
108
|
+
numeric input as long the numeric input does not have fractional
|
109
|
+
parts. This makes it more usable when handling JSON input.
|
110
|
+
|
111
|
+
* If the flash is empty after the request is processed, the flash
|
112
|
+
session key is removed from the session instead of being left as
|
113
|
+
an empty hash. If addition to making the session smaller, this
|
114
|
+
makes the session appear empty if there are no other keys in the
|
115
|
+
session, which works better with the sessions plugin as empty
|
116
|
+
sessions will remove the session cookie completely.
|
117
|
+
|
118
|
+
= Backwards Compatibility
|
119
|
+
|
120
|
+
* The flash plugin now uses '_flash' instead of :_flash as the session
|
121
|
+
key. When using session middleware that uses
|
122
|
+
Rack::Session::Abstract::SessionHash to store the session (e.g.
|
123
|
+
Rack::Session::Cookie), session keys are converted internally to
|
124
|
+
strings, so this change will not affect you unless you are using
|
125
|
+
alternative session support. Even if your session does treat
|
126
|
+
:_flash different than '_flash' in keys, the plugin will still work
|
127
|
+
because it will try :_flash if there is no value for '_flash'. This
|
128
|
+
change was made to support the sessions plugin, which doesn't
|
129
|
+
convert keys to strings.
|
130
|
+
|
131
|
+
* This DEFAULT_PARSER and DEFAULT_SERIALIZER constants from the
|
132
|
+
the json_parser and json plugins have been removed.
|
data/lib/roda.rb
CHANGED
@@ -228,7 +228,7 @@ class Roda
|
|
228
228
|
# Add a middleware to use for the rack application. Must be
|
229
229
|
# called before calling #route to have an effect. Example:
|
230
230
|
#
|
231
|
-
# Roda.use Rack::
|
231
|
+
# Roda.use Rack::ShowExceptions
|
232
232
|
def use(*args, &block)
|
233
233
|
@middleware << [args, block].freeze
|
234
234
|
build_rack_app
|
@@ -372,7 +372,7 @@ class Roda
|
|
372
372
|
@scope = scope
|
373
373
|
@captures = []
|
374
374
|
@remaining_path = _remaining_path(env)
|
375
|
-
|
375
|
+
@env = env
|
376
376
|
end
|
377
377
|
|
378
378
|
# Handle match block return values. By default, if a string is given
|
@@ -670,7 +670,7 @@ class Roda
|
|
670
670
|
# The session for the current request. Raises a RodaError if
|
671
671
|
# a session handler has not been loaded.
|
672
672
|
def session
|
673
|
-
@env['rack.session'] || raise(RodaError, "You're missing a session handler
|
673
|
+
@env['rack.session'] || raise(RodaError, "You're missing a session handler, try using the sessions plugin.")
|
674
674
|
end
|
675
675
|
|
676
676
|
private
|
data/lib/roda/plugins/assets.rb
CHANGED
@@ -368,7 +368,7 @@ class Roda
|
|
368
368
|
|
369
369
|
if opts[:precompiled] && !opts[:compiled] && ::File.exist?(opts[:precompiled])
|
370
370
|
require 'json'
|
371
|
-
opts[:compiled] = ::JSON.parse(::File.read(opts[:precompiled]))
|
371
|
+
opts[:compiled] = (app.opts[:json_parser] || ::JSON.method(:parse)).call(::File.read(opts[:precompiled]))
|
372
372
|
end
|
373
373
|
|
374
374
|
if opts[:early_hints]
|
@@ -451,7 +451,7 @@ class Roda
|
|
451
451
|
if assets_opts[:precompiled]
|
452
452
|
require 'json'
|
453
453
|
::FileUtils.mkdir_p(File.dirname(assets_opts[:precompiled]))
|
454
|
-
::File.open(assets_opts[:precompiled], 'wb'){|f| f.write(assets_opts[:compiled]
|
454
|
+
::File.open(assets_opts[:precompiled], 'wb'){|f| f.write((opts[:json_serializer] || :to_json.to_proc).call(assets_opts[:compiled]))}
|
455
455
|
end
|
456
456
|
|
457
457
|
assets_opts[:compiled]
|
data/lib/roda/plugins/flash.rb
CHANGED
@@ -91,7 +91,8 @@ class Roda
|
|
91
91
|
# Access the flash hash for the current request, loading
|
92
92
|
# it from the session if it is not already loaded.
|
93
93
|
def flash
|
94
|
-
|
94
|
+
# :_flash to support transparent upgrades from previous key
|
95
|
+
@_flash ||= FlashHash.new(session['_flash'] || (session['_flash'] = session.delete(:_flash)))
|
95
96
|
end
|
96
97
|
|
97
98
|
# If the routing doesn't raise an error, rotate the flash
|
@@ -100,7 +101,12 @@ class Roda
|
|
100
101
|
res = super
|
101
102
|
|
102
103
|
if f = @_flash
|
103
|
-
|
104
|
+
f = f.next
|
105
|
+
if f.empty?
|
106
|
+
session.delete('_flash')
|
107
|
+
else
|
108
|
+
session['_flash'] = f
|
109
|
+
end
|
104
110
|
end
|
105
111
|
|
106
112
|
res
|
data/lib/roda/plugins/json.rb
CHANGED
@@ -53,8 +53,6 @@ class Roda
|
|
53
53
|
#
|
54
54
|
# plugin :json, content_type: 'application/xml'
|
55
55
|
module Json
|
56
|
-
DEFAULT_SERIALIZER = :to_json.to_proc
|
57
|
-
|
58
56
|
# Set the classes to automatically convert to JSON, and the serializer to use.
|
59
57
|
def self.configure(app, opts=OPTS)
|
60
58
|
classes = opts[:classes] || [Array, Hash]
|
@@ -63,7 +61,7 @@ class Roda
|
|
63
61
|
app.opts[:json_result_classes].uniq!
|
64
62
|
app.opts[:json_result_classes].freeze
|
65
63
|
|
66
|
-
app.opts[:json_result_serializer] = opts[:serializer] || app.opts[:json_result_serializer] ||
|
64
|
+
app.opts[:json_result_serializer] = opts[:serializer] || app.opts[:json_result_serializer] || app.opts[:json_serializer] || :to_json.to_proc
|
67
65
|
|
68
66
|
app.opts[:json_result_include_request] = opts[:include_request] if opts.has_key?(:include_request)
|
69
67
|
|
@@ -12,7 +12,6 @@ class Roda
|
|
12
12
|
# header for the request includes "json".
|
13
13
|
module JsonParser
|
14
14
|
DEFAULT_ERROR_HANDLER = proc{|r| r.halt [400, {}, []]}
|
15
|
-
DEFAULT_PARSER = JSON.method(:parse)
|
16
15
|
|
17
16
|
# Handle options for the json_parser plugin:
|
18
17
|
# :error_handler :: A proc to call if an exception is raised when
|
@@ -32,7 +31,7 @@ class Roda
|
|
32
31
|
# only wrap values that are not already hashes.
|
33
32
|
def self.configure(app, opts=OPTS)
|
34
33
|
app.opts[:json_parser_error_handler] = opts[:error_handler] || app.opts[:json_parser_error_handler] || DEFAULT_ERROR_HANDLER
|
35
|
-
app.opts[:json_parser_parser] = opts[:parser] || app.opts[:json_parser_parser] ||
|
34
|
+
app.opts[:json_parser_parser] = opts[:parser] || app.opts[:json_parser_parser] || app.opts[:json_parser] || JSON.method(:parse)
|
36
35
|
app.opts[:json_parser_include_request] = opts[:include_request] if opts.has_key?(:include_request)
|
37
36
|
|
38
37
|
case opts[:wrap]
|
@@ -69,10 +69,15 @@ class Roda
|
|
69
69
|
# application that the current request is a middleware request.
|
70
70
|
# You should only need to override this if you are using multiple
|
71
71
|
# roda middleware in the same application.
|
72
|
+
# :handle_result :: Callable object that will be called with request environment
|
73
|
+
# and rack response for all requests passing through the middleware,
|
74
|
+
# after either the middleware or next app handles the request
|
75
|
+
# and returns a response.
|
72
76
|
def self.configure(app, opts={}, &block)
|
73
77
|
app.opts[:middleware_env_var] = opts[:env_var] if opts.has_key?(:env_var)
|
74
78
|
app.opts[:middleware_env_var] ||= 'roda.forward_next'
|
75
79
|
app.opts[:middleware_configure] = block if block
|
80
|
+
app.opts[:middleware_handle_result] = opts[:handle_result]
|
76
81
|
end
|
77
82
|
|
78
83
|
# Forwarder instances are what is actually used as middleware.
|
@@ -102,10 +107,14 @@ class Roda
|
|
102
107
|
end
|
103
108
|
|
104
109
|
if call_next
|
105
|
-
@app.call(env)
|
106
|
-
else
|
107
|
-
res
|
110
|
+
res = @app.call(env)
|
108
111
|
end
|
112
|
+
|
113
|
+
if handle_result = @mid.opts[:middleware_handle_result]
|
114
|
+
handle_result.call(env, res)
|
115
|
+
end
|
116
|
+
|
117
|
+
res
|
109
118
|
end
|
110
119
|
end
|
111
120
|
|
@@ -26,9 +26,11 @@ class Roda
|
|
26
26
|
# sessions, so if the attacker has the ability to read cookie data
|
27
27
|
# and you are using Rack::Session::Cookie, it will still be possible
|
28
28
|
# for an attacker to generate valid CSRF tokens specific to arbitrary
|
29
|
-
# request method and request path.
|
29
|
+
# request method and request path. Roda's session plugin uses
|
30
|
+
# encrypted sessions and therefore is safe even if the attacker can
|
31
|
+
# read cookie data.
|
30
32
|
#
|
31
|
-
#
|
33
|
+
# == Usage
|
32
34
|
#
|
33
35
|
# It is recommended to use the plugin defaults, loading the
|
34
36
|
# plugin with no options:
|
@@ -37,39 +39,39 @@ class Roda
|
|
37
39
|
#
|
38
40
|
# This plugin supports the following options:
|
39
41
|
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
42
|
+
# :field :: Form input parameter name for CSRF token (default: '_csrf')
|
43
|
+
# :header :: HTTP header name for CSRF token (default: 'X-CSRF-Token')
|
44
|
+
# :key :: Session key for CSRF secret (default: '_roda_csrf_secret')
|
45
|
+
# :require_request_specific_tokens :: Whether request-specific tokens are required (default: true).
|
46
|
+
# A false value will allow tokens that are not request-specific
|
47
|
+
# to also work. You should only set this to false if it is
|
48
|
+
# impossible to use request-specific tokens. If you must
|
49
|
+
# use non-request-specific tokens in certain cases, it is best
|
50
|
+
# to leave this option true by default, and override it on a
|
51
|
+
# per call basis in those specific cases.
|
52
|
+
# :csrf_failure :: The action to taken if a request fails the CSRF check (default: :raise). Options:
|
53
|
+
# :raise :: raise a Roda::RodaPlugins::RouteCsrf::InvalidToken exception
|
54
|
+
# :empty_403 :: return a blank 403 page (rack_csrf's default behavior)
|
55
|
+
# :clear_session :: Clear the current session
|
56
|
+
# Proc :: Treated as a routing block, called with request object
|
57
|
+
# :check_header :: Whether the HTTP header should be checked for the token value (default: false).
|
58
|
+
# If true, checks the HTTP header after checking for the form input parameter.
|
59
|
+
# If :only, only checks the HTTP header and doesn't check the form input parameter.
|
60
|
+
# :check_request_methods :: Which request methods require CSRF protection
|
61
|
+
# (default: <tt>['POST', 'DELETE', 'PATCH', 'PUT']</tt>)
|
62
|
+
# :upgrade_from_rack_csrf_key :: If provided, the session key that should be checked for the
|
63
|
+
# rack_csrf raw token. If the session key is present, the value
|
64
|
+
# will be checked against the submitted token, and if it matches,
|
65
|
+
# the CSRF check will be passed. Should only be set temporarily
|
66
|
+
# if upgrading from using rack_csrf to the route_csrf plugin, and
|
67
|
+
# should be removed as soon as you are OK with CSRF forms generated
|
68
|
+
# before the upgrade not longer being usable. The default rack_csrf
|
69
|
+
# key is <tt>'csrf.token'</tt>.
|
68
70
|
#
|
69
71
|
# The plugin also supports a block, in which case the block will be used
|
70
72
|
# as the value of the :csrf_failure option.
|
71
73
|
#
|
72
|
-
#
|
74
|
+
# == Methods
|
73
75
|
#
|
74
76
|
# This adds the following instance methods:
|
75
77
|
#
|
@@ -116,7 +118,7 @@ class Roda
|
|
116
118
|
# will not work unless you set the :require_request_specific_tokens option to
|
117
119
|
# false, which is a bad idea from a security standpoint.
|
118
120
|
#
|
119
|
-
#
|
121
|
+
# == Token Cryptography
|
120
122
|
#
|
121
123
|
# route_csrf uses HMAC-SHA-256 to generate all CSRF tokens. It generates a random 32-byte secret,
|
122
124
|
# which is stored base64 encoded in the session. For each CSRF token, it generates 31 bytes
|