FuelPHPのAjaxでのCSRF対策

AjaxというかJavaScriptでのCSRF対策用のSecurity::js_fetch_token()メソッドがFuelPHPにはあって、ビューで

<?php
echo Security::js_fetch_token();
?>

とやると、CSRF対策のワンタイムトークンを取得するfuel_csrf_token()関数がJavaScriptで使えるようになります。

なので、デフォルトではfuel_csrf_tokenというキーでトークンをPOSTします。例えば、jQueryだと以下のようなコードになり、

$.post(
    '/post/create',
    {
        content: data,
        fuel_csrf_token: fuel_csrf_token()
    }
)
…略…

コントローラでは、普通にSecurity::check_token()メソッドでトークンをチェックすればいいです。

if (! Security::check_token()) {
    // トークンエラー
}

POSTじゃない場合も、トークンを抽出して

if (! Security::check_token($token)) {
    // トークンエラー
}

のように受け取ったトークンを引数に渡せばOKです。

ところで、Security::js_fetch_token()メソッドの生成するコードは、以下のようになっています。

<script type="text/javascript">
function fuel_csrf_token()
{
    if (document.cookie.length > 0)
    {
        var c_name = "fuel_csrf_token";
        c_start = document.cookie.indexOf(c_name + "=");
        if (c_start != -1)
        {
            c_start = c_start + c_name.length + 1;
            c_end = document.cookie.indexOf(";" , c_start);
            if (c_end == -1)
            {
                c_end=document.cookie.length;
            }
            return unescape(document.cookie.substring(c_start, c_end));
        }
    }
    return "";
}
</script>

Cookieからトークンの値を取得しています。なので、HttpOnlyなCookieだとJavaScriptからはトークンを取得できなくなり使えません。

なんかちょっと微妙なんですが、FuelPHPのCSRF対策のトークンはワンタイムトークンのためAjaxリクエストごとに値が変わります。連続してAjaxリクエストを送る可能性がある場合、他にうまく新しいトークンを取得する方法を思い付きません。

ワンタイムトークンをやめないと無理な気がします。

関連

Date: 2014/12/20

Tags: fuelphp, csrf, ajax