ましめも

技術系メモ

Railsのprotect_from_forgeryは必ずしも外部からのリクエストから守ってくれない

Railsお勉強中。ためしにログイン不要な掲示板作ってる
https://github.com/mashijp/railsboard

外部からトピック作成やレス作成をできないように対策することを「CSRF対策」っていうのかと思っていたが、ログイン前提じゃないとCSRFじゃないらしい…知らんかった…。Railsのprotect_from_forgeryはトークンのない更新系アクセスがあった場合セッションを消すだけなので、ログイン不要なサイトだと意味がない(警視庁等の通報ページも同様)。
※詳しくは» Rails3のprotect_from_forgeryはトークンの検証NGの場合にreset_sessionする TECHSCORE BLOGを参照


どうしようかなと頭を抱えていたけど、Railsソースコード(request_forgery_protection.rb)読んでたら簡単にオーバーライドできることが判明

      # This is the method that defines the application behavior when a request is found to be unverified.
      # By default, \Rails resets the session when it finds an unverified request.
      def handle_unverified_request
        reset_session
      end

ということでoverrideしました!

class ApplicationController < ActionController::Base
  protect_from_forgery

  protected
  def handle_unverified_request
    raise Exception("なんやこれ")
  end
end

https://github.com/mashijp/railsboard/commit/31d67c7803770219b8461a0266e46f76392f72b4
↑あとでまじめにやる

(タイトルは煽り)

Amazon CloudFrontを使ってみた

Amazon CloudFrontってなに

f:id:mashijp:20130114230342p:plain
http://aws.amazon.com/jp/cloudfront/
(語弊がありそうだけど)簡単に書くとキャッシュサーバ。
配信サーバ(Originと呼ばれる)のデータをキャッシュし、クライアントに効率的に配信する。日本やアメリカ、ヨーロッパなどにサーバがあり、クライアントの地理的条件に応じて使用するサーバを選択するので、レイテンシも小さい。

作成の仕方

アカウント登録した後は超カンタンだった!(といってもそのアカウント登録に時間がかかったんだけど…)

1. Account ConsoleのCloudFrontにアクセス
Create Distributionを押すと次の画面がでてくる
f:id:mashijp:20130114224803p:plain
今回は静的ファイルを配信したいのでDownloadを選択。

2. Originサーバの設定をする
f:id:mashijp:20130114224804p:plain
配信元のドメイン名をOrigin Domainに入力。

3. (必要なら)CNAMEの設定もする
標準だと"fugahogefuga.cloudfront.net"のようなドメイン名がふられるのだが、自分のドメイン名に設定することができる。
f:id:mashijp:20130114224800p:plain
やりたいならCNAMEに"example.mashijp.net"等を追加。

これで終わり。簡単。(CNAME設定したい場合は、作成後ドメイン名がふられるのでそれに対するCNAMEレコードを追加する)

料金は?

http://aws.amazon.com/jp/cloudfront/pricing/ にも書かれている通り、リクエスト数・転送量に応じた従量課金制。つまり、青天井です。DoSられるといくらでも料金がかかるのではという不安が常に押し寄せます。

何に使うの?

静的ファイル配信に便利なんじゃないかなあ、と思ったり。急激にアクセスが来た場合(or 見込まれる場合)は使うと良さそう。

(自分用メモ) playのevolutionファイルの番号飛ばしはダメ

1.sql, 2.sql, 999.sqlというファイルを作っても、999.sqlは見てくれない(2.sqlまでしか実行してくれない)
連番になってないと実行しない。(1.sql, 2.sql, 3.sql...)

ソース

Play Framework 2.1のちょっとした新機能

実はPlay Framework 2.0.xのFormでは同じフィールド名で複数値を受け取ることができない。

<input type="checkbox" name="name" value="value1">
<input type="checkbox" name="name" value="value2">
<input type="checkbox" name="name" value="value3">

例えば、上のようなフォームがあったとして、全てにチェックを入れても一つしか値は格納されない(多分name=value3しか入らない。もしかしたらvalue1かも)。

