CSRFの動きと対策を確認する(Python)

セキュリティのCSRF(クロスサイトリクエストフォージェリ)の動きと対策を確認します。

確認環境
・Python 3.8.5
・Windows 10

目次

サンプル CSRF(クロスサイトリクエストフォージェリ)とは
  サンプルイメージ

CSRF(クロスサイトリクエストフォージェリ)とは

  • CSRFは、以下のような手順です。
    1.あるサイトAにログインしてセッションを保持している状態とします。
    2.悪意のあるURLをクリックし、そのURLのサーバーのプログラムからサイトAにアクセスしにいきます。
    3.そのサイトAがCSRF対策をしていない場合、セッションはあるのでアクセスを受け付けて動作してしまいます。
  • CSRF対策としてサイトAは、そのアクセスがどこから来たのか確認する必要があります。
  • 画面表示時にアクセストークンやワンタイムパスワード等を発行してクライアント側に送信し、再度のサイトへのアクセス時はサーバー側の値と照合して確かめるようにします。

サンプルイメージ

PythonのBottleフレームワークでCSRF対策をしたサンプルイメージです。

# coding: utf-8
from bottle import route,run,get,request,app
from beaker.middleware import SessionMiddleware
import secrets

session_opts = {
    'session.type': 'file',
    'session.cookie_expires': 300,
    'session.data_dir': './data',
    'session.auto': True
}
app1 = SessionMiddleware(app(), session_opts)
token = ''

# 初期表示
@route("/test1", method="GET")
def init():
    global token
    session1 = request.environ.get('beaker.session')
    session1['logged_in'] = True
    session1.save()
    rec = "first"
    token = secrets.token_urlsafe(32)
    html = '<form action="/display1" method="POST">'
    html += '<p>session:' + rec + '</p>'
    html += '<input name="token" type="hidden" value="' + token + '"/>'
    html += '<input value="登録" type="submit" /></form>'
    return html

@route("/display1", method="POST")
def display1():
    session1 = request.environ.get('beaker.session')
    if session1.get('logged_in'):
        if checkToken():
            rec = "OK"
        else:
            rec = "トークンが異なる"
    else:
        rec = "セッションなし"

    html = '<p>session:' + rec + '</p>'
    return html

def checkToken():
    return (secrets.compare_digest(token, request.forms.get("token")))

run(app=app1,host="127.0.0.1",port='8765')

初期表示時は、16行目から実行されます。
20行目は、セッションの'logged_in'にTrueをセットしています。

23行目は、secrets.token_urlsafeで文字列のトークンを出力しています。
トークンの文字列は、以下のようなイメージです。
CqUfJLENmVlaobL33T7kEV_YhDjr4qcio7x2ThbtAxU
26行目は、出力したトークンをhiddenのvalue値にセットしています。
27行目の登録ボタンを押すと30行目から実行されます。

33行目は、セッションのlogged_in'を判定しています。

CSRF対策の箇所

34行目で44行目が実行されます。
サーバーにあるトークン値と取得した画面から送信されたトークン値が同じ場合trueになり、異なる場合はfalseとして処理を分けます。

関連の記事

Python Bottleでセッションを発行して確認する

△上に戻る