目前你的
polls.urls
模組中,每個 url
都設定了 name
:

有沒有想過,如果在不同 App 的
urls
模組中,也有重複的 name
設定值該怎麼辦?實際上,你可以為每個 App 的 urls
模組設定不同的名稱空間(Namespace),來避免名稱衝突的問題發生,在接下來的練習 13 中,也要來看看如何建立一個簡易表單。
練習 13:建立 URL 名稱空間與簡易表單
在目前的 mysite/urls.py 檔案中,在include
函式上增加 namespace='polls'
:

接下來,你就可以在模版中使用這個名稱空間設定,例如,修改 polls/index.html 模版:

接著要來建立一個簡易表單了,修改 polls/detail.html,如下包括 HTML 的
<form>
標籤內容:

在 polls/views.py 中增加以下內容與修改
results
及 vote
,讓 results
可以根據請求的 poll_id
與指定的模版檔案繪製畫面,而 results
用以取得 poll_id
更新選項結果:

當然,我們必須建立 polls/results.html 模版檔案:

接著你可以試著連結網站,在上頭作些投票,你應該可以看到以下結果:

簡介 CSRF
在練習 13 中看到了個{% csrf_token %}
,這是什麼?CSRF 全名 Cross-Site Request Forgery,中文常翻為跨站請求攻擊或跨站偽造請求,這是利用 Web 應用程式在設計 HTTP 請求時,因為考量不周全造成的漏洞,從而進行攻擊的手法,通常是在 Web 應用程式站外的其他頁面中,包括惡意程式碼或鏈結,當使用者已通過驗證且會話(Session)未過期時,瀏覽該頁面或點選該惡意鏈結,就會造成攻擊成功的可能性。
一個 CSRF 攻擊的情境範例會像是 ...
- Bob 登入了 www.webapp.com,並且會話尚未過期。
- Bob 瀏覽了另一個頁面,這個頁面中包括了惡意駭客置入的
<img src="http://www.webapp.com/project/1/destroy">
,然後 Bob 的某個專案就莫名奇妙被刪除了。 - Bob 瀏覽的頁面是不是跟 www.webapp.com 同一個網站並不重要,也許是在另一個論譠、Blog 或特意發給 Bob 的郵件中。
<img>
時,就會自動以 GET 請求 src
指定的網址,就這個情境來說,攻擊要能成立的前題,是 /project/1/destroy 這樣的請求就能刪除專案,這很顯然是 URL 設計時的不良,加上應用程式沒有在重大操作之前,進一步確認使用者身份與意圖而導致。
對於 HTTP 請求,有些人會有 GET 不安全,而 POST 比較安全的錯誤觀念,乍看這個例子好像是如此,實際上,也可以透過 POST 來發動類似的請求。例如:
<a href="http://www.harmless.com/" onclick="
var f = document.createElement('form');
f.style.display = 'none';
this.parentNode.appendChild(f);
f.method = 'POST';
f.action = 'http://www.example.com/account/destroy';
f.submit();
return false;">好康在這裡</a>
就算不點選,只要滑鼠略過圖也可以 ...
<img src="http://www.harmless.com/img" width="400" height="400" onmouseover="..." />
就算沒有任何滑鼠操作,現在只要利用一些 JavaScript 寫些 Ajax 請求,都有可能讓這類攻擊發生 ...
先前談過,CSRF 是利用 Web 應用程式在設計 HTTP 請求時,因為考量不周全造成的漏洞,因此,防範方式就是認真思考 HTTP 請求方法之使用。單就
<form>
的 method
允許設置的 GET 與 POST 來說,至少要想一下:
- GET 應用於等冪(Idempotent)操作,相同請求重複多次都必須有相同結果,就 GET 而言,語義上也是「取得」資訊,因此 GET 請求不建議用於改變應用程式狀態。
- POST 應用於非等冪操作,同樣請求重複多次,可能會產生不同結果,也就是會改變應用程式狀態。
{% csrf_token %}
來產生:
當你瀏覽表單時,
{% csrf_token %}
會產生隱藏欄位,當中包括了一組安全代碼,例如:

Django 應用程式接受請求時,必須同時在請求中找到這組安全代碼,從而確認請求是來自於同一站上的表單。
(這個例子並不單只是如何防範 CSRF,主要想表達的是,安全其實是現代設計應用程式時應主動納入的考量,現在有不少框架也將安全納為特色之一,讓開發者不用煩惱安全防護實作時的枝微末節。)