net/http で Rails application の動作チェックをする ; POST

rb ファイルを使って Rails app に POST した時の挙動をチェックする

セキュリティ的にズル抜けなので、参考になる部分は薄いと断言


前提条件

rails g scaffold name:string email:string password:string

rake db:migrate

がされている。

次のレコードも用意されている。

name email password
matsu matsu@matsu.jp ustam
sqlite> select name, email, password from users;
matsu|matsu@matsu.jp|ustam

次のレコードを rb ファイルで POST して app 側で save してもらう

name email password
mat5L1 mat5L1@matsu.jp what

controller の準備

application_controller で、authenticity_token の verify を skip する。

これをしないと HTTP セッション がリセットされてしまう[1]

app/controller/application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  skip_before_filter :verify_authenticity_token
end

rb ファイルの作成

次のコードを書いて、実行する。

実行した結果

sqlite> select name, email, password from users;
matsu|matsu@matsu.jp|ustam
mat5L1|mat5L1@matsu.jp|what

コードの中で大まかにやっていることは以下の通り

  1. URI の Parse
  2. GET /users (index) 処理 (これを通さないと、POST できない <= session に token が無いから)
  3. Cookie 中の、Session に関る部分を取ってくる
  4. header に Cookie 情報など諸々入れる。
  5. POST /users (create) 処理
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require 'net/http'
require 'uri'

getAndPostUri = 'http://localhost:3000/users' # 1
uri = URI.parse(getAndPostUri)

res = Net::HTTP.start(uri.host, uri.port) { |http|
  response, data = http.get(uri.request_uri)        # 2
  cookie         = response.response['set-cookie']  # 3
  ######
  body = 'user[name]=mat5L1&user[email]=mat5L1@matsu.jp&user[password]=what'
  headers = {                                       # 4
    'Cookie'       => cookie,
    'Referer'      => getAndPostUri,
    'Content-Type' => 'application/x-www-form-urlencoded',
  }
  http.post(getAndPostUri, body, headers) # 5
}

[1]

何らかの突貫チェックぐらいなら、一時的にはいいかもしれない。

全コントローラに渡って verify_authenticity_token を skip しているので、色々とマズい。


ruby-2.0.0-p353\@mygemset/gems/actionpack-4.0.2/lib/action_controller/metal/request_forgery_protection.rb の中を見ると,次のようにすれば上手くいくらしい

"header に X-CSRF-Token が あり、それが form_authenticity_token と合致していること"

試行錯誤した物の(X-CSRF-Token に Base64.decode64/Marshal.load したりと)上手くいかず、結局こうなった

以下、そのコード

ruby-2.0.0-p353\@mygemset/gems/actionpack-4.0.2/lib/action_controller/metal/request_forgery_protection.rb

173       # The actual before_action that is used. Modify this to change how you handle unverified requests.
174       def verify_authenticity_token
175         unless verified_request?
176           logger.warn "Can't verify CSRF token authenticity" if logger
177           handle_unverified_request
178         end
179       end
180 
181       # Returns true or false if a request is verified. Checks:
182       #
183       # * is it a GET or HEAD request?  Gets should be safe and idempotent
184       # * Does the form_authenticity_token match the given token value from the params?
185       # * Does the X-CSRF-Token header match the form_authenticity_token
186       def verified_request?
187         !protect_against_forgery? || request.get? || request.head? ||
188           form_authenticity_token == params[request_forgery_protection_token] ||
189           form_authenticity_token == request.headers['X-CSRF-Token']
190       end

参考

CSRF

http://qiita.com/naoty_k/items/b40b13735fd7f06f8cb7

session/cookie

http://qiita.com/labocho/items/32efc5b7c73aba3500ff

net/http

http://www.rubyinside.com/nethttp-cheat-sheet-2940.html

http://dzone.com/snippets/custom-httphttps-getpost