正確に書くと、request.body.asFormUrlEncodedの時点ではMap[String, Seq[String]]としてちゃんと格納されているのでこれを直接参照すれば"(name -> Seq(value1, value2, value3))"が得られるのだが、form.bindFromRequest()した瞬間に内部的にMap[String, String]に変換され、(name -> value3)となってしまう…。

POSTパラメータ name=value1&name=value2&name=value3
request.body.asFormUrlEncoded Map(name -> Seq(value1, value2, value3))
bindFromRequestした際の内部的なデータ Map(name -> value3)


一応、以下のようにフィールド名に"[番号]"をつけてあげれば複数の値を取得することができる。

<input type="checkbox" name="name[0]" value="value1">
<input type="checkbox" name="name[1]" value="value2">
<input type="checkbox" name="name[2]" value="value3">

コントローラ側の例

  case class AddForm(val name: List[String])

  val addForm = Form(
    mapping(
      "name" -> list(text)
    )(AddForm.apply)(AddForm.unapply)
  )

チェックボックスならこれで解決だが、複数選択可能なセレクトボックス(multiple要素をつけたselect)だとお手上げ。(どう転んでもnameは1つしか指定できないので)

しかし、Play Framework 2.1はこれが改善されており、末尾が"[]"で終わるフィールド名は自動的にname[0], name[1]に変換されるようになった!

POSTパラメータ name=value1&name=value2&name=value3
request.body.asFormUrlEncoded Map(name -> Seq(value1, value2, value3))
bindFromRequestした際の内部的なデータ Map(name[0] -> value1, name[1] -> value2, name[2] ->value3)

↑こんな感じに保存されてる

これで、mappingのlistが使い物になる!やったー (2.0.xでもやってほしい…)

Playに対応!IntelliJ IDEA 12 Ultimate

IntelliJ IDEA 12 Ultimateをマヤ文明特価に乗せられてたくさん買った。

View編集が便利

インデントが適切にされる

f:id:mashijp:20121223214242p:plain
前までは変にインデントされたり(されなかったり)してイライラしてたのが解消された!

入力補完が出る

f:id:mashijp:20121223214249p:plain
f:id:mashijp:20121223214253p:plain
ちゃんとテンプレート構文をわかってくれて、入力補完を出してくれる

git pullやpushでリモートのブランチ名を指定するのが面倒

新しいブランチを作成し、originにpushし共有するとき。

git checkout -b newbranch #新しいブランチを作成し、ついでにcheckout
#... コードの修正など
git push origin newbranch #リモートに反映

問題点

pushしたあとpullすると(他の人が同じブランチで作業などしてその変更を取り込みたい時など)次のようなエラーが発生する*1

$ git pull
You asked me to pull without telling me which branch you
want to merge with, and 'branch.newbranch.merge' in
your configuration file does not tell me, either. Please
specify which branch you want to use on the command line and
try again (e.g. 'git pull <repository> <refspec>').
See git-pull(1) for details.

If you often merge with the same branch, you may want to
use something like the following in your configuration file:
    [branch "newbranch"]
    remote = <nickname>
    merge = <remote-ref>

    [remote "<nickname>"]
    url = <url>
    fetch = <refspec>

See git-config(1) for details.

原因

エラーにも書かれているとおり、ブランチとリモート先との対応が設定されていないため。gitconfigに設定を追加するか、"git pull origin newbranch"のように明示的にリモートブランチを指定すればよい。

しかし、どっちの方法もめんどくさい。わずかな作業だが、めんどくさい。

もっと簡単な方法が

実はpushするときに-uオプションをつければ勝手に設定が追加される*2

git push -u origin newbranch

まとめ

ブランチ作成後の初回pushの際は-uオプションをつけよう

*1:ついでにいうと、"git push"を実行したときも同様のエラーが発生する

*2:git 1.7.x以降のみ

gitコマンド解説

自分で使ってるgitコマンドは、

  • git status
  • git clone
  • git checkout
  • git branch
  • git pull --rebase
  • git rebase --continue
  • git push
  • git flowコマンド

しかほとんど使ってないことに気づいた。(つまり、いつもgit flowコマンドに頼ってる)
gitコマンドを一から勉強しようと思う。