module Roda::RodaPlugins::Rails42::InstanceMethods
Rails 4.2 integration code, most code from Rails
Public Instance Methods
call()
click to toggle source
Calls superclass method
# File lib/roda/plugins/rails42.rb, line 23 def call catch(:halt) do rails = self.class.opts[:rails] r = request if instance_exec(r, &rails[:check_csrf]) unless valid_authenticity_token?(session, instance_exec(r, &rails[:csrf_token])) instance_exec(r, &rails[:invalid_csrf]) end end super end end
csrf_tag()
click to toggle source
# File lib/roda/plugins/rails42.rb, line 41 def csrf_tag "<input type='hidden' name='authenticity_token' value=\"#{masked_authenticity_token(session)}\" />".html_safe end
flash()
click to toggle source
# File lib/roda/plugins/rails42.rb, line 37 def flash env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"]) end
Private Instance Methods
compare_with_real_token(token, session)
click to toggle source
# File lib/roda/plugins/rails42.rb, line 95 def compare_with_real_token(token, session) ActiveSupport::SecurityUtils.secure_compare(token, real_csrf_token(session)) end
masked_authenticity_token(session)
click to toggle source
Creates a masked version of the authenticity token that varies on each request. The masking is used to mitigate SSL attacks like BREACH.
# File lib/roda/plugins/rails42.rb, line 50 def masked_authenticity_token(session) one_time_pad = SecureRandom.random_bytes(AUTHENTICITY_TOKEN_LENGTH) encrypted_csrf_token = xor_byte_strings(one_time_pad, real_csrf_token(session)) masked_token = one_time_pad + encrypted_csrf_token Base64.strict_encode64(masked_token) end
real_csrf_token(session)
click to toggle source
# File lib/roda/plugins/rails42.rb, line 99 def real_csrf_token(session) session[:_csrf_token] ||= SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH) Base64.strict_decode64(session[:_csrf_token]) end
valid_authenticity_token?(session, encoded_masked_token)
click to toggle source
Checks the client’s masked token to see if it matches the session token. Essentially the inverse of masked_authenticity_token
.
# File lib/roda/plugins/rails42.rb, line 60 def valid_authenticity_token?(session, encoded_masked_token) if encoded_masked_token.nil? || encoded_masked_token.empty? || !encoded_masked_token.is_a?(String) return false end begin masked_token = Base64.strict_decode64(encoded_masked_token) rescue ArgumentError # encoded_masked_token is invalid Base64 return false end # See if it's actually a masked token or not. In order to # deploy this code, we should be able to handle any unmasked # tokens that we've issued without error. if masked_token.length == AUTHENTICITY_TOKEN_LENGTH # This is actually an unmasked token. This is expected if # you have just upgraded to masked tokens, but should stop # happening shortly after installing this gem compare_with_real_token masked_token, session elsif masked_token.length == AUTHENTICITY_TOKEN_LENGTH * 2 # Split the token into the one-time pad and the encrypted # value and decrypt it one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH] encrypted_csrf_token = masked_token[AUTHENTICITY_TOKEN_LENGTH..-1] csrf_token = xor_byte_strings(one_time_pad, encrypted_csrf_token) compare_with_real_token csrf_token, session else false # Token is malformed end end
xor_byte_strings(s1, s2)
click to toggle source
# File lib/roda/plugins/rails42.rb, line 104 def xor_byte_strings(s1, s2) s1.bytes.zip(s2.bytes).map { |(c1,c2)| c1 ^ c2 }.pack('c*') end