「クロスサイトリクエストフォージェリ(CSRF)」とは
CSRF(クロスサイトリクエストフォージェリ)とは、Cross-Site Request Forgeriesの略で、別のサイトを通して(クロスサイト)、悪意のある要求(リクエスト)を正しい要求だと偽って(フォージェリ)を送信することです。
##CSRFの原理
上の図から、1回CSRFの攻撃をするなら、
1、信用サイトAにログインして、cookieを生成します。
2、Aサイトを閉じてないまま、危険サイトBに訪問します。
二つ要素が必要です。
危険性
CSRF脆弱性を具体例を用いて説明する。
今ある銀行のWebサイト(標的サイト)にログインしているユーザALICEが自身の口座からBOBという別のユーザの口座に100ドルを送金する際、ALICEのブラウザから
//bank.com/transfer.do?acct=BOB&amount=100 HTTP/1.1
```というHTTPリクエストが銀行のWebサイトに向かって送信されるものとする。
攻撃者MARIAは
```http://bank.com/transfer.do?acct=MARIA&amount=10000 HTTP/1.1
```というURLを公開掲示板に張ったり、不特定多数にメールしたりする。
銀行のユーザALICEがこのURLをクリックしてしまうと、ALICEのブラウザから
```GET http://bank.com/transfer.do?acct=MARIA&amount=10000 HTTP/1.1
```というHTTPリクエストが銀行に向かって送信される[7]。この際たまたまALICEが銀行にログインしていたら、銀行のサーバはこのリクエストを送金要求だと解釈してしまい、ALICEの口座からMARIAの口座に一万ドルが不正送金されてしまう。
## 対策
***1、http referer***
Referer が正しいリンク元かチェックする
![image.png](https://qiita-image-store.s3.amazonaws.com/0/310210/f0fa6fb4-b9bb-bf02-98ef-98b800d0da9d.png)
リファラを参照することで、
どこからそのページに要求が来たのかを知ることができます。
ブラウザから送信されてくるリンク元の情報である Referer を確認することで、
正しい画面遷移を行なってきていることを判断することでも CSRF の対策になります。
しかし、Referer の情報は、ユーザーの設定やセキュリティソフトなどにより情報が送信されないこともあるので、社内環境など環境と特定できる場合に利用することがよいかと思います。
***2、検証コード***
重要な情報が入力する場合、検証コードとかpasswordの入力を求めます。
***3、Tokens***
重要な処理を行うページに、hidden 項目(見えない項目)でトークンという秘密情報をセットして表示します。このとき、セッション情報としてトークンを保存しておきます。
そして、ユーザーが画面に必要情報を入力後送信処理を行うと、トークンも一緒に Webアプリケーションに送信されます。この送信されたトークンとセッションに保存しておいたトークンが等しければ、正しいリクエストだと判断できます。トークンが空だったり、値が異なればエラーとして処理をします。
## Flask-WTFのテストのCSRFトークンの扱い
- 1.バックエンドでcsrf_tokenを生成して、フロントでログインとか登録のルクエスト送る時に、csrf_tokenをフロントに送ります。
+ ファムに隠して送る
+ cookieの方法で送る
- 2.フロントで、またリクエストを送る時、csrf_tokenをバックエンドに渡します。
- 3.バックエンドで、フロントで持ってきたcsrf-tokenを検証します。
- 4.csrf-tokenの値が同じあれば、正しいリクエスト、トークンが空だったり、値が異なればエラーとして処理をします。
***全部のviewを保護するため、CsrfProtectモジュールを導入します***
from flask_wtf.csrf import CsrfProtect
CsrfProtect(app)
***テンプレートのファームにcsrf_tokenを入れます***
<form method="post" action="/">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>
csrf検証失敗のリクエストを処理します
@csrf.error_handler
def csrf_error(reason):
return render_template('csrf_error.html', reason=reason), 400
csrf検証が要らない場合
@csrf.exempt
@app.route('/foo', methods=('GET', 'POST'))
def my_handler():
# ...
return 'ok'
AJAX
テンプレートの< meta >にcsrf_tokenを渡します
<meta name="csrf-token" content="{{ csrf_token() }}">
テンプレートの< script >でも使えます
<script type="text/javascript">
var csrftoken = "{{ csrf_token() }}"
</script>
AJAX(ここでは< meta >の方法を使いました)
var csrftoken = $('meta[name=csrf-token]').attr('content')
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", csrftoken)
}
}
})