天天看點

HTTP Basic和Digest認證介紹與計算 HTTP Basic和Digest認證介紹與計算 

轉載自:HTTP Basic和Digest認證介紹與計算 - 諸子流 - 部落格園

HTTP Basic和Digest認證介紹與計算 

一、說明

web使用者認證,最開始是get送出+把使用者名密碼存放在用戶端的cookie中的形式;在意識到這樣不安全之後逐漸演變成了post送出+把使用者憑證放到了服務端的session中的形式(當然sessionid還在cookie中)。

不過其實最初給http設計的認證方式,既不是“get+cookie”也不是“post+session”,而是Basic和Digest。但Basic和Digest并不流行我想主要是因為麻煩,一是說Basic和Digest使用的Authorization頭并不會被浏覽器自動發往伺服器,二是說對于Digest計算很麻煩。

二、Basic認證形式

2.1 Basic認證請求示例

請求示例如下,主要是Authorization頭(位置不重要,http頭一般都不分先後)

HTTP Basic和Digest認證介紹與計算 HTTP Basic和Digest認證介紹與計算 
GET /GetDeviceInfo HTTP/1.1
Host: 192.168.220.128
Authorization: Basic YWRtaW46MTIzNDU2
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept-Encoding: gzip, deflate
Accept: */*
Cache-Control: no-cache
Cookie: Secure
Connection: close      
HTTP Basic和Digest認證介紹與計算 HTTP Basic和Digest認證介紹與計算 

2.2 Basic認證計算方法

前邊請求Authorization頭的YWRtaW46MTIzNDU2,實際上是使用者名admin密碼123456使用以下計算方法得到:

base64(username:password)      

Python計算代碼如下:

HTTP Basic和Digest認證介紹與計算 HTTP Basic和Digest認證介紹與計算 
import base64

def get_basic_authorization_header_value(username,password):
    # base64編碼前後都(要)是位元組碼形式
    authorization_value = base64.b64encode((f"{username}:{password}").encode()).decode()
    authorization_header_value = f"Basic {authorization_value}"
    return authorization_header_value      
HTTP Basic和Digest認證介紹與計算 HTTP Basic和Digest認證介紹與計算 

三、Digest認證形式

3.1 Digest認證請求示例

HTTP Basic和Digest認證介紹與計算 HTTP Basic和Digest認證介紹與計算 
GET /GetDeviceInfo HTTP/1.1
Host: 192.168.220.128
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:60.0) Gecko/20100101 Firefox/60.0
Authorization: Digest username="admin",realm="TVT API Test Tool",nonce="d4f95e85dc5a39a4914db61b67878f5b",uri="GetDeviceInfo",algorithm="MD5",cnonce="d4f95e85dc5a39a4914db61b67878f5b",nc=00000001,qop="auth",response="1cc4cf126d3c4a70d2de34c5d8c2943c"
Accept-Encoding: gzip, deflate
Accept: */*
Cache-Control: no-cache
Cookie: Secure
Connection: close      
HTTP Basic和Digest認證介紹與計算 HTTP Basic和Digest認證介紹與計算 

username----系統使用者名;用戶端自行填充

realm----領域;服務端通過WWW-Authenticate頭傳回内容可以自己随便定,但其目的是用于提示用戶端目前是什麼系統,是以規範來說應類似于“[email protected]”的形式。

nonce----服務端通過WWW-Authenticate頭傳回的随機數

uri----請求接口或資源(似乎規範來說應用GET或POST後的一樣,上邊例子中少了/是因為服務端沒按規範實作)

algorithm----後邊response用的計算方法

cnonce----client nonce,用戶端生成的随機數

nc----nonce count,用于辨別進行請求的次數。(但你一直不變服務端也不會管你對不對)

qop----quality of protection,進一步限定response的計算方法,服務端通過WWW-Authenticate頭傳回。

response----認證最主要的值,前面各字段除algorithm外全要參與該值的計算。

3.2 Digest認證計算方法

在最開始的RFC 2069中規定response計算方法如下:

HA1 = MD5(username:realm:password)
HA2 = MD5(method:uri)
response = MD5(HA1:nonce:HA2)      

