安全開發 | 如何讓Django框架中的CSRF_Token的值每次請求都不一樣


前言

用過Django 進行開發的同學都知道,Django框架天然支持對CSRF攻擊的防護,因為其內置了一個名為CsrfViewMiddleware的中間件,其基於Cookie方式的防護原理,相比基於session的方式,更適合目前前后端分離的業務場景,但美中不足的是,其生成的csrf_token在一個session周期中是不變,這對於一些特定的業務場景,顯然有點遺憾。

為了彌補這個遺憾,本文介紹一種不用修改CsrfViewMiddleware中間件源碼的方式,實現基於請求的csrf_token更新方式,詳文如下。

實現過程

1、Django csrf 校驗的兩個場景

在業務場景中,有兩種不同的csrf防護場景,一種是基於Form 表單提交數據的防護,一種是基於ajax 異步請求數據的防護。

對於Form 表單,可以通過在表單中內置`{% csrf_token %}` 實現在提交數據時一起攜帶csrf_token提交上去,從而通過后端csrf 校驗。

這里順便提下 Django模板引擎是如何渲染`{% csrf_token %}`的,其實就是將`{% csrf_token %}` 替換成csrf_input的返回值(這點可從Django 模板引擎源碼中找到),如下:

Django csrf 校驗的兩個場景

而get_token則是從request.META['CSRF_COOKIE']中獲取:

Django csrf 校驗的兩個場景

對於ajax 請求,需要在提交請求的時候,添加一個名為x-csrftoken的頭部(這個頭部是Django源碼中內定的),值為從cookie中提取的指定name的值,這個name可自定義,比如下圖為`csrf-bastion`:

Django csrf 校驗的兩個場景

2、Django csrf token 的生成流程

主要關注下CsrfViewMiddleware 中間件的process_view和process_response。

process_view的主要功能之一就是從請求的cookie中提取指定name的(通過settings.CSRF_COOKIE_NAME指定)cookie值為:csrf token,然后賦值給request.META['CSRF_COOKIE'],如果從請求頭中提取不到,則重新生成,如下圖:

Django csrf token 的生成流程

process_response 中會有更新csrf token cookie的功能,如下:

Django csrf token 的生成流程

3、 通過在視圖中修改request.META['CSRF_COOKIE']值實現csrf token 的更新

通過1和2我們就可以知道只要在在response返回之前更新request.META['CSRF_COOKIE']的值,便可以實現每次請求的csrf token 都不一樣,當然可以通過修改CsrfViewMiddleware中間件源碼的方式實現,不過這種方式的入侵性太大,最好的選擇是在視圖中修改,因為視圖處理流程是在response之前進行,如下:

通過在視圖中修改request.META['CSRF_COOKIE']值實現csrf token 的更新

這里的rotate_token則是CsrfViewMiddleware 中間件提供的更新csrf token的接口,如下:

通過在視圖中修改request.META['CSRF_COOKIE']值實現csrf token 的更新

總結

本文提到的這個方法確實可以實現基於請求對csrf_token值進行更新,而不是原先的基於session的,這樣更新之后,對於ajax請求倒是沒有什么問題,不過對於From表單中csrf_token值的更新是需要進行后端渲染更新的,對於前后端分離的請求,這個不足是可以讓后端提供個token 獲取接口來實現前端頁面中form表單的csrf_token值更新實現,當然也可以對form表單的提交行為進行監聽,然后異步提交,這樣直接走ajax那條路線,就沒啥問題了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM