前言:本文是學習網易微專業的《python全棧工程師 - Flask進階建站》課程的筆記,歡迎學習交流。同時感謝老師們的精彩傳授!
一、課程目标
- 什麼是CSRF
- 表單的CSRF保護
- AJAX的CSRF保護
二、詳情解讀
2.1.CSRF跨站攻擊
2.1.1.什麼是跨站攻擊
在會員管理中,要删除一個會員隻要通路
127.0.0.1:5000/admin/user/delete/1
,
有人會認為:這個連結你一定要登入才能通路,怎麼會有安全性問題呢
攻擊行為: 想辦法給管理者發一條删除連結,比如在圖檔中放置這個連結

管理者如果在登入狀态下點選此圖檔就會将員删除,攻擊成功。
這種攻擊行為稱為:
Cross-SiteRequestForgecy
- 跨站請求僞造,簡稱:
CSRF
如果改成表單送出會不會避免這個問題?
問題同樣存在,對于伺服器來說,如同不能識别身份一樣,也沒能識别資料的來源。
B站一樣可以送出登入資料完成登入,B站如果是一個僞站,那麼使用者就可能在不知情的情況下完成登入,進而帳号密碼被盜。
是以表單也不是安全的,必須想辦法在連結或者表單上做識别符号,就與使用者的身份識别一樣,給每個表單或者連結加上身份。通常用
csrf-token
辨別符來表示這個身份
2.2.表單的CSRF保護
2.2.1.CSRF保護
flask_wtf
提供了
csrf
防範能力,預設建立一個包含
csrf_token
值的隐藏域,
當使用
form.validate_on_submit()
方法驗證資料時,就會用
session
中的值與
csrf_token
的值進行比較,
如果兩者不一緻,那麼就認為表單是僞造的,拒絕處理。
2.3.ajax的CSRF保護
Step1
:
from flask_wtf.csrf import CSRFProtect
Step2
:建立執行個體
Step3
:
app
執行個體參數配置:
Step4
:在模版中使用
{{ csrf_token() }}
可以獲得
token
值
我們首先将通過連結删除修改為通過
ajax
删除
1.請求方式修改為
post
2.在
ajax
中設定:
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", {{ csrf_token() }})
}
3.視圖函數中使用
csrf.protect()
檢驗
csrf_token
值:
def deleteUser():
csrf.protect()
實操:
Step1
:在
libs.py
檔案中導入
csrfprotect
并建立執行個體
.
.
.
from functools import wraps
# 新增下面這一行
from flask_wtf.csrf import CSRFProtect
from flask_ckeditor import CKEditor
# 建立資料庫對象
# 這裡不用傳入app執行個體對象,因為這裡尚未建立app執行個體
db=SQLAlchemy()
# 新增下面這一行
csrf = CSRFProtect()
ckeditor = CKEditor()
.
.
.
Step2
:在
app.py
中導入
csrf
執行個體并初始化配置
from flask import Flask, render_template
from flask import request, redirect, url_for
# 導入下面一行中的csrf
from libs import db, ckeditor, csrf
from views.users import user_app
.
.
.
db.init_app(app)
ckeditor.init_app(app)
# 下面這一行初始化csrf
csrf.init_app(app)
.
.
.
Step3
:在
templates/login.html
中測試一下
.
.
.
</div>
</div>
<!-- 添加下面這個csrf_token() -->
{{ csrf_token() }}
</div>
{% endblock %}
</body>
</html>
第三步這裡,登入頁面會輸出一串字元串
Step4
:修改
templates/user/user_list.html
中的删除使用者連結,并新增
delUser()
函數:
.
.
.
<a href="{{ url_for(" target="_blank" rel="external nofollow" user_app.editUser", user_id=user.id) }}">修改</a>
<a href="#" onclick="delUser({{ user.id }})">删除</a>
.
.
.
<script>
function delUser(userid) {
$.ajax({
type: 'post',
url: '{{ url_for("admin_app.deleteUser")}}',
data: { user_id: userid},
dataType: 'json',
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", '{{ csrf_token() }}')
},
success: function(data) {
if (data.result == 'success') {
$('#user_' + userid).innerHTML = "<tr>" +
"<td colspan='6'>" + "删除成功" + "</td>" + "</tr>"
} else {
alert('删除過程發生錯誤,請重新嘗試')
}
}
})
}
</script>
{% endblock %}
</body>
</html>
Step5
:修改
admin/user.py
内容:
.
.
.
# 導入csrf
from libs import db, csrf
.
.
.
# 根據使用者id删除使用者, 新增methods
@admin_app.route('/user/delete', methods=['POST'])
def deleteUser():
# 新增下面這一行
csrf.protect()
user_id = int(request.form.get('user_id'))
user = User.query.get(user_id)
message = {}
try:
db.session.delete(user)
db.session.commit()
except:
message['result'] = 'fail'
else:
message['result'] = 'success'
return json.dumps(message)
.
.
.
三、課程小結
-
跨站攻擊csrf
-
csrf_token
-
發送ajax
csrf_token