如何在不使用密钥的情况下验证 Fernet 令牌?
How can I validate a Fernet token without using the key?
我正在使用 cryptography.fernet
库来加密一些小文本,当我在我的服务器上收到该文本时,我想在进行 RPC 之前验证它实际上是有效的密文解密它,这样我的服务器就可以 return 数据无效。是否有一些廉价的方法来避免垃圾邮件?
例如,如果这是我的密文:
>>> k = Fernet.generate_key()
>>> f = Fernet(k)
>>> c = f.encrypt("misingnoglic@gmail.com")
>>> c
'gAAAAABdCC4Z8fqgRu7fCv2e7cvPm46rMwTVmSJK6guR5vrnvjaCICXKI1cI-_qr3Cs_z602a4tS-sMYm_smSOzgwOJ8biVQDqlyyyt-iLcxQNCOmjBywwM='
有什么便宜的方法可以验证以 gAAAA 开头的字符串是否有效?或者字符串 'abcde' 无效?
谢谢!
嗯,看看the Fernet specification。 Fernet 令牌(这就是您正在谈论的字符串的调用方式)的结构如下(其中 ‖
表示串联):
token = urlsafe_b64encode(Version ‖ Timestamp ‖ IV ‖ Ciphertext ‖ HMAC)
作为令牌最后一部分的 HMAC 是从初始部分 (Version ‖ Timestamp ‖ IV ‖ Ciphertext
) 使用签名密钥(这是前半部分)计算得出的键),如规范所述。
问题是你没有密钥,因此你唯一能做的就是在从 Base64 解码后检查令牌的初始字段:
Version
应该是 0x80
,这意味着版本 1(同样如规范中所述)。
Timestamp
是与令牌关联的时间戳:您可以检查此数字是否在特定范围内以丢弃任何过期或格式错误的令牌。
所以,这就是你可以对 "reduce the spam" 做的事情:
from base64 import urlsafe_b64decode
from struct import unpack
from datetime import datetime, timedelta
bin_token = urlsafe_b64decode(c) # <-- c is the Fernet token you received
version, timestamp = unpack('>BQ', bin_token[:9])
tok_age = datetime.now() - datetime.fromtimestamp(timestamp)
max_age = timedelta(7) # 7 days
if version != 0x80:
print 'Invalid token version!'
if tok_age > max_age:
print 'Token expired!'
elif tok_age < timedelta(0):
print 'Token timestamp in the future! Invalid token!'
奖励:如我所说,如果您至少没有签名密钥(即密钥的前半部分),则无法验证令牌的有效性。因此,以下内容当然不适用于您的情况,但假设您拥有签名密钥。在这种情况下,除了上述你仍然应该做的检查,你还可以做以下事情来验证令牌的有效性:
from base64 import urlsafe_b64decode
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.hmac import HMAC
from cryptography.hazmat.backends import default_backend
bin_data = urlsafe_b64decode(c)
# Assuming you have this:
signing_key = "???" # should be urlsafe_b64decode(k)[:16]
client_data = bin_data[:-32]
client_hmac = bin_data[-32:]
print 'Client HMAC:', client_hmac.encode('hex')
real_hmac = HMAC(signing_key, hashes.SHA256(), default_backend())
real_hmac.update(client_data)
real_hmac = real_hmac.finalize()
print 'Real HMAC :', real_hmac.encode('hex')
if client_hmac == real_hmac:
print 'Token seems valid!'
else:
print 'Token does NOT seem valid!'
我正在使用 cryptography.fernet
库来加密一些小文本,当我在我的服务器上收到该文本时,我想在进行 RPC 之前验证它实际上是有效的密文解密它,这样我的服务器就可以 return 数据无效。是否有一些廉价的方法来避免垃圾邮件?
例如,如果这是我的密文:
>>> k = Fernet.generate_key()
>>> f = Fernet(k)
>>> c = f.encrypt("misingnoglic@gmail.com")
>>> c
'gAAAAABdCC4Z8fqgRu7fCv2e7cvPm46rMwTVmSJK6guR5vrnvjaCICXKI1cI-_qr3Cs_z602a4tS-sMYm_smSOzgwOJ8biVQDqlyyyt-iLcxQNCOmjBywwM='
有什么便宜的方法可以验证以 gAAAA 开头的字符串是否有效?或者字符串 'abcde' 无效?
谢谢!
嗯,看看the Fernet specification。 Fernet 令牌(这就是您正在谈论的字符串的调用方式)的结构如下(其中 ‖
表示串联):
token = urlsafe_b64encode(Version ‖ Timestamp ‖ IV ‖ Ciphertext ‖ HMAC)
作为令牌最后一部分的 HMAC 是从初始部分 (Version ‖ Timestamp ‖ IV ‖ Ciphertext
) 使用签名密钥(这是前半部分)计算得出的键),如规范所述。
问题是你没有密钥,因此你唯一能做的就是在从 Base64 解码后检查令牌的初始字段:
Version
应该是0x80
,这意味着版本 1(同样如规范中所述)。Timestamp
是与令牌关联的时间戳:您可以检查此数字是否在特定范围内以丢弃任何过期或格式错误的令牌。
所以,这就是你可以对 "reduce the spam" 做的事情:
from base64 import urlsafe_b64decode
from struct import unpack
from datetime import datetime, timedelta
bin_token = urlsafe_b64decode(c) # <-- c is the Fernet token you received
version, timestamp = unpack('>BQ', bin_token[:9])
tok_age = datetime.now() - datetime.fromtimestamp(timestamp)
max_age = timedelta(7) # 7 days
if version != 0x80:
print 'Invalid token version!'
if tok_age > max_age:
print 'Token expired!'
elif tok_age < timedelta(0):
print 'Token timestamp in the future! Invalid token!'
奖励:如我所说,如果您至少没有签名密钥(即密钥的前半部分),则无法验证令牌的有效性。因此,以下内容当然不适用于您的情况,但假设您拥有签名密钥。在这种情况下,除了上述你仍然应该做的检查,你还可以做以下事情来验证令牌的有效性:
from base64 import urlsafe_b64decode
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.hmac import HMAC
from cryptography.hazmat.backends import default_backend
bin_data = urlsafe_b64decode(c)
# Assuming you have this:
signing_key = "???" # should be urlsafe_b64decode(k)[:16]
client_data = bin_data[:-32]
client_hmac = bin_data[-32:]
print 'Client HMAC:', client_hmac.encode('hex')
real_hmac = HMAC(signing_key, hashes.SHA256(), default_backend())
real_hmac.update(client_data)
real_hmac = real_hmac.finalize()
print 'Real HMAC :', real_hmac.encode('hex')
if client_hmac == real_hmac:
print 'Token seems valid!'
else:
print 'Token does NOT seem valid!'