python, Django csrf token的問題

環境

Window 7html

Python2.7python

Django1.4.1sql

sqlite3django

問題

在使用Django搭建好測試環境後,寫了一個提交POST表單提交留言的測試頁面。瀏覽器

如圖:bash

Django表單

填寫表單,點擊「提交留言」按鈕提交到服務器,卻出現服務器

Forbidden (403)cookie

CSRF verification failed. Request aborted.session

因爲以前使用GET方式提交表單內容測試均正常,就覺得這個問題估計是配置問題沒細看後邊的幫助提示直接在網上搜索解決方案。app

一搜索發現相關網頁不少,看來你們都遇到過這個問題,想着應該很快能解決。

解決方案1:失敗

在settings.py的MIDDLEWARE_CLASSES加入

'django.middleware.csrf.CsrfResponseMiddleware',

最終settings.py MIDDLEWARE_CLASSES 配置部分的代碼以下:

1
2
3
4
5
6
7
8
9
10
11
12
MIDDLEWARE_CLASSES = (
     'django.middleware.common.CommonMiddleware' ,
     'django.contrib.sessions.middleware.SessionMiddleware' ,
     'django.middleware.csrf.CsrfViewMiddleware' ,
     # add
     'django.middleware.csrf.CsrfResponseMiddleware' ,
     # add end
     'django.contrib.auth.middleware.AuthenticationMiddleware' ,
     'django.contrib.messages.middleware.MessageMiddleware' ,
     # Uncomment the next line for simple clickjacking protection:
     # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

保存,從新加載http://127.0.0.1/comment/add頁面提交留言測試。

但在打開頁面時出現500錯誤

Django表單

趕忙看了一下控制檯,發現以下錯誤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Traceback (most recent call last):
   File "D:\Python27\lib\wsgiref\handlers.py" , line 85, in run
     self.result = application(self.environ, self.start_response)
   File "D:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py" , l
ine 67, in __call__
     return self.application(environ, start_response)
   File "D:\Python27\lib\site-packages\django\core\handlers\wsgi.py" , line 219, i
n __call__
     self.load_middleware()
   File "D:\Python27\lib\site-packages\django\core\handlers\base.py" , line 51, in
  load_middleware
     raise exceptions.ImproperlyConfigured('Middleware module "%s" does not defin
e a "%s" class' % (mw_module, mw_classname))
ImproperlyConfigured: Middleware module "django.middleware.csrf" does not define
  a "CsrfResponseMiddleware" class
[12 /Sep/2012 11:00:35] "GET /comment/add/ HTTP/1.1" 500 59

大體的意思是我剛纔在settings.py的MIDDLEWARE_CLASSES內添加的

'django.middleware.csrf.CsrfResponseMiddleware',

這個模塊找不到,當時就有點鬱悶了。網上一大堆都說是添加這個模塊解決問題的,如今我本機上添加這個模塊之後卻提示沒有找到模塊?

爲此,我從新把Django從新安裝了一遍仍是提示找不到模塊。我只好到官網去看看手冊,才發現Django新版已去掉這個模塊。而個人Django正好是最新版本1.4只好放棄這個方案!

解決方案2:失敗

在視圖裏使用@csrf_protect修飾

因而我在views.py文件裏的add函數前加了@csrf_protect修飾符,以下代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Create your views here.
# coding=utf-8
from django.shortcuts import render_to_response
import datetime
 
@csrf_protect
def add(request):
     dict = {}
     if request.method = = "POST" :
         comment = request.POST.get( 'comment' ,'')
         submit_time = datetime.datetime.now().strftime( '%Y-%m-%d %H:%M:%S' )
         dict .setdefault( 'comment' ,comment)
         dict .setdefault( 'submit_time' ,submit_time)
     return render_to_response( "comment/add.html" , dict )

打開留言頁面發現又有一個錯誤提示

NameError at /comment/add/

name 'csrf_protect' is not defined

提示找不到我剛纔添加的修飾符@csrf_protect,應該是沒有導入相關模塊的問題,因而在個人視圖views.py頭部添加了一句代碼導入相關模塊

from django.views.decorators.csrf import csrf_protect

保存文件,從新打開網頁,錯誤已清除。心中一陣大喜,覺得OK了。誰知提交留言之後仍是提示

Forbidden (403)

CSRF verification failed. Request aborted.

有點急了,只好繼續搜索其它解決方案

解決方案3:失敗

在模板頁的from表單標籤內添加{% csrf_token %}

添加之後的代碼以下

Django表單

從新打開頁面測試,依舊提示:

Forbidden (403)

CSRF verification failed. Request aborted.

有點火大了!

解決方案4:成功

一番折騰不能解決問題,因而只好冷靜的查看錯誤頁面的提示幫助。

Django表單

第一個提示表示瀏覽器要開啓cookie,個人是IE9瀏覽器,毋庸置疑默認是開啓的。

第三個與第四個方案我都已測試過,惟獨第二個方案我沒有仔細研究。問題會不會在哪裏呢?因而到官網文檔尋找

The view function uses RequestContext for the template, instead of Context.

這句英文大體的意思是要在視圖裏使用RequestContext這個方法,最終在官網文檔找到了如下解決方案

在return render_to_response函數里加入context_instance=RequestContext(request),代碼以下:

return render_to_response("comment/add.html",dict,context_instance=RequestContext(request))

從新運行網頁,又提示新的錯誤

NameError at /comment/add/

global name 'RequestContext' is not defined

提示找不到RequestContext,估計是我沒有導入RequestContext模塊,因而在把

from django.shortcuts import render_to_response

改寫成

from django.shortcuts import render_to_response,RequestContext

視圖總體代碼以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Create your views here.
# coding=utf-8
from django.shortcuts import render_to_response, RequestContext
import datetime
from django.views.decorators.csrf import csrf_protect
 
@csrf_protect
def add(request):
     dict = {}
     if request.method = = "POST" :
         comment = request.POST.get( 'comment' ,'')
         submit_time = datetime.datetime.now().strftime( '%Y-%m-%d %H:%M:%S' )
         dict .setdefault( 'comment' ,comment)
         dict .setdefault( 'submit_time' ,submit_time)
     return render_to_response( "comment/add.html" , dict ,context_instance = RequestContext(request))

從新運行網頁正常,提交留言終於成功了

Django表單

回顧優化

雖然折騰了半天才解決,但仍是感受有點糊里糊塗的。根據以前報錯最後一條提示信息

If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as well as those that accept the POST data.

大體意思是若是settings.py的MIDDLEWARE_CLASSES裏不開啓CsrfViewMiddleware那麼就必需要在視圖裏使用@csrf_protect模塊修飾方法。我看看MIDDLEWARE_CLASSES裏的設置,代碼以下:

1
2
3
4
5
6
7
8
9
MIDDLEWARE_CLASSES = (
     'django.middleware.common.CommonMiddleware' ,
     'django.contrib.sessions.middleware.SessionMiddleware' ,
     'django.middleware.csrf.CsrfViewMiddleware' ,
     'django.contrib.auth.middleware.AuthenticationMiddleware' ,
     'django.contrib.messages.middleware.MessageMiddleware' ,
     # Uncomment the next line for simple clickjacking protection:
     # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

默認個人MIDDLEWARE_CLASSES已經給我開啓了CsrfViewMiddleware這個模塊。按照提示幫助,我能夠把視圖views.py裏的修飾模塊去掉應該也能夠,因而註釋了@csrf_protect修飾符與包含模塊語句,最終代碼以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Create your views here.
# coding=utf-8
from django.shortcuts import render_to_response, RequestContext
import datetime
# from django.views.decorators.csrf import csrf_protect
 
# @csrf_protect
def add(request):
     dict = {}
     if request.method = = "POST" :
         comment = request.POST.get( 'comment' ,'')
         submit_time = datetime.datetime.now().strftime( '%Y-%m-%d %H:%M:%S' )
         dict .setdefault( 'comment' ,comment)
         dict .setdefault( 'submit_time' ,submit_time)
     return render_to_response( "comment/add.html" , dict ,context_instance = RequestContext(request))

測試成功!

什麼是CSRF

問題是解決了,但我不由回想爲何在Django裏提交POST表單要配置那麼麻煩(其實也不麻煩,但對新手來講就不同了),因而搜索關鍵字看看,得知它是一種跨站請求僞造,黑客能夠利用這個攻擊站點。 而Django裏的使用這個機制就是防止CSRF模式攻擊,原理大體是當你打開頁面的時候產生一個csrftokey種下cookie,而後當你提交表單 時會把本地cookie裏的csrftokey值給提交服務器,服務器判斷只有有效的csrftokey值才處理請求。

既然這樣,我就查看了一下cookie信息,發現果然種了一個csrftokey值

Django表單

右鍵HTML頁面查看源代碼,發現{% csrf_token %}位置替換成了一個input隱藏值

Django表單

input隱藏標籤值與cookie裏的csrftoken值一致。

因而我作了一個測試,在from表單裏把{% csrftoken %}標籤直接替換成如上圖的input標籤,name與value保持一致,提交留言的時候服務器正常處理,測試成功。

不使用CSRF驗證

Django提供了POST表單使用CSRF驗證功能,感受仍是挺不錯的。但在Django裏能不能像普通的Form表單同樣不使用CSRF驗證功能呢?答案是確定能夠的。

一、我在settings.py的MIDDLEWARE_CLASSES把'django.middleware.csrf.CsrfViewMiddleware'註釋

二、移出FROM表單裏的{% csrf_token %}標記

三、不導入RequestContext模塊,並把render_to_response()裏的context_instance=RequestContext(request)去掉

從新運行網頁測試提交留言正常。至此,應該能夠判斷出上邊1步驟裏的'django.middleware.csrf.CsrfViewMiddleware'語句開啓了CSRF驗證功能,默認是開啓的,註釋之後就關閉CSRF驗證POST表單提交功能了。

支持一下(195)
相關文章
相關標籤/搜索