關於bottle WEB框架中籤名cookie的一點理解

首先要理解一個概念git

MAC (message authenticate code)shell

消息認證碼(帶密鑰的Hash函數):密碼學中,通訊實體雙方使用的一種驗證機制,保證消息數據完整性的一種工具。數組

構造方法由M.Bellare提出,安全性依賴於Hash函數,故也稱帶密鑰的Hash函數。消息認證碼是基於密鑰和消息摘要所得到的一個值,安全

可用於數據源發認證和完整性校驗。cookie

 

消息認證碼(帶密鑰的 Hash函數):密碼學中,通訊實體雙方使用的一種驗證機制,保證消息 數據完整性的一種工具。 構造方法由M.Bellare提出,安全性依賴於Hash函數,故也稱帶密鑰的Hash函數。消息認證碼是基於密鑰和消息摘要所得到的一個值,可用於數據源發認證和完整性校驗。
在發送數據以前,發送方首先使用通訊雙方協商好的 散列函數計算其摘要值。在雙方共享的會話密鑰做用下,由摘要值得到消息驗證碼。以後,它和數據一塊兒被髮送。接收方收到報文後,首先利用會話密鑰還原摘要值,同時利用散列函數在本地計算所收到數據的摘要值,並將這兩個數據進行比對。若二者相等,則報文經過認證。

 

 簽名cookie就是基於這種原理。框架

通常發送的數據都會作base64編碼,關於base64編碼能夠看這個連接ide

 blog.xiayf.cn/2016/01/24/base64-encoding/函數

總的來講:base64並非用來加密的,base64是一種數據編碼方式,目的讓數據符合傳輸協議的要求,將二進制的數據轉化爲一種文本數據工具

在Python中提供了兩個模塊,去實現。如下是base64的基本用法測試

>>> import base64
>>> s='b'asdafsdafa'
SyntaxError: invalid syntax
>>> s=b'asdasdas'
>>> s_64 = base64.bencode(s)

    s_64 = base64.bencode(s)
AttributeError: module 'base64' has no attribute 'bencode'
>>> s_64 = base64.b64encode(s)
>>> s_64
b'YXNkYXNkYXM='
>>> base64.b64decode(s_64)
b'asdasdas'
>>> 

 

對於mac,Python有個模塊hmac基本用法以下

>>> import hmac
>>> hmac.new(b'slat')
<hmac.HMAC object at 0x0000000004110B70>
>>> hmac = hmac.new(b'slat')
>>> hmac.update(b'asdas')
>>> hmac.digest()
b'\xe8\\\xb6\x11\x9dj\rY\x06I\x1f[\x06\xeb\xeb\xf3'
>>> 

 

 文檔說明

class HMAC(builtins.object)
 |  RFC 2104 HMAC class.  Also complies with RFC 4231.
 |  
 |  This supports the API for Cryptographic Hash Functions (PEP 247).
 |  
 |  Methods defined here:
 |  
 |  __init__(self, key, msg=None, digestmod=None)
 |      Create a new HMAC object.
 |      
 |      key:       key for the keyed hash object.
 |      msg:       Initial input for the hash, if provided.
 |      digestmod: A module supporting PEP 247.  *OR*
 |                 A hashlib constructor returning a new hash object. *OR*
 |                 A hash name suitable for hashlib.new().
 |                 Defaults to hashlib.md5.
 |                 Implicit default to hashlib.md5 is deprecated and will be
 |                 removed in Python 3.6.
 |      
 |      Note: key and msg must be a bytes or bytearray objects.
 |  
 |  copy(self)
 |      Return a separate copy of this hashing object.
 |      
 |      An update to this copy won't affect the original object.
 |  
 |  digest(self)
 |      Return the hash value of this hashing object.
 |      
 |      This returns a string containing 8-bit data.  The object is
 |      not altered in any way by this function; you can continue
 |      updating the object after calling this function.
 |  
 |  hexdigest(self)
 |      Like digest(), but returns a string of hexadecimal digits instead.
 |  
 |  update(self, msg)
 |      Update this hashing object with the string msg.
View Code

 

當即了基礎,來看bottle框架設置簽名cookie和獲取簽名cookie的值的源碼.

