前提概要:
Digest认证是为了修复基本认证协议的严重缺陷而设计的,秉承“毫不经过明文在网络发送密码”的原则,经过“密码摘要”进行认证,大大提升了安全性
- WWW-Authentication:用来定义使用何种方式(Basic、Digest、Bearer等)去进行认证以获取受保护的资源
- realm:表示Web服务器中受保护文档的安全域(好比公司财务信息域和公司员工信息域),用来指示须要哪一个域的用户名和密码
- qop:保护质量,包含auth(默认的)和auth-int(增长了报文完整性检测)两种策略,(能够为空,可是)不推荐为空值
- nonce:服务端向客户端发送质询时附带的一个随机数,这个数会常常发生变化。客户端计算密码摘要时将其附加上去,使得屡次生成同一用户的密码摘要各不相同,用来防止重放攻击
- nc:nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量。例如,在响应的第一个请求中,客户端将发送“nc=00000001”。这个指示值的目的是让服务器保持这个计数器的一个副本,以便检测重复的请求
- cnonce:客户端随机数,这是一个不透明的字符串值,由客户端提供,而且客户端和服务器都会使用,以免用明文文本。这使得双方均可以查验对方的身份,并对消息的完整性提供一些保护
- response:这是由用户代理软件计算出的一个字符串,以证实用户知道口令
- Authorization-Info:用于返回一些与受权会话相关的附加信息
- nextnonce:下一个服务端随机数,使客户端能够预先发送正确的摘要
- rspauth:响应摘要,用于客户端对服务端进行认证
- stale:当密码摘要使用的随机数过时时,服务器能够返回一个附带有新随机数的401响应,并指定stale=true,表示服务器在告知客户端用新的随机数来重试,而再也不要求用户从新输入用户名和密码了
MD5(MD5(A1):<nonce>:<nc>:<cnonce>:<qop>:MD5(A2))
算法 A1
MD5(默认) <username>:<realm>:<password>
MD5-sess MD5(<username>:<realm>:<password>):<nonce>:<cnonce>
算法 A2
auth(默认) <request-method>:<uri>
auth-int <request-method>:<uri>:MD5(<request-entity-body>)
# -*- coding: utf-8 -*-
import json
from contextlib import closing
import requests
import hashlib
uri = 'http://192.168.10.108/cgi-bin/snapshot.cgi?channel=1'
payload = {}
resp = requests.get(uri, data=payload)
print(resp.status_code)
response = resp.headers
print(response)
response = json.loads(json.dumps(dict(response)))
nonce = eval((((response['WWW-Authenticate'].split(','))[2]).split('='))[1])
opaque = eval((((response['WWW-Authenticate'].split(','))[3]).split('='))[1])
realm = eval((((response['WWW-Authenticate'].split(','))[0]).split('='))[1])
print(f'nonce:{nonce}')
print(f'opaque:{opaque}')
print(f'realm:{realm}')
def MD5_demo(str):
md = hashlib.md5() # 创建md5对象
md.update(str.encode(encoding='utf-8'))
return md.hexdigest() # 小写
def getResponse(admin, password, uri, nonce):
a1 = MD5_demo(f"{admin}:{realm}:{password}")
a2 = MD5_demo(f"GET:{uri}")
nc = "00000001" #
cnonce = "0a4f113b" #
qop = 'auth'
response = MD5_demo(f"{a1}:{nonce}:{nc}:{cnonce}:{qop}:{a2}")
print(f'response:{response}')
return response
if __name__ == '__main__':
response = getResponse("xxxxx", "xxxxx", "/cgi-bin/snapshot.cgi?channel=1", nonce)
Authorization = f"""Digest username="admin", realm="Login to 3c31ed46887da25fbd83f9be93f99263", nonce={nonce}, nc=00000001, cnonce="0a4f113b", qop="auth", uri="/cgi-bin/snapshot.cgi?channel=1", response={response} opaque="e9f7b2ca236c7b4c603cbfa07905fbbdcd427ff0" """
print(Authorization)
headers = dict()
headers['Authorization'] = Authorization
print(headers, type(headers))
with closing(requests.get(uri, headers=headers, data=payload, stream=True)) as response:
# 这里打开一个空的png文件,相当于创建一个空的txt文件,wb表示写文件
with open('car.png', 'wb') as file:
# 每128个流遍历一次
for data in response.iter_content(128):
# 把流写入到文件,这个文件最后写入完成就是,car.png
file.write(data)
评论