網站是經過cookie來實現登陸功能的。而cookie只要存在瀏覽器中,那麼瀏覽器在訪問這個cookie的服務器的時候,就會自動的攜帶cookie信息到服務器上去。那麼這時候就存在一個漏洞了。若是你訪問了一個惡意網站,這個網站能夠在網頁源碼中插入JS代碼,使用JS代碼給其餘服務器發送請求。由於在發送請求的時候,瀏覽器會自動把cookie發送給對應的服務器,這時候相應的服務器就不知道這個請求是僞造的。從而達到在用戶不知情的狀況下,給某個服務器發送了一個請求。css
CSRF攻擊的要點就是在向服務器發送請求的時候,相應的cookie會自動的發送給對應的服務器。形成服務器不知道這個請求是用戶發起的仍是僞造的。這時候,咱們能夠在用戶每次訪問有表單頁面的時候,在網頁源碼中加一個隨機的字符串叫作csrf_token,在cookie中也加入一個相同值的csrf_token字符串。之後給服務器發送請求的時候,必須在body中以及cookie中都攜帶 csrf_token,服務器只有檢測到cookie中的csrf_token和body中的csrf_token都相同,才認爲這個請求是正常的。不然就是僞造的,那麼黑客就沒辦法僞造請求了。html
使用flask_wtf.CSRFProtect來包裹appjquery
import os from flask_wtf import CSRFProtect app.config['SECRET_KEY'] = os.urandom(24) #和session同樣,必需要配置一個secret key參與加密 CSRFProtect(app)
而後在模板form表單中添加一個input標籤,加載csrf_tokenajax
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
以上配置就可以在,用戶在提交表單後一併把表單中的csrf_token提交到服務器,並和服務器上的csrf_token作對比,這樣就可以識別請求是不是僞造的,從而避免CSRF攻擊flask
若是咱們的表單是經過AJAX提交處理的。示例以下瀏覽器
<head> ... <!--咱們這裏的AJAX代碼是用JQuery寫的,所以要引入Jquery--> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <!--AJAX代碼在login.js文件中--> <script src="{{ url_for('static', filename='login.js') }}"></script> </head> <body> <form action="" method="post"> <table> <tr> <td>郵箱:</td> <td><input name="email" type="text" /></td> </tr> <tr> <td>密碼:</td> <td><input name="password" type="text" /></td> </tr> <tr> <td></td> <td><input type="submit" value="提交" id="submit"/></td> </tr> </table> </form> </body>
login.js緩存
... class LoginView(views.MethodView): def get(self): return render_template('login.html') def post(self): email = request.form.get('email') password = request.form.get('password') if email == 'heboan@qq.com' and password == '123456': return '登陸成功' else: return '登陸失敗' app.add_url_rule('/login/', view_func=LoginView.as_view('login'))
以上咱們已經成功使用了AJAX處理表單。如今咱們啓用CSRF,主程序中代碼以下:服務器
import os from flask_wtf import CSRFProtect app.config['SECRET_KEY'] = os.urandom(24) #和session同樣,必需要配置一個secret key參與加密 CSRFProtect(app)
而後咱們再次用正確的帳號密碼登陸,發現出現錯誤,由於咱們啓用防csrf攻擊,可是AJAX那裏並無攜帶csrf_token,因此請求失敗cookie
所以,咱們須要在login.html中添加一個表單用來存放tokensession
<form action="" method="post"> <!--添加一個隱藏input用來放置csrf_token--> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <table> <tr> <td>郵箱:</td> <td><input name="email" type="text" /></td> </tr> <tr> <td>密碼:</td> <td><input name="password" type="text" /></td> </tr> <tr> <td></td> <td><input type="submit" value="提交" id="submit"/></td> </tr> </table> </form>
編輯AJAX代碼段,把表單中的csrf_token傳給服務器做比對
再次登陸(強制刷新瀏覽器,由於js有緩存)
csrf_token能夠放置的head中。在實際項目中,咱們通常也是放在頭部,這樣作有個好處就是:當多處須要用到csrf_token,咱們就不須要屢次寫一個隱藏的csrf_token表單。特別是在模板繼承的時候,咱們只要在父模板中的head中加入csrf配置,其子模板就能夠直接繼承了。
編輯login.html
<head> <!--在head中加上此配置--> <meta name="csrf-token" content="{{ csrf_token() }}"> ... </head> <!--表單中就不須要添加一個csrf_token表單了--> <form action="" method="post"> <table> <tr> <td>郵箱:</td> <td><input name="email" type="text" /></td> </tr> <tr> <td>密碼:</td> <td><input name="password" type="text" /></td> </tr> <tr> <td></td> <td><input type="submit" value="提交" id="submit"/></td> </tr> </table> </form>
新建一個heboan_ajax.js, 來封裝
修改login.js
最後在login.html中要引入封裝的js
<head> ... <meta name="csrf_token" content="{{ csrf_token() }}"> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script src="{{ url_for('static', filename='heboan_ajax.js') }}"></script> <script src="{{ url_for('static', filename='login.js') }}"></script> </head>
這樣實現的好處就是之後在其餘地方處理CSRF的時候,咱們直接把咱們封裝好的heboan_ajax拿來用就能夠了,模板那邊能夠經過繼承實現避免重複定義csrf_token表單和js引入