好的,原来的任务是跟踪2"友好"能够共享用户cookie的网站(比方说,我有example.com,我的朋友有mysite.com,他还有一个域simple.example.com,所以他可以在.example.com上设置cookie)。 / p>
要跟踪我们想要设置唯一Cookie的用户活动,此Cookie应该是唯一的并且长度为32个字节(ascii)。从这个角度来看非常简单,可以这样实现:
md5(microtime)
就是这样,但现在我们有新的约束:
我们应该能够确定究竟是谁设置了cookie:exmaple.com引擎或mysite.com引擎
32个字节的长度是必须的,仍然是
我们应该能够加密时间戳(发布cookie时)
生成的cookie值的第一个和最后一个字符应该不同,所以我们可以根据cookie进行A / B测试(所以我们总是可以说,如果cookie的最后一个字符是"> K& #34;,显示此用户"功能A")
鉴于结果字符串的长度应始终为32个或更少,并且数据应加密和解密(当然不是用户),并且字符串对于用户应该是唯一的,这会使任务变得非常复杂。 p>
我的想法和问题:
我们应该使用对称密钥加密(解决约束 1和3),但在这种情况下我们如何确保结果字符串不超过32个字符(约束< / em> 2)?
是否有其他解决问题的方法,因为我们需要加密的数据量是:时间戳和微秒(14字节),站点发布者标志(1个字节)=总共15个字节
我的第一个想法是将数据打包成二进制字符串而不是base64编码。结果将是8-chars long base64-encoded string:
def encode():
base64( pack('Lv', timestamp, microseconds) )
在开头和结尾添加site-issuer标志和字符:
def getCookie():
rand('a'...'Z') + encode() + issuerFlagChar() + rand('a'...'Z')
因此,结果是11个字符长,我们很容易遇到约束2 。
但问题是:这个算法肯定不安全,我不确定数百万网站用户的结果字符串是否是唯一的。
我想知道我是否可以将DES或AES用于此目的,但我不确定结果字符串是否始终符合约束2 (结果字符串不应超过32个ascii字符)。
是否存在对称密钥算法以确保类似&#34;如果使用M字节密钥加密N个字节,则结果数据长度为Math.Ceil(N * 2 + 1 / M)字节&#34;?结果长度是可以预测的吗?
答案 0 :(得分:6)
撇开您确实应该咨询安全顾问这一事实,您可以轻松回答您提出的实际问题:
是否存在对称密钥算法,以确保“如果使用M字节密钥加密N个字节,那么结果数据长度为Math.Ceil(N * 2 + 1 / M)字节”?结果长度是可以预测的吗?
是的。它们被称为Block Ciphers。
根据定义,每个块密码都具有以下属性:密文的长度等于纯文本的长度。实际上,大多数分组密码(包含DES和AES)都有些作弊,因为它们在开始加密之前需要明文为padded to the length of the block。
换句话说,如果明文为N
个字节且块大小为B
,则密文的长度为B*(Math.ceil(N/B))
个字节。
请注意我是如何讨论块大小的,它与密钥大小不同。在这种情况下,密钥大小实际上是无关紧要的。
例如,AES uses a block size of 128 bits或16个字节。这意味着如果您的纯文本长度在17到32个字节之间,AES将保证您的密文长度为32个字节。这与您选择的密钥大小无关,密钥大小可以是128,192或256位(16,24或32字节)之一。
答案 1 :(得分:3)
首先,您需要知道是否要加密或签署数据。
加密会阻止用户查看数据,但他们仍然可以根据加密类型以某种方式对其进行修改。例如,解密修改后的密文只会提供损坏的数据,它不会失败。
另一方面,签名将阻止用户修改数据,也就是说,您的代码将能够检测到已修改的数据。一个简单的算法是HMAC。
我假设你想要两者。我的解决方案同时做到了。
您的cookie必须是32个字节长,即256位。我们将使用128位加密数据和128位用于HMAC。
对于数据,我将时间戳编码为64位整数(即使您希望将其存储为微秒精度,也足够了)。如果您有两个站点,则发布cookie的站点可以存储为1位,但我会将它存储在32位整数中,因为我们有足够的空间。对于可用于a / b测试的标记也是如此。
所有数据都是128位,16个字节。这是AES块的确切大小。所以,我们将使用AES加密它!
其他16个字节将是密文的MAC(Encrypt then MAC)。我使用HMAC-SHA256,它有256位输出。我们只有128位的空间,所以我截断了它。从理论上讲,这使得安全性降低,但实际上128bit足以让蛮力企图无法实现。
解密cookie是相反的过程:计算给定密文的HMAC并检查它是否与给定的MAC匹配。如果是,则继续解密密文并解压缩数据。
以下是代码:
from struct import pack, unpack
from Crypto.Cipher import AES
import hashlib
import hmac
AES_KEY = hashlib.sha256(b"secret key 1 asdfasdf").digest()
HMAC_KEY = hashlib.sha256(b"secret key 2 asdfasdf").digest()
# timestamp: 64bit unix timestamp
# site: 32bit integer, which site issued the cookie
# tag: 32bit integer, tag used for a/b testing.
def encrypt_cookie(timestamp, site, tag):
# Pack the data
data = pack('QII', timestamp, site, tag)
# Encrypt it
aes = AES.new(AES_KEY, AES.MODE_ECB, 'This is an IV456')
ciphertext = aes.encrypt(data)
# Do HMAC of the ciphertext
sig = hmac.new(HMAC_KEY, ciphertext, hashlib.sha256).digest()
sig = sig[:16] # Truncate to only first 16 bytes.
return ciphertext + sig
def decrypt_cookie(cookie):
# Do HMAC of the ciphertext
sig = hmac.new(HMAC_KEY, cookie[:16], hashlib.sha256).digest()
sig = sig[:16] # Truncate to only first 16 bytes.
# Check the HMAC is ok
if sig != cookie[16:]:
raise Exception("Cookie has been tampered with")
# Decrypt it
aes = AES.new(AES_KEY, AES.MODE_ECB, 'This is an IV456')
data = aes.decrypt(cookie[:16])
# unPack the data
timestamp, site, tag = unpack('QII', data)
return timestamp, site, tag
cookie = encrypt_cookie(1, 2, 3)
print(len(cookie)) # prints: 32
print(decrypt_cookie(cookie)) # prints: 1, 2, 3
# Change a single byte in the cookie, the last one
cookie = cookie[:31] + b'0'
print(decrypt_cookie(cookie)) # raises the exception
我很想知道为什么cookie必须是32字节。似乎有一个奇怪的要求,如果你没有,你可以使用许多旨在解决这个问题的库,例如Django signing如果你正在使用它Django的。