Home

へぼへぼCTO日記

PlackにおけるCSRFとDNS-Rebinding対策

  • 2009-12-20 (日)
  • perl
  • hatena count

最近のwebセキュリティ界隈ではCSRFやDNS-Rebindingが話題ですが、Plackでアプリケーションサーバを立ち上げる際にこれらの対策をどのように行うかについてまとめてみました。

まず、CSRF対策ですが、拙作のPlack::Middleware::RefererCheckを使うことにより、RefererのチェックによるCSRF対策が行えます。CSRF対策としては、onetime token方式も存在しますが、個人的にはRefererチェックが導入が楽で好きではあります。Refererを送信しないクライアントを対象にしたサービスを運営される方は別途onetime token方式の対策をおこなってください。

Plack::Middleware::RefererCheckの使い方はこのようになります。(SYNOPSYSからの抜粋)

use Plack::Builder;

builder {
  enable 'RefererCheck', host => 'www.example.com', same_scheme => 1, error_app => sub { [403, [], ['Forbidden']] };
  $app;
};

# or more simply(host from $env->{HTTP_HOST} and same_scheme => 0)
# this is vulnerabilly for DNS Rebinding
builder {
  enable 'RefererCheck';
  $app;
};

さて、コード中にも書いてあるように後者のチェック用hostをHTTP_HOSTから取得する方法はDNS-Rebindingに脆弱です。しかし前者のように固定hostを設定することにより、DNS-Rebindingへの対策も同時にできることになります。尚、onetime token方式ではXHRのsame originポリシーの崩壊によりDNS-Rebindingへの対策を単体で行うことはできません。

このようにRefererCheckを固定hostで行うことによりCSRF対策においてはDNS-Rebindingに対応できますが、CSRFから保護されるページ以外についても当然DNS-Rebindingでのアクセスははじきたいところです。apache等においてはname base virtual hostを活用することにより、期待するホスト名以外のアクセスをはじくことができますが、PlackでもPlack::App::URLMap(mount)を使うことにより同様のことが可能です。

use Plack::Builder;
builder {
  mount 'http://example.com/' => builder {
    mount '/app' => $app;
    mount '/admin' => $admin_app;
 }
}

このようにすることにより、example.com以外のHOSTでのアクセスは404となります。mountは上記の例のように、hostの指定とpath_infoの指定を同時に行えるもので、apacheでいうとVirtualhostとLocationを一緒にしたような機能になります。なので、上記のコードは下記のコードと等価です。

use Plack::Builder;
builder {
  mount 'http://example.com/app/' => $app;
  mount 'http://example.com/admin/' => $admin_app;
}

もちろんホスト名を違うものをmountできますのでPlack単体でVirtualhostな運用もできます。URLMap便利ですね。

Plack::Middleware::Header

  • 2009-12-03 (木)
  • perl
  • hatena count

Plack::Middleware::Headerをgithubに上げました。

これは、Plack::Middlewareレイヤーでレスポンスヘッダを設定するもので、Apacheでいうところのmod_headersに相当します。

使い方はこんな感じです。

use Plack::Builder;
builder {
    enable 'Header',
        set => ['X-Plack-One' => '1'],
        append => ['X-Plack-Two' => '2'],
        unset => ['X-Plack-Three'];
    $app;
};

自分はPlack::Middleware::Staticと併用してExpiresをつけたりするのに使うために作りました。IEがExpires: -1とかやっておかないとしばらくキャッシュしてIf-Modifled-Sinceなリクエストも投げてくれなくなっちゃうという弊害があったので。

あと、IE関連では、If-Modified-Sinceの値に”;length=***”という余計なものが付いてきてしまい(ここここを参照)、Middleware::ConditionalGETを有効にしても304を吐けなかったりしていたので、それを取り除くためのMiddlewareも書いてみました。こちらは名前とかがきもいのでgistレベル。

追記(2009-12-13 21:27): cpanにアップしました。あと、If-Modified-SinceのIEバグの件はmiyagawaさんがConditionalGET側で対応してくれました

patternがemptyな場合には前回成功時のpatternが使われるというハマりポイント

  • 2009-11-26 (木)
  • perl
  • hatena count
print "match1\n" if "baz" =~ m{};
"foo" =~ m{foo};
print "match2\n" if "baz" =~ m{};

の結果は

match1

である。なぜならperldoc perlrerefに書いてある通り、

If 'pattern' is an empty string, the last successfully matched regex is used.

であるから。

patternを動的に生成してたりする時に空になっちゃうケースなんかがあると思うので、その場合はマッチ処理自体を回避するようにしたほうがよいかと思われます。

最初バグかと思いました。

Casual Talks#1でLTしてきました

  • 2009-11-21 (土)
  • perl
  • hatena count

昨日(11/20)にperl-casualのCasual Talks#1にて拙作のFormValidator::NestedについてLTしてきました。

slideをslideshareにあげておきました。

得られた教訓
1. 実際に話し始めると早口気味になるのでスライド大目ぐらいが自分にはちょうどいいかも
2. プロジェクターにつなぐときはねじをちゃんとしめておく
3. やっぱり笑いを一つぐらいとっといたほうが場は暖かくなる
(PAUSE ID requestの際にcatalyst-pluginを作りたいと書いていたと言ったときに若干嘲笑気味の笑いはありましたがw)

自分以外も発表が初めてだという方も多かったのに、発表が皆さんうまくて非常に面白かったです。またの機会があったら今回の教訓を踏まえまた発表できたら幸いです。

主催されたyusukebeさん、会場の用意や二次会の(実質)幹事をされたnobjasさんお疲れ様でした。

JSocketを使ってPOSTもストリーミングするPlackアプリ

  • 2009-10-20 (火)
  • perl
  • hatena count

さて、前エントリJSocketというのを作ったと書いたのですが、これは実はjavascriptで動くtwitter streamクライアントを作るならばmultipart/mixedを使うべきというのを読んで、レスポンスがpollではなくてストリーミングできるというのを知って、リクエストもやりたいよというふうに思ったのがきっかけでした。

まず、XHRでできないかを試してみたのですが、xhr.send(data)を一回読んでしまうと少なくともjavascript側ではリクエストは完了したと思ってしまって、再度xhr.send(data)しても反応はありませんでした。

そこでJSocketの登場です。JSocketを使うとhttpリクエストを断続的に送信することができます。やったですね。

で、Plack::Server::Coroを使って試しに
リアルタイムチャット
を作ってみました。どうぞお試し下さい。レスポンスもJSocketを使っていますのでmultipartに比べてboundary分お得だったりもします。テキストエリアに入力された文字がストリーミングで送信され、また他人のデータもストリーミングで受信できていると思います。ちなみに自分の発言もサーバから持ってきてるので一人でも確認できます。

ちなみにこれを1つのコネクションでやってしまおうというのがWebsocketなんだろうという認識ですね。ただwebsocketはhttpの上に一応のってますがハンドシェイク以外の部分はhttp/1.0やhttp/1.1的には正しくないでしょうし、JSocketでの二重接続であれば正しいhttpとしてふるまえるはずで、現行のapacheやproxy等でも扱える可能性があったりします。

あ、チャットのサンプルコードもgithubにあげてあります。

plackup -s Coro --app chat.psgi

という感じでPlack::Server::Coroをお使いください。

javascriptからsocketが扱えるJSocketを作りました

javascriptからsocketを扱いたいなぁという事象が発生したんですが(それについては別エントリで)、SocketJSというのがあるんですがDownloadするところがなぜかSSLになっていてCAがCAcertでfirefoxだとデフォルトでは見れないので、かっとなって自分で作ってみました。今も反省していません。

コードはgithubからどうぞ。
使い方はjs/jsocket.jsに軽く書いてあります。

/*
 * Jsocket - Socket on Javascript
 * Author: Masahiro Chiba <nihen@megabbs.com>
 * Depends:
 *  - jQuery: http://jquery.com/
 *  - jQuery TOOLS - Flashembed: http://flowplayer.org/tools/flashembed.html
 * SYNOPSIS:
 *  JSocket.init('/static/JSocket.swf', function () {
 *     socket = new JSocket({
 *         connectHandler: connectHandler,
 *         dataHandler:    dataHandler,
 *         closeHandler:   closeHandler,
 *         errorHandler:   errorHandler
 *     });
 *     socket.connect(location.hostname, location.port || 80);
 *  });
 *  function connectHandler() {
 *      socket.write("GET / HTTP/1.0\x0D\x0A\x0D\x0A");
 *      socket.flush();
 *  }
 *  function dataHandler(data) {
 *      alert(data);
 *      socket.close();
 *  }
 *  function closeHandler() {
 *      alert('lost connection')
 *  }
 *  function errorHandler(errorstr) {
 *      alert(errorstr);
 *  }
 *
 * */

こんな感じですね。flashの力を借りているので、Flashのソケットポリシーファイルとかを参考にポリシーファイルを配布するサーバを立ち上げておいてから使ってください。まる。

mod_psgi試してみた

  • 2009-10-16 (金)
  • perl
  • hatena count

mod_psgi をインストールしてみたをみて速くなるなら試してみようということでmod_psgiを試してみますた。

まずはこんな感じの簡単なPSGIアプリで。

my $app = sub {
    return ['200', [Content-Length => '5'], ['hello']];
};
ab -n 10000 -c 100
mod_psgi
 Requests per second:    1403.51 [#/sec] (mean)
mod_perl
 Requests per second:    668.33 [#/sec] (mean)
Standalone
 Requests per second:    702.46 [#/sec] (mean)
Standalone::Prefork
 Requests per second:    725.78 [#/sec] (mean)

おおおお。超絶早いですね!
(plackupの際に-E product(development以外ならなんでもおk)をつけないとだいぶ遅くてそれをそのまま載せてたのを修正しました)

で、お次は、Catalyst::Engine::PSGIを使ってCatalystのhello worldなアプリを。

catalyst.pl Hello

で作って、debugモードを切って、Root.pmのindexで

    $c->response->content_type('text/plain');
    $c->response->body( 'hello' );

こんな感じにしたもの。

ab -n 1000 -c 10
mod_psgi
 Requests per second:    207.87 [#/sec] (mean)
mod_perl
 Requests per second:    144.94 [#/sec] (mean)
cat-standalone
 Requests per second:    184.95 [#/sec] (mean)

おおおおお。早いですね!!これはこれだけでも乗り換える意味がありそうだ。と思ってたんですが現在実運用中のCatalystアプリでやってみたところ”child pid 32320 exit signal Segmentation fault (11)”とかいって死にました。うーむ。これは再現する最小構成を探ってid:spiritlooseさんにフィードバックしたいところですね。

casual-perlerな人たちは今持ってるCatalystアプリをmod_psgi+Catalyst::Engine::PSGIで動かしてどんどんフィードバックするといいんじゃないかな!

Archer and Git

  • 2009-10-16 (金)
  • git
  • hatena count

最近会社のレポジトリ管理も順次gitに移行しようとしてるのですが、今までArcher&svnでデプロイしていたものをgit用にarcher.ymlを書き換えしてみた。

汎用的にはこんな感じ。中央サーバのレポジトリにreleaseというブランチを作っておいて、作業者もそれの追跡ローカルブランチなreleaseを作っておくというのが前提。releaseに直接コミットは厳禁。

git stash saveがあるから作業ディレクトリを別に用意する必要ないかなぁとか思ってこんな形にしてみた。今のところ満足してるけど運用しているうちになんか不具合がでるかもしれない。

Acme::Coro::Sukeをリリースしました

  • 2009-10-12 (月)
  • perl
  • hatena count

多分、5人以上の人が思いついたけどあまりのくだらなさに作成を断念したといううわさのAcme::Coro::Sukeを先ほどCPANにリリースしました。(githubにもあげてあります)

これは、Coroのasyncと基本的には同じ動作をするbenzoというブロック定義ができるようになるもので、benzoブロックにCoroがスレッドを切り替えるたびに、「うわぁ…べんぞうさんの中…すごくあったかいナリぃ… 」とコロちゃんが囁やいてくれるモジュールです。
一番簡単な使い方は以下のようになります。

use Coro;
use Acme::Coro::Suke;
benzo {
};
cede;

enjoy Acme::Coro::Suke!

CPANに上げる三つめのモジュールがAcmeモジュールというのはこれはひどい。

Re: PSGI Implのsendfileについて

  • 2009-10-07 (水)
  • perl
  • hatena count

PSGI Implのsendfileについてですが、自分の今のところのイメージを書いておくです。

on Plack::Server

  • $res->[2]がGLOBだったら fileno($res->[2])して sendfile に fd を渡す
  • $res->[2]->can(‘fileno’) が生えてたら、$res->[2]->filenoからfdを取って使う
  • $res->[2]->can(‘path’) が生えてたら、$res->[2]->pathからファイルパスを取って使う

$env->{psgix.sendfile}を使わない以外は一緒

on app

realfileをserveしたい時は

  • GLOBを返す
  • ->pathを生やしたIO-Handle-likeなオブジェクトを返す

の二種類の方法があって、後者のほうがあらゆるサーバで最適化される可能性がある。かといって前者だからといってクライアントに届くresponseが変わることはないと期待される。後者において->filenoの実体のファイルと->pathが違うものだった場合の動作は未定義。

on middleware

  • Plack::Middleware::XSendfile
    responseから->pathが取れる場合にはそれをヘッダのX-Sendfileにセットするようなmiddleware。これのpsgix.sendfileサポートを無くしたやつのイメージ。
    X-Sendfileヘッダをセットする場合というのはbackendがlightyの場合とかだと思うけど、それをセットしていいかどうかはPlackは知り得ない。かといってappでセットしていて必要のない時に外にファイルPATHが出ていくものいくない。というわけでappでは->path付きオブジェクトを返すだけにして、このmiddlewareをbackendにあわせて付けたり外したりする。Plack::Server::*のsendfile(2)サポートとかとはまったく関係無い。
  • Plack::Middleware::GuessPath
    GLOBからファイルPathを推測して->path付きのオブジェクトに変換しちゃうMiddleware。マルチプラットフォーム対応が課題。Plack::Middleware::XSendfileより先に実行する。Plack::Server::*のsendfile(2)サポートとも関係する。


追記(10/7 17:24): 大体ここに書いてある感じで追加されたみたいです。詳しくは本家specのchangesetをご覧ください。Plack::Middleware::XSendfileも追加されたみたいです。miyagawaさん仕事ハヤス、、、。

Home

Search
Feeds
Meta

Return to page top