随後的RFC 2617對計算方法進行了增強,規定計算方法如下(當algorithm值為MD5或未指定、qop未指定時等同RFC 2069):

HTTP Basic和Digest認證介紹與計算 HTTP Basic和Digest認證介紹與計算 
# HA1部分
# 當algorithm值為"MD5"或未指定時,HA1計算方法如下
HA1 = MD5(username:realm:password)
# 當algorithm值為"MD5-sess"時,HA1計算方法如下
HA1 = MD5(MD5(username:realm:password):nonce:cnonce)

# HA2部分
# 當qop值為"auth"或未指定時,HA2計算方法如下
HA2 = MD5(method:uri)
# 當qop值為"auth-int"時,HA2計算方法如下;entityBody是指整個body(?)
HA2 = MD5(method:uri:MD5(entityBody))

# response部分
# 當qop值為"auth"或"auth-int"時,response計算方法如下
response = MD5(HA1:nonce:nonceCount:cnonce:qop:HA2)
# 當qop未指定時,response計算方法如下
response = MD5(HA1:nonce:HA2)      
HTTP Basic和Digest認證介紹與計算 HTTP Basic和Digest認證介紹與計算 

 Python計算代碼如下:

HTTP Basic和Digest認證介紹與計算 HTTP Basic和Digest認證介紹與計算 
import hashlib

# body初始值不要是None,不然下邊encode時會報錯
def get_basic_authorization_header_value(username, password, uri, method, realm, nonce, nc, cnonce, algorithm=None, qop=None, body=""):
    response_value = calc_digest_response_value(username, password, uri, method, realm, nonce, nc, cnonce, algorithm, qop, body)
    authorization_header_value = f'Digest username="{username}",realm="{realm}",nonce="{nonce}",uri="{uri}",algorithm="{algorithm}",cnonce="{cnonce}",nc={nc},qop="{qop}",response="{response_value}"',
    return authorization_header_value

def calc_digest_response_value(username, password, uri, method, realm, nonce, nc, cnonce, algorithm=None, qop=None, body=""):
    # HA1部分
    # 當algorithm值為"MD5"或未指定時,HA1計算方法如下
    if algorithm == "MD5" or algorithm == "" or algorithm is None:
        HA1 = hashlib.md5((f"{username}:{realm}:{password}").encode()).hexdigest()
    # 當algorithm值為"MD5-sess"時,HA1計算方法如下
    elif algorithm == "MD5-sess":
        HA1 = hashlib.md5((f"{username}:{realm}:{password}").encode()).hexdigest()
        HA1 = hashlib.md5((f"{HA1}:{nonce}:{cnonce}").encode()).hexdigest()
    else:
        response_value = '"the value of algorithm must be one of "MD5"/"MD5-sess"/""/None'
        return response_value

    # HA2部分
    # 當qop值為"auth"或未指定時,HA2計算方法如下
    if qop == "auth" or qop == "" or qop is None:
        HA2 = hashlib.md5((f"{method}:{uri}").encode()).hexdigest()
    # 當qop值為"auth-int"時,HA2計算方法如下;entityBody是不是指整個body我其實不太确定
    elif qop == "auth-int":
        HA2 = hashlib.md5((f"{body}").encode()).hexdigest()
        HA2 = hashlib.md5((f"{method}:{uri}:{HA2}").encode()).hexdigest()
    else:
        response_value = '"the value of qop must be one of "auth"/"auth-int"/""/None'
        return response_value

    # response部分
    # 當qop值為"auth"或"auth-int"時,response計算方法如下
    if qop == "auth" or qop == "auth-int":
        response_value = hashlib.md5((f"{HA1}:{nonce}:{nc}:{cnonce}:{qop}:{HA2}").encode()).hexdigest()
    # 當qop未指定時,response計算方法如下
    elif qop == "" or qop is None:
        response_value = hashlib.md5((f"{HA1}:{nonce}:{HA2}").encode()).hexdigest()
    else:
        response_value = "unknown error"
    return response_value      
HTTP Basic和Digest認證介紹與計算 HTTP Basic和Digest認證介紹與計算 

參考:

https://en.wikipedia.org/wiki/Digest_access_authentication

rfc2069

rfc2617