Skip to content

Instantly share code, notes, and snippets.

@rantler
Created July 3, 2019 00:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rantler/e99811b4126621bd5fa469a49fb6c3b2 to your computer and use it in GitHub Desktop.
Save rantler/e99811b4126621bd5fa469a49fb6c3b2 to your computer and use it in GitHub Desktop.
require 'base64'
class ValidateCsrfTokens
class << self
def validate(form_token:, cookie_token:)
decoded_form_token = Base64::decode64(form_token)
decoded_cookie_token = Base64::decode64(cookie_token)
form_token_length = decoded_form_token.bytes.length
cookie_token_length = decoded_cookie_token.bytes.length
if cookie_token_length != form_token_length
messages = [token_length_messages(cookie_token_length, 'cookie')]
messages << token_length_messages(form_token_length, 'form')
puts("FAILED: Token length mismatch (#{messages.compact})")
end
form_otp = decoded_form_token[0..31]
form_encrypted_bytes = decoded_form_token[32..-1]
cookie_otp = decoded_cookie_token[0..31]
cookie_encrypted_bytes = decoded_cookie_token[32..-1]
if xor_byte_strings(form_otp, form_encrypted_bytes) == xor_byte_strings(cookie_otp, cookie_encrypted_bytes)
puts("PASS: CSRF token checks out!")
else
puts("FAILED: Invalid CSRF token - decrypted bytes didn't match")
end
end
private
def token_length_messages(length, type)
case
when length < 64 then "#{type} too short"
when length > 64 then "#{type} too long"
end
end
# Copy/pasta from Rails internal code
def xor_byte_strings(s1, s2)
s2 = s2.dup
size = s1.bytesize
i = 0
while i < size
s2.setbyte(i, s1.getbyte(i) ^ s2.getbyte(i))
i += 1
end
s2
end
end
end
ValidateCsrfTokens.validate(
form_token: 'bm+aqMlmtq49D9SuxNtkwftnharM8yISYJe+rkCAeIcAgkxlpr6uh4SEyOZEueY9IcyCZKV/nu3x2etqt3ykng==',
cookie_token: 'CN46Ju+pu2X30xxqKZ9pYZfobje/zHgYnu+Vw7TgBXVmM+zrgHGjTE5YACKp/eudTUNp+dZAxOcPocAHQxzZbA=='
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment