3

次のようなCSRF攻撃を防ぐために、HTTPリクエストヘッダーとPOST隠しフィールドでCSRFトークンを送信しています。

var request;
var timeout;

function insert(callback)
{
    if(!request) 
    {
        CKEDITOR.instances.txtAboutProducts.updateElement();
        var contents=$("#txtAboutProducts").val();
        var csrf_token=$("#token").val();

        if(contents==null||contents=='')
        {
            alert("Please enter the contents.");
            return;
        }

        request = $.ajax({
            headers: { 
                    'Accept': 'application/json',
                    'Content-Type': 'application/json' 
                },
            datatype:"json",
            type: "POST",
            url: "../admin_side/AboutProducts.htm",
            data: JSON.stringify({"contents":contents, "token":csrf_token}),

            beforeSend: function (xhr)
            {
                xhr.setRequestHeader('X-CSRF-Token', csrf_token); 
            },
            success: function(response)
            {
                callback(response);
            },
            complete: function()
            {
                timeout = request = null;
            },
            error: function(request, status, error)
            {
                if(status!=="timeout"&&status!=="abort") // or just if(status==="error")
                {
                    alert(status+" : "+error);
                }
                callback(request);
            }
        });
        timeout = setTimeout(function() {
            if(request) 
            {
                request.abort();
                alert("The request has been timed out.");
            }
      }, 300000); //5 minutes.
    }
}

この関数は、CKEditor によって保持されている CMS コンテンツを、JSON を介して Spring への POST AJAX 呼び出しでデータベースに挿入することを目的としています (ボタンがクリックされたとき)。

if(!request){...}関数本体の先頭にある条件チェックは、(おそらくせっかちなユーザーによる) AJAX 呼び出しの重複を防ぐためのものです。

ランダムに生成されたトークン値は、ページが読み込まれ、JavaScript 変数で取得されるとすぐに非表示フィールドに格納されます。

var csrf_token=$("#token").val();

次のハンドラは、リクエストとともに送信されるヘッダーを設定します。

beforeSend: function (xhr)
{
    xhr.setRequestHeader('X-CSRF-Token', csrf_token); 
},

なぜヘッダーが必要なのですか? 隠しフィールドとして送信されたトークンだけでは十分ではありませんか? 古い Flash Player のため、ヘッダーを確認する必要がありますか (例として)?


このアプローチでは、サーバー側で、ヘッダーと非表示フィールドの両方の値が一致するかどうかを確認しています (セッション内のそのトークンの存在を確認するとともに、セッション内のトークンの存在を確認することはどこにもありませんただし、言及されています})。

上記の JavaScript 関数によって呼び出される Spring コントローラ クラスのメソッドは次のとおりです。

@RequestMapping(value=("admin_side/AboutProducts"), method=RequestMethod.POST)
private @ResponseBody CKEditorContentsHandler insert(@RequestBody final CKEditorContentsHandler object, final HttpServletResponse response, final HttpServletRequest request)
{
    if(object!=null&&StringUtils.isNotBlank(object.getToken())&&StringUtils.isNotBlank(request.getHeader("X-CSRF-Token"))&&sessionTokenService.isTokenValid(object.getToken())&&object.getToken().equals(request.getHeader("X-CSRF-Token")))
    {
        aboutProductsService.insert(object.getContents());
        object.setMessage("Insertion done successfully.");
        object.setStatus(1);
    }
    else
    {
        object.setMessage("The authentication token cannot be verified.");
        object.setStatus(-1);
    }
    return object;
}

ここCKEditorContentsHandlerで、このメソッドの最初のパラメーターは、ニーズを満たすためにいくつかのプロパティを保持する単純な Java クラスです。


トークンを Cookie に保存し、投稿されたデータ (隠しフィールド) と一緒に送信して、投稿されたデータと Cookie の値が一致するかどうかを確認することもお勧めします。そうでない場合は、CSRF の可能性があります (同じオリジン ポリシーにより、攻撃者は被害者のブラウザーで Cookie を読み取ったり変更したりできないため)。

その後、なぜヘッダーが必要なのですか? CSRF 攻撃を防ぐ (または少なくとも軽減する) ための推奨される方法は実際には何ですか?


次の回答はそれをうまく要約しています。

アンチ CSRF トークンと Javascript

4

0 に答える 0