專題網站的註冊後存入資料庫前需要用到加密模塊,所以就來學習了!
這裡的內容會隨著我學習的知識越來越多而增加,並不是一次過學習全部!

簡介

passlib支持python2&3,擁有超過30種加密方式。
下面是一個PBKDF2-SHA256加密算法的例子

import passlib
from passlib.hash import pbkdf2_sha256

hash = pbkdf2_sha256.hash("toomanysecrets")

print(pbkdf2_sha256.verify("toomanysecrets", hash))// true

print(pbkdf2_sha256.verify("test", hash)) // false

PasswordHash

Passlib支持大量的加密演算法,這些都可以從passlib.hash模塊引入。雖然每種演算法的具體選項和設置都有些不同,可是passlib都使用相同的接口,該接口由passlib.ifc.PasswordHash的Class定義

PasswordHash的用例:

  • 創建&驗證 哈希

  • 檢查哈希的配置,並且自定義配置

PasswordHash.hash()可以加密

PasswordHash.verify()可以驗證

加密

首先,import想要使用的加密演算法,這裡使用pbkdf2_sha256

from passlib.hash import pbkdf2_sha256

用Password.hash() 來加密一個密碼,這裡會使用unicode來編碼,並且生成隨機值

hash = pbkdf2_sha256.hash("your_password")
print(hash)
// '$pbkdf2-sha256$29000$9t7be09prfXee2/NOUeotQ$Y.RDnnq8vsezSZSKy1QNy6xhKPdoBIwc.0XDdRm9sJ8'

每次調用都會產生新的哈希值,可是每個生成的哈希值都不一樣,雖然原先的密碼是一樣的。

驗證

使用PasswordHash.verify()來來驗證input和創建的哈希值是否正確

print(pbkdf2_sha256.verify("password", hash)) // True
print(pbkdf2_sha256.verify("joshua", hash)) //False

Unicode&非ASCII

主流的加密演算法的用法,被加密的通常是unicode utf-8 編碼

有一種例外是遠古時代的密碼用另種標準元碼來加密。這種情況,密碼應該用主流的方式來加密,不然的話verify()驗證會失敗,用戶會沒辦法登錄

為了確保安全,程式也應該把input用passlib.utils.saslprep()來標準化,再進行加密。

配置

using()函數

每個演算法都有自己的attributes,有些允許自己配置一些設定,如果你需要配置的時候不需要修改hasher的class,或者傳入PasswordHash.hash()函數。

你只需要使用PasswordHash.using() 就可以對該演算法進行配置。

用例

from passlib.hash import pbkdf2_sha256

print(pbkdf2_sha256.default_rounds) //29000
print(pbkdf2_sha256.hash("password"))
// '$pbkdf2-sha256$29000$V0rJeS.FcO4dw/h/D6E0Bg$FyLs7omUppxzXkARJQSl.ozcEOhgp3tNgNsKIAhKmp8'
                ^^^^^

如果使用PasswordHash.using(),我們可以覆蓋該參數。

custom_pbkdf2 = pbkdf2_sha256.using(rounds=123456)
print(custom_pbkdf2.default_rounds)
// 123456

print(custom_pbkdf2.hash("password"))
// '$pbkdf2-sha256$123456$QwjBmJPSOsf4HyNE6L239g$8m1pnP69EYeOiKKb5sNSiYw9M8pJMyeW.CSm0KKO.GI'
                ^^^^^^

其他設置

PasswordHash.setting_kwds

Context Keywords

雖然上面PasswordHash.hash() 的例子是一種很常見的用法,可是有些加密演算法是需要你提供額外的資料(如用戶名)來加密

下面的例子就是oracle10的加密演算法,當加密的時候就需要一個用戶名.

from passlib.hash import oracle10
hash = oracle10.hash("secret", user="admin")
print(hash) // 'B858CE295C95193F'

這個和以上的配置的區別就在,配置只需要配置一次,而這個每次調用都需要提供,並且當做密碼加密在其中。並且每次使用PasswordHash.hash()或者PasswordHash.verify()都需要提供。

print(oracle10.verify("secret", hash, user="admin"))
// True

下面的例子是當username或者password錯誤的時候,verify()會導致False

print(oracle10.verify("secret", hash, user="wronguser"))
// False

print(oracle10.verify("wrongpassword", hash, user="admin"))
// False

如果沒有假如context keywords則會觸發TypeError:

>>> hash = oracle10.hash("password")
Traceback (most recent call last):
    <traceback omitted>
TypeError: user must be unicode or bytes, not None

要查看那個加密演算法是否需要額外的keyword,可以來到文檔查看,或者從該演算法的context_kwds 屬性

>>> oracle10.context_kwds
("user",)

>>> pbkdf2_sha256.context_kwds
()

辨認演算法

一種稀奇的用法就是你需要查看一個字串是否屬於某個加密演算法。這個時候使用PasswordHash。verify()會報錯

>>> from passlib.hash import pbkdf2_sha256, md5_crypt

>>> other_hash = md5_crypt.hash("password")

>>> pbkdf2_sha256.verify("password", other_hash)
Traceback (most recent call last):
    <traceback omitted>
ValueError: not a valid pbkdf2_sha256 hash

這個時候可以使用identify函數來檢查字串屬於哪個演算法。

