# 加密簽名 #
web應用安全的黃金法則是,永遠不要相信來自不可信來源的數據。有時通過不可信的媒介來傳遞數據會非常方便。密碼簽名后的值可以通過不受信任的途徑傳遞,這樣是安全的,因為任何篡改都會檢測的到。
Django提供了用于簽名的底層API,以及用于設置和讀取被簽名cookie的上層API,它們是web應用中最常使用的簽名工具之一。
你可能會發現,簽名對于以下事情非常有用:
+ 生成用于“重置我的賬戶”的URL,并發送給丟失密碼的用戶。
+ 確保儲存在隱藏表單字段的數據不被篡改,
+ 生成一次性的秘密URL,用于暫時性允許訪問受保護的資源,例如用戶付費的下載文件。
## 保護 SECRET_KEY ##
當你使用 startproject創建新的Django項目時,自動生成的 settings.py文件會得到一個隨機的SECRET_KEY值。這個值是保護簽名數據的密鑰 -- 它至關重要,你必須妥善保管,否則攻擊者會使用它來生成自己的簽名值。
## 使用底層 API ##
Django的簽名方法存放于`django.core.signing`模塊。首先創建一個 `Signer` 的實例來對一個值簽名:
```
>>> from django.core.signing import Signer
>>> signer = Signer()
>>> value = signer.sign('My string')
>>> value
'My string:GdMGD6HNQ_qdgxYP8yBZAdAIV1w'
```
這個簽名會附加到字符串末尾,跟在冒號后面。你可以使用`unsign`方法來獲取原始的值:
```
>>> original = signer.unsign(value)
>>> original
'My string'
```
如果簽名或者值以任何方式改變,會拋出`django.core.signing.BadSignature` 異常:
```
>>> from django.core import signing
>>> value += 'm'
>>> try:
... original = signer.unsign(value)
... except signing.BadSignature:
... print("Tampering detected!")
```
通常,`Signer`類使用`SECRET_KEY`設置來生成簽名。你可以通過向`Signer`構造器傳遞一個不同的密鑰來使用它:
```
>>> signer = Signer('my-other-secret')
>>> value = signer.sign('My string')
>>> value
'My string:EkfQJafvGyiofrdGnuthdxImIJw'
```
`class Signer(key=None, sep=':', salt=None)[source]`
返回一個`signer`,它使用`key` 來生成簽名,并且使用`sep`來分割值。`sep` 不能是 [URL安全的base64字母表(http://tools.ietf.org/html/rfc4648#section-5)]中的字符。字母表含有數字、字母、連字符和下劃線。
### 使用salt參數 ###
如果你不希望對每個特定的字符串都生成一個相同的簽名哈希值,你可以在`Signer`類中使用可選的`salt` 參數。使用`salt`參數會同時用它和`SECRET_KEY`初始化簽名哈希函數:
```
>>> signer = Signer()
>>> signer.sign('My string')
'My string:GdMGD6HNQ_qdgxYP8yBZAdAIV1w'
>>> signer = Signer(salt='extra')
>>> signer.sign('My string')
'My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw'
>>> signer.unsign('My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw')
'My string'
```
以這種方法使用`salt`會把不同的簽名放在不同的命名空間中。來自于單一命名空間(一個特定的`salt`值)的簽名不能用于在不同的命名空間中驗證相同的純文本字符串。不同的命名空間使用不同的`salt`設置。這是為了防止攻擊者使用在一個地方的代碼中生成的簽名后的字符串,作為使用不同`salt`來生成(和驗證)簽名的另一處代碼的輸入。
不像你的`SECRET_KEY`,你的`salt`參數可以不用保密。
### 驗證帶有時間戳的值 ###
`TimestampSigner`是 `Signer`的子類,它向值附加一個簽名后的時間戳。這可以讓你確認一個簽名后的值是否在特定時間段之內被創建:
```
>>> from datetime import timedelta
>>> from django.core.signing import TimestampSigner
>>> signer = TimestampSigner()
>>> value = signer.sign('hello')
>>> value
'hello:1NMg5H:oPVuCqlJWmChm1rA2lyTUtelC-c'
>>> signer.unsign(value)
'hello'
>>> signer.unsign(value, max_age=10)
...
SignatureExpired: Signature age 15.5289158821 > 10 seconds
>>> signer.unsign(value, max_age=20)
'hello'
>>> signer.unsign(value, max_age=timedelta(seconds=20))
'hello'
```
`class TimestampSigner(key=None, sep=':', salt=None)[source]`
`sign(value)[source]`
簽名`value`,并且附加當前的時間戳。
`unsign(value, max_age=None)[source]`
檢查`value`是否在少于`max_age` 秒之前被簽名,如果不是則拋出`SignatureExpired`異常。`max_age` 參數接受一個整數或者`datetime.timedelta`對象。
```
Changed in Django 1.8:
在此之前, max_age參數只接受整數。
```
### 保護復雜的數據結構 ###
如果你希望保護一個列表、元組或字典,你可以使用簽名模塊的`dumps` 和 `loads` 函數來實現。它們模仿了Python的`pickle`模塊,但是在背后使用了`JSON`序列化。`JSON`可以確保即使你的`SECRET_KEY`被盜取,攻擊者并不能利用`pickle`的格式來執行任意的命令:
```
>>> from django.core import signing
>>> value = signing.dumps({"foo": "bar"})
>>> value
'eyJmb28iOiJiYXIifQ:1NMg1b:zGcDE4-TCkaeGzLeW9UQwZesciI'
>>> signing.loads(value)
{'foo': 'bar'}
```
由于`JSON`的本質(列表和元組之間沒有原生的區別),如果你傳進來一個元組,你會從`signing.loads(object)`得到一個列表:
```
>>> from django.core import signing
>>> value = signing.dumps(('a','b','c'))
>>> signing.loads(value)
['a', 'b', 'c']
```
`dumps(obj, key=None, salt='django.core.signing', compress=False)[source]`
返回URL安全,sha1簽名的base64壓縮的JSON字符串。序列化的對象使用`TimestampSigner`來簽名。
`loads(string, key=None, salt='django.core.signing', max_age=None)[source]`
`dumps()`的反轉,如果簽名失敗則拋出`BadSignature`異常。如果提供了`max_age`則會檢查它(以秒為單位)。
> 譯者:[Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html),原文:[Cryptographic signing](https://docs.djangoproject.com/en/1.8/topics/signing/)。
>
> 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 協議發布,轉載請保留作者署名和文章出處。
>
> [Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html)人手緊缺,有興趣的朋友可以加入我們,完全公益性質。交流群:467338606。
- 新手入門
- 從零開始
- 概覽
- 安裝
- 教程
- 第1部分:模型
- 第2部分:管理站點
- 第3部分:視圖和模板
- 第4部分:表單和通用視圖
- 第5部分:測試
- 第6部分:靜態文件
- 高級教程
- 如何編寫可重用的應用
- 為Django編寫首個補丁
- 模型層
- 模型
- 模型語法
- 元選項
- 模型類
- 查詢集
- 執行查詢
- 查找表達式
- 模型的實例
- 實例方法
- 訪問關聯對象
- 遷移
- 模式編輯器
- 編寫遷移
- 高級
- 管理器
- 原始的SQL查詢
- 聚合
- 多數據庫
- 自定義查找
- 條件表達式
- 數據庫函數
- 其它
- 遺留的數據庫
- 提供初始數據
- 優化數據庫訪問
- 視圖層
- 基礎
- URL配置
- 視圖函數
- 快捷函數
- 裝飾器
- 參考
- 內建的視圖
- TemplateResponse 對象
- 文件上傳
- 概覽
- File 對象
- 儲存API
- 管理文件
- 自定義存儲
- 基于類的視圖
- 概覽
- 內建顯示視圖
- 內建編輯視圖
- API參考
- 分類索引
- 高級
- 生成 CSV
- 生成 PDF
- 中間件
- 概覽
- 內建的中間件類
- 模板層
- 基礎
- 面向設計師
- 語言概覽
- 人性化
- 面向程序員
- 表單
- 基礎
- 概覽
- 表單API
- 內建的Widget
- 高級
- 整合媒體
- 開發過程
- 設置
- 概覽
- 應用程序
- 異常
- 概覽
- django-admin 和 manage.py
- 添加自定義的命令
- 測試
- 介紹
- 部署
- 概述
- WSGI服務器
- 部署靜態文件
- 通過email追蹤代碼錯誤
- Admin
- 管理操作
- 管理文檔生成器
- 安全
- 安全概述
- 說明Django中的安全問題
- 點擊劫持保護
- 加密簽名
- 國際化和本地化
- 概述
- 本地化WEB UI格式化輸入
- “本地特色”
- 常見的網站應用工具
- 認證
- 概覽
- 使用認證系統
- 密碼管理
- 日志
- 分頁
- 會話
- 數據驗證
- 其它核心功能
- 按需內容處理
- 重定向
- 信號
- 系統檢查框架