設置簽名cookie

first_bottle.py
response.set_cookie('account',username,secret='salt')

 

 

    def set_cookie(self, name, value, secret=None, **options):
 
        if not self._cookies:
            self._cookies = SimpleCookie()

        if secret:
            value = touni(cookie_encode((name, value), secret))
        elif not isinstance(value, basestring):
            raise TypeError('Secret key missing for non-string Cookie.')

        if len(value) > 4096: raise ValueError('Cookie value to long.')
        self._cookies[name] = value

        for key, value in options.items():
            if key == 'max_age':
                if isinstance(value, timedelta):
                    value = value.seconds + value.days * 24 * 3600
            if key == 'expires':
                if isinstance(value, (datedate, datetime)):
                    value = value.timetuple()
                elif isinstance(value, (int, float)):
                    value = time.gmtime(value)
                value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value)
            self._cookies[name][key.replace('_', '-')] = value

 self._cookie默認是None,SimpleCookie繼承BaseCookie,BaseCookie繼承一個字典,因此暫且認爲self.cookie是一個字典,

if secret  是判斷,若是設置了密鑰,就執行這一步,

 

def tob(s, enc='utf8'):
    return s.encode(enc) if isinstance(s, unicode) else bytes(s)
def touni(s, enc='utf8', err='strict'):
    return s.decode(enc, err) if isinstance(s, bytes) else unicode(s)
tonat = touni if py3k else tob

 

touni 函數是返回str類型的字符串,這裏unicode=str,unicode(s) 至關於str(s)

 

def cookie_encode(data, key):
    ''' Encode and sign a pickle-able object. Return a (byte) string '''
    msg = base64.b64encode(pickle.dumps(data, -1))
    sig = base64.b64encode(hmac.new(tob(key), msg).digest())
    return tob('!') + sig + tob('?') + msg

 

pickle.dumps將數據序列化,返回的是bytes類型的字符串,而後編碼爲base64    sig 是先用hmac加密,

最後將msg(消息) 和sig(簽名)拼接,這樣一個簽名cookie就設置好了,注意這裏的msg是一個(name,value)包含cookie的key和value

這樣一個簽名cookie就設置好了

 

理解了簽名cookie的設置,再看得到簽名cookie的值就比較簡單了。。

大體原理是拿到cookie的值,經過?分割出message 和sig  ,再拿message和secret 進行hmac 拿到新的sig,這個新的sig與分割出來的sig比較,若是一致,表示沒有被篡改,這樣吧message 用base64decode

而後pickle.loads 就拿到原來的數組了。數組的[1]就是那個value,

def cookie_decode(data, key):
    ''' Verify and decode an encoded string. Return an object or None.'''
    data = tob(data)
    if cookie_is_encoded(data):
        sig, msg = data.split(tob('?'), 1)if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg).digest())):
            return pickle.loads(base64.b64decode(msg))

 

 

由於以前setcookie時在自古穿前面加了一個感嘆 號!  ,因此切片sig[1:]     

def _lscmp(a, b):
    ''' Compares two strings in a cryptographically safe way:
        Runtime is not affected by length of common prefix. '''
    return not sum(0 if x==y else 1 for x, y in zip(a, b)) and len(a) == len(b)

 

上面這個函數是逐個字符比較,若是比較的字符都相等那麼就返回0,不然返回1,這樣若是是兩個字符串徹底匹配,就都是0,調用sum() 相加確定返回0 ,不然確定不是1,可是必須在長度相等的條件下才能夠

測試代碼
>>> a='asdas' >>> unicode(a) Traceback (most recent call last): File "<pyshell#1>", line 1, in <module> unicode(a) NameError: name 'unicode' is not defined >>> b='asd' >>> (0 if x==y else 1 for x,y in zip(a,b)) <generator object <genexpr> at 0x0000000003170200> >>> sum((0 if x==y else 1 for x,y in zip(a,b))) 0 >>> s=zip(a,b) >>> s <zip object at 0x0000000003147948> >>> for i in s: print(i) ('a', 'a') ('s', 's') ('d', 'd')

 

 

爲何比較字符串相等不直接用 a==b?

 

於洋 回到頂部

相關文章
相關標籤/搜索