てぃーだブログ › 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

# 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が誤ってたり、古かったりするとエラーになります。


タグ :railsruby


Posted by kanedayo at 23:31│Comments(0)
上の画像に書かれている文字を入力して下さい
 
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。