>>> hash = pbkdf2_sha256.hash("password")
>>> pbkdf2_sha256.identify(hash)
True

>>> pbkdf2_sha256.identify(other_hash)
False

CryptContext

passlib.context模組裡面只有一個class叫passlib.context.CryptContext

這個用途是為了處理同時有多種哈希密碼和各種樣式的情況的程序。

  • 辨別加密演算法,並且驗證。

  • 配置演算法,加載演算法,廢棄演算法。設置時間參數。

  • 轉移加密過的數據,當一個演算法被拋棄。

  • 加載本地配置文件。

基本來說,CryptContext只是PasswordHah的對象。

下面的代碼創建了一個對象,並且支持三種演算法

>>> from passlib.context import CryptContext
>>> myctx = CryptContext(schemes=["sha256_crypt", "md5_crypt", "des_crypt"])

這個物件使用和PasswordHash非常相似的函數:

>>> # this loads first algorithm in the schemes list (sha256_crypt),
>>> # generates a new salt, and hashes the password:
>>> hash1 = myctx.hash("joshua")
>>> hash1
'$5$rounds=80000$HFEGd1wnFknpibRl$VZqjyYcTenv7CtOf986hxuE0pRaGXnuLXyfb7m9xL69'

>>> # when verifying a password, the algorithm is identified automatically:
>>> myctx.verify("gtnw", hash1)
False
>>> myctx.verify("joshua", hash1)
True

>>> # alternately, you can explicitly pick one of the configured algorithms,
>>> # through this is rarely needed in practice:
>>> hash2 = myctx.hash("dogsnamehere", scheme="md5_crypt")
>>> hash2
'$1$e2nig/AC$stejMS1ek6W0/UogYKFao/'

>>> myctx.verify("letmein", hash2)
False
>>> myctx.verify("dogsnamehere", hash2)
True

如果沒有特別配置,這個對象會使用第一個在物件裡列出的加密演算法。

默認可以由default來設置

>>> myctx = CryptContext(schemes=["sha256_crypt", "md5_crypt", "des_crypt"],
                         default="des_crypt")
>>> hash = myctx.hash("password")
>>> hash
'bIwNofDzt1LCY'

>>> myctx.identify(hash)
'des_crypt'

設置配置

以上的用法只是基礎的用法,實際使用CryptContext是有其他的用途,比如說,單獨配置每一個概括在對象裡的演算法。這樣就不需要一個個配置。

>>> from passlib.context import CryptContext
>>> myctx = CryptContext(["sha256_crypt", "ldap_salted_md5"])

>>> # sha256_crypt using 80000 rounds...
>>> myctx.hash("password", scheme="sha256_crypt")
'$5$rounds=80000$GgU/gwNBs9SaObqs$ohY23/zm.8O0TpkGx5fxk0aeVdFpaeKo9GUkMJ0VrMC'
           ^^^^^

>>> # ldap_salted_md5 with an 8 byte salt...
>>> myctx.hash("password", scheme="ldap_salted_md5")
'{SMD5}cIYrPh5f/TeUKg9oghECB5fSeu8='
       ^^^^^^^^^^
>>> # this reconfigures the existing context object so that
>>> # sha256_crypt now uses 91234 rounds,
>>> # and ldap_salted_md5 will use 16 byte salts:
>>> myctx.update(sha256_crypt__default_rounds=91234,
...              ldap_salted_md5__salt_size=16)

>>> # the effect of this can be seen the next time encrypt is called:
>>> myctx.hash("password", scheme="sha256_crypt")
'$5$rounds=91234$GgU/gwNBs9SaObqs$ohY23/zm.8O0TpkGx5fxk0aeVdFpaeKo9GUkMJ0VrMC'
           ^^^^^

>>> myctx.hash("password", scheme="ldap_salted_md5")
'{SMD5}NnQh2S2pjnFxwtMhjbVH59TaG6P0/l/r3RsDwPj/n/M='
       ^^^^^^^^^^^^^^^^^^^^^

以上的用法是兩段式的使用,線把constructor叫出來,然後用update()函數去做相應的配置。

這是一段式的用法。

>>> from passlib.context import CryptContext
>>> myctx = CryptContext(schemes=["sha256_crypt", "ldap_salted_md5"],
...                      sha256_crypt__default_rounds=91234,
...                      ldap_salted_md5__salt_size=16)

這個配置,是可以通過to_dict()函數轉換成dict

>>> myctx.to_dict()
{'schemes': ['sha256_crypt', 'ldap_salted_md5'],
'ldap_salted_md5__salt_size': 16,
'sha256_crypt__default_rounds': 91234}

CryptContext對象還可以將其配置以兼容ConfigParser的字符串形式導出,允許將配置寫入到文件中:

>>> cfg = print myctx.to_string()
>>> print cfg
[passlib]
schemes = sha256_crypt, ldap_salted_md5
ldap_salted_md5__salt_size = 16
sha256_crypt__default_rounds = 912345

或者從文件中提取

>>> # using the special from_string() constructor to
>>> # load the exported configuration created in the previous step:
>>> myctx2 = CryptContext.from_string(cfg)

>>> # or it can be loaded from a local file:
>>> myctx3 = CryptContext.from_path("/some/path/on/local/system")