m_shige1979のときどきITブログ

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

Github(変なおっさんの顔でるので気をつけてね)

https://github.com/mshige1979

MojoliciousでCSRFを実施

Mojoliciousでは内部実装可能

なんかtokenが同じ値なのでなんかおかしいかも…

サンプル

コントローラーのモジュール
package Sample::Web::Test1;
use Mojo::Base 'Mojolicious::Controller';
use Mojolicious::Validator;
use Mojolicious::Validator::Validation;

# This action will render a template
sub index {
  my $self = shift;

  # template変数を設定
  #$self->stash->{msg} = "index";

  $self->app->log->debug("index");

  # template配下のtest1.aaa.html.ep
  $self->render();

}

sub post {
  my $self = shift;

  # template変数を設定
  #$self->stash->{msg} = "index";

  # undefを設定してクリア
  $self->session->{csrf_token} = undef;

  $self->app->log->debug("new");
  #$self->app->log->debug($self->session->{csrf_token});

  if($self->session->{errors}){
    $self->app->log->debug("error !!!");
    $self->stash->{errors} = $self->session->{errors};
  }else{
    $self->stash->{errors} = undef;
  }
  $self->session->{errors} = undef;

  # template配下のtest1.aaa.html.ep
  $self->render('test1/post');

}

sub conf {
  my $self = shift;

  my $validation = $self->validation;
  my $messages;
  if ($validation->csrf_protect->has_error('csrf_token') ) {
    return $self->render_not_found();
  }

  if ($validation->required('title')->has_error('title') ) {
    push @$messages, 'title error !';
  }

  if ($validation->required('body')->has_error('body') ) {
    push @$messages, 'body error !';
  }

  my $cnt = scalar(@$messages);
  if($cnt > 0){
    $self->session->{errors} = $messages;
    $self->redirect_to('/test1/new');
  }

  # template変数を設定
  #$self->stash->{msg} = "index";

  $self->app->log->debug("conf");
  $self->app->log->debug($self->param('title'));
  $self->app->log->debug($self->param('body'));

  $self->stash->{title} = $self->param('title');
  $self->stash->{body} = $self->param('body');

  # template配下のtest1.aaa.html.ep
  $self->render();

}

sub create {
  my $self = shift;

  $self->redirect_to('/test1');

}

1;

※「$self->session->{csrf_token} = undef;」を設定することで毎回tokenの値を振り直し
※バリデータのモジュールでチェックなどを行う

CSRFは失敗時はエラー処理を実施するけど、入力データ不備の場合は前の画面へ戻す

テンプレート

% layout 'default';
% title 'Welcome';

% if ($errors) {
<b>Error!</b>
<ul>
% for my $error (@$errors) {
  <li><%= $error %></li>
% }
</ul>
% }

<h1>入力してpostする画面</h1>

<form action="./conf" method="post">
    <input type="hidden" name="csrf_token" value="<%= csrf_token %>" />
    <form role="form">
      <div class="form-group">
        <label for="title">タイトル</label>
        <input type="text" class="form-control" id="title" name="title" placeholder="タイトル">
      </div>
      <div class="form-group">
        <label for="body">Password</label>
        <input type="text" class="form-control" id="body" name="body" placeholder="本文">
      </div>
      <button type="submit" class="btn btn-default">確認する</button>
    </form>
</form>

csrf_tokenというヘルパーが存在するのでそれを使用してtokenを設定する

画面

CSRFのtokenを確認

f:id:m_shige1979:20140805000127p:plain

リロードしてから再度確認
f:id:m_shige1979:20140805000328p:plain

変わっている

tokenを変えた場合はエラー

f:id:m_shige1979:20140805000546p:plain

所感

チェックするのをコントローラーでやらずにモデルとかでやったりといろいろ工夫することを追加して対応してみる必要があります。基本的な部分を少しずつうめていって対応して行くようにする