現時点で最新のMojolicious「 4.59 」ではCSRF対策のメソッド諸々がようやく実装されました。以下Changesより引用。
- Added CSRF protection support.
- Added csrf_protect method to Mojolicious::Validator::Validation.
- Added csrf_token attribute to Mojolicious::Validator::Validation.
- Added csrf_token helper to Mojolicious::Plugin::DefaultHelpers.
- Added csrf_field helper to Mojolicious::Plugin::TagHelpers.
Mojoliciousデフォルトのセッションを使うのでPlack::Middleware::Sesssionその他を使っている場合は有効にならないケースがありますが、カジュアルにCSRF対策をするにはよいでしょう。また内部のコードを追えば独自のCSRF-Protectionなヘルパーなどを自作するキッカケになるかもしれません。今回はMojolicious::Liteで試せるサンプルをつくってみました。
ポイントは..
- テンプレートの中で
csrf_token
ヘルパーメソッドを呼び出してhidden-typeなフォームに入れる - ちなみにTagヘルパーを使う場合は
csrf_field
で一発 - Mojolicious::Validator::Validationの
csrf_protect
メソッドでtokenのバリデーションを行う - セッション
$self->session->{csrf_token}
にtokenが収められているのでそれを参照して独自にチェックもアリ
って感じです。ではサンプルのコードをば。ちょいと冗長に書いてある部分あるっす。
前半のコード
#!/usr/bin/env perl
use Mojolicious::Lite;
use Mojolicious::Validator;
use Mojolicious::Validator::Validation;
get '/' => sub {
my $self = shift;
$self->render('index');
};
post '/post' => sub {
my $self = shift;
my $validation = $self->validation;
my $messages;
if ($validation->csrf_protect->has_error('csrf_token') ) {
push @$messages, 'Invalid CSRF Token!';
}
if ($validation->required('text')->has_error('text') ) {
push @$messages, 'Text is required!';
}
$self->stash->{messages} = $messages;
$self->render('result');
};
app->start;
__DATA__
後半のコード
@@ index.html.ep
% layout 'default';
<form action="/post" method="post">
<input type="text" name="text" placeholder="Text" />
<input type="hidden" name="csrf_token" value="<%= csrf_token %>" />
<input type="submit" value="Submit" />
</form>
@@ result.html.ep
% layout 'default';
% if ($messages) {
<b>Error!</b>
<ul>
% for my $message (@$messages) {
<li><%= $message %></li>
% }
</ul>
% }else{
<b>Success!</b>
% }
@@ layouts/default.html.ep
<!DOCTYPE html>
<html>
<head><title>How about CSRF?</title></head>
<body><%= content %></body>
</html>
テンプレートのCSRF-tokenのinputタグの部分を削除するなどいじってみると挙動が変わって面白いですよ!