7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Mojolicious のセキュリティ対策

Last updated at Posted at 2017-04-08

Mojolicious の XSS 対策

Mojolicious の XSS 対策は簡単で、テンプレートで <%= => 形式で記述すればエスケープで出力されます。
<%== => で書くとエスケープされない。

<input name="company_name" type="text"value="<%= $company_name %>">

XSS は javascript が画面に表示されて実行されることによる問題なので
出力時にエスケープすれば問題ありません。
HTML メールを送っている場合も同様です。
メールテンプレートでエスケープすればいい。

Mojolicious のセッション漏洩、セッションハイジャック対策

ご存知の方も多いと思いますが Mojolicious はセッション機能に問題があります。普通はセッションIDのみを cookie に保存しデータはサーバ側に保存しますが、Mojolicious の場合はデータをパスフレーズと組み合わせて Base64 にエンコードして cookie に保存しています。Base64 はデコード可能なのでパスフレーズが分かれば値が見えてしまいます。
また、セキュリティとは関係ないですがデータを cookie に入れているのでデータが大きいと正常に動かなくなります。

以下はデフォルトのままセッションを保存したものですが、様々な問題があることがわかります。
対策前.jpg

設定しだいである程度は対策できますが、cookie にデータそのものを保存するという仕様はどうにもできません。

この対策としては他のライブラリを使うことです。僕の場合はオーソドックスに CGI::Session を使いました。

use CGI::Session;
use CGI::Session::ExpireSessions;
use Mojolicious::Lite;

my $app = app;
my $session;

post '/send' => sub {
    my $c = shift;
    
    # セッションを読み込む
    &load_session($c);
    
    # 何らかの処理
};

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ■ セッション
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
sub load_session
{
	my $c = shift;
	
	# セッションを保存するディレクトリ
	my $session_dir = defined $c->app->config->{'session'}->{'session_dir'} ? $c->app->config->{'session'}->{'session_dir'} : 'session';
	# 有効期限
	my $expire = defined $c->app->config->{'session'}->{'expire'} ? $c->app->config->{'session'}->{'expire'} : 1200;
	# クッキー名
	my $cookie_name = defined $c->app->config->{'session'}->{'cookie_name'} ? $c->app->config->{'session'}->{'cookie_name'} : 'htwsid';
	# パス
	my $path = defined $c->app->config->{'session'}->{'path'} ? $c->app->config->{'session'}->{'path'} : '/';
	# cookieはSSLのみ
	my $secure = defined $c->app->config->{'session'}->{'secure'} ? $c->app->config->{'session'}->{'secure'} : 1;
	# cookieはhttpのみ
	my $httponly = defined $c->app->config->{'session'}->{'httponly'} ? $c->app->config->{'session'}->{'httponly'} : 1;
	
	# cookie名をセット
	CGI::Session->name($cookie_name);
	
	# 古くなったセッションファイルを削除 1h
	CGI::Session::ExpireSessions -> new(temp_dir => $session_dir, delta => 3600 ) -> expire_file_sessions();
	
	# GETリクエストによるセッションIDの指定の無効化
	$c->redirect_to('/') and exit if $c->param($cookie_name);
	
	# cookieからセッションIDを探す。なければ undef。
	my $sid = $c->signed_cookie($cookie_name) || undef;
	$session = CGI::Session->load(undef, $sid, { Directory => $session_dir }) or die CGI::Session->errstr();
	
	# 取得したセッションidが有効ならそのまま。無効(空or有効期限切れ)なら別のidを発番。
	if ( $session->is_empty || $session->is_expired )
	{
		$session = $session->new(undef, $sid, { Directory => $session_dir }) or die $session->errstr;
	}
	
	# cookieをセット
	$sid = $session->id();
	$c->signed_cookie( $cookie_name, $sid, { expires => time + $expire, path => $path, secure => $secure, httponly => $httponly } );
}

対策後の cookie の状態。
対策後.jpg

  • セッションIDのキー(クッキー名)をオリジナルにすることで使われているフレームワークを予測できなくします。
  • 保存されるセッションIDは72文字で、ブルートフォース攻撃されてもそれなりに時間がかかるでしょう。
  • 有効期限を短くすればブルートフォース攻撃でセッションIDが分かった頃には期限が切れて使えなくなっています。
  • cookie を HTTP 通信のみにすることで、仮に XSS 脆弱性があっても javascript で cookie(セッション)を盗めなくなります。
  • cookie を SSL 通信のみにすることでセッションIDが常に暗号化されるので、セッションIDが推測されにくくなります。
  • GET クエリでセッションIDを送ってきたらリダイレクトしてセッションIDを指定できないようにする。

Mojolicious の CSRF 対策

最新の Mojolicious では CSRF 対策がされているようです。
4.59から導入されたCSRF対策を使ってみる

サーバ環境の問題で古い Mojolicious を使う必要があったので、以下のプラグインをカスタマイズしました。
Mojolicious::Plugin::CSRFDefenderというのを作った

カスタマイズといっても、エラーだった場合の処理を変えているだけですが...

CSRFDefender.pm
#    # input check
#    $app->hook(after_static_dispatch => sub {
#        my ($c) = @_;
#        unless ($self->_validate_csrf($c)) {
#            my $content;
#            if ($self->error_template) {
#                my $file = file($self->error_template);
#                $content = $file->slurp;
#            }
#            else {
#                $content = $self->{error_content},
#            }
#            $c->render(
#                status => $self->{error_status},
#                text   => $content,
#            );
#        };
#    });

    # input check
    # CSRFエラーの場合はフォームに戻る
    $app->hook(after_static_dispatch => sub {
        my ($c) = @_;
        unless ($self->_validate_csrf($c)) {
            $c->redirect_to('/');
        };
    });

Mojolicious のセッション機能を使うので、Mojolicious でできるセッション対策を施しておきます。

# cookieを暗号化
app->secret('passphrase');
app->sessions->secure(1);
app->sessions->cookie_name('csrftoken');
7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?