› Ruby 勉強会 › Railsとcsrf_tokenとauthenticity_token
Railsとcsrf_tokenとauthenticity_token
2015年04月30日
$ ruby -v
ruby 2.0.0p643 (2015-02-25 revision 49749) [x86_64-linux]
$ rails -v
Rails 4.2.1
CSRF対策がdefaultでONになっています。
このとき、GETしたHTMLに、AUTHENTICITY_TOKENが埋め込まれるのですが、
ヘッダ内のmetaタグと、フォーム内のhidden属性のパラメータと、2箇所に埋め込まれています。
それぞれのTOKENは、値が異なるため、どちらを使っていいのか悩みます。
普通に考えると、送信フォームに埋め込まれたTOKENを使うのが自然だと思ってたが、
ブラウザの動きを観察していると、どうもmetaタグのTOKENを利用しているようだ。
調べてみると、値は異なるが、乱数がXORされており、
デコードしてみると、同じ値が取り出せることが分かった。
どうやら、metaタグを使っても、hiddenパラメータを使っても、
サーバ側では同じ扱いとなるようだ。
めでたし、めでたし。
https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/request_forgery_protection.rb#L312-L314
「Can't verify CSRF token authenticity」
TOKENが誤ってたり、古かったりするとエラーになります。
ruby 2.0.0p643 (2015-02-25 revision 49749) [x86_64-linux]
$ rails -v
Rails 4.2.1
# HTML
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="3qzVAnYwSX6VjH5NYY57cnokuwVBdfUmHo5zKIGYWvKSHOiJoqTNem3zjmK3+0idTczeYiVHyTJsg6sbXa1+DA==" />
<input type="hidden"
name="authenticity_token"
value="hWu4EQJayinqjvTqxRHVgRM5t6L7AiFDiC0X5rmU5RjJ24Wa1s5OLRLxBMUTZOZuJNHSxZ8wHVf6IM/VZaHB5g==" />
CSRF対策がdefaultでONになっています。
このとき、GETしたHTMLに、AUTHENTICITY_TOKENが埋め込まれるのですが、
ヘッダ内のmetaタグと、フォーム内のhidden属性のパラメータと、2箇所に埋め込まれています。
それぞれのTOKENは、値が異なるため、どちらを使っていいのか悩みます。
普通に考えると、送信フォームに埋め込まれたTOKENを使うのが自然だと思ってたが、
ブラウザの動きを観察していると、どうもmetaタグのTOKENを利用しているようだ。
調べてみると、値は異なるが、乱数がXORされており、
デコードしてみると、同じ値が取り出せることが分かった。
どうやら、metaタグを使っても、hiddenパラメータを使っても、
サーバ側では同じ扱いとなるようだ。
めでたし、めでたし。
https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/request_forgery_protection.rb#L312-L314
#!/usr/bin/ruby
require "base64"
token0="3qzVAnYwSX6VjH5NYY57cnokuwVBdfUmHo5zKIGYWvKSHOiJoqTNem3zjmK3+0idTczeYiVHyTJsg6sbXa1+DA=="
token1="hWu4EQJayinqjvTqxRHVgRM5t6L7AiFDiC0X5rmU5RjJ24Wa1s5OLRLxBMUTZOZuJNHSxZ8wHVf6IM/VZaHB5g=="
AUTHENTICITY_TOKEN_LENGTH=32
def xor_byte_strings(s1, s2)
s1.bytes.zip(s2.bytes).map { |(c1,c2)| c1 ^ c2 }.pack('c*')
end
def decode_mask(encoded_masked_token)
masked_token = Base64.strict_decode64(encoded_masked_token)
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)
Base64.strict_encode64(csrf_token)
end
p decode_mask(token0) # => "TLA9i9SUhAT4f/Av1nUz7zfoZWdkMjwUcg3YM9w1JP4="
p decode_mask(token1) #=> "TLA9i9SUhAT4f/Av1nUz7zfoZWdkMjwUcg3YM9w1JP4="
「Can't verify CSRF token authenticity」
TOKENが誤ってたり、古かったりするとエラーになります。
Posted by kanedayo at 23:31│Comments(0)