注意:我最初将其发布到信息安全部门,但我开始认为它在这里可能更相关,因为它实际上是关于确定我应该如何处理请求而不是保护信息。
情况
系统A
:
我有一个向用户提供请求的系统A
。此服务器执行某些操作,然后将用户重定向到系统B
。在该重定向期间,服务器A
可以为用户提供一个 32 个字符的字母数字信息字符串,以传递给系统B
。该信息需要 31 个字符,但其中一个可以用作校验和。此字符串或多或少可以被视为请求 ID。
系统B
:
当系统B
收到来自用户的请求时,它可以通过分析 31 个字符的字符串、查询数据库并与系统 A 通信来验证请求(和类似 ID 的字符串)是否有效。该系统可以绝对确定地验证请求是否有效且未被篡改,但它的计算成本非常高。
攻击:
此系统可能会看到许多欺骗 ID 的尝试。这是通过以后的检查过滤的,所以我不担心单个字符完美地签署 ID,但我确实希望避免在处理这些请求上花费比需要更多的资源。
我需要什么
我正在寻找一种校验和/签名方案,该方案可以通过单个字符让我很好地了解请求是否应该继续验证过程,或者是否应该立即将其丢弃为无效。如果消息被丢弃,我需要 100% 确定它无效,但如果我保留无效的消息也没关系。我相信理想的解决方案意味着保留 1/62 个无效请求(攻击者必须猜测检查字符),但作为最小的解决方案,丢弃所有无效请求的一半就足够了。
我尝试过什么
我已经考虑过使用 Luhn 算法(与用于信用卡的算法相同),但我希望能够使用密钥来生成字符,以使攻击者更难欺骗校验和。
作为创建签名方案的第一次尝试,我使用 31 字节键对 31 字节 id 进行按位异或,对生成的字节求和,转换为十进制并将数字相加,直到它小于 62,然后将其映射到集合[a-bA-Z0-9]
中的字符(下面的伪代码)。问题是,虽然我很确定这不会丢弃任何有效的请求,但我不确定如何确定这将允许无效 ID 的频率,或者是否可以使用最终值检索密钥。
Set alphabet to (byte[]) "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
Set keystring to "aLaklj14sdLK87kxcvalskj7asfrq01";
Create empty byte[] key;
FOR each letter in keystring
Push (index of letter in alphabet) to key;
Create empty byte[] step1;
FOR each (r, k) in (request, key)
Push r XOR s to step1;
Set step2 to 0;
FOR each b in step1
Add (int) b to step2;
WHILE step2 > 62
Copy step2 to step3;
Set step2 to 0;
Convert step3 to String;
Split step3 between characters;
FOR each digit in step3
Add (int) digit to step2;
END WHILE
RETURN alphabet[step2]
正式声明
一个确定性的哈希函数,给定一个私钥和一个31字节长的输入,在设定{x | x ∈ ℕ, x < 62}
中产生一个输出,其中猜测输出将比计算私钥更有效。(可变长度输入的奖励积分)
这最终将在 NodeJS/JavaScript 中实现,但并不真正依赖于语言。
免责声明:如果这个问题太模糊和理论化,我深表歉意。如果需要,请发表评论以澄清。显然,我有一些方法可以解决这个问题,但在这种情况下,我正在寻找尽可能直接的解决方案。
如果你想要一个带有私钥的"确定性哈希函数",那么我相信你可以使用 sha256(或加密库中的任何其他哈希函数)并将密钥附加到输入:
sha256(input+key).toString('hex');
之后,取哈希值的最后几位,将其从十六进制字符串转换为整数,将整数除以 62,得到余数,并根据余数确定字符。
这不会为每个字符提供完美的 1/62 分布概率(十六进制字符串应该对每个值具有均匀分布,但在除以 62 后没有余数),但应该非常接近。
一种方法是在用户访问初始document
时创建一个Blob URL
。该Blob URL
对于创建 URL 的document
应该是唯一的。然后,用户可以将该Blob URL
用作服务器"B"的请求标识符。当用户向"B"发出请求时,撤销Blob URL
。
每次调用URL.createObjectURL()
Blob URL
都是唯一的,用户创建唯一标识符,其中Blob URL
的生存期是创建Blob URL
或撤销Blob URL
的document
的生存期。除非个人计算机上存在其他问题,否则创建Blob URL
的用户以外的任何个人从访问者的浏览器中复制Blob URL
的可能性很小。
const requestA = async() => {
const blob = new Blob();
const blobURL = URL.createObjectURL(blob);
const A = await fetch("/path/to/server/A", {
method:"POST", body:JSON.stringify({id:blobURL})
});
const responseA = await A.text();
// do stuff with response
return [blobURL, responseA];
}
服务器"A"将创建的Blob URL
与服务器"B"通信
const requestB = async(blobURL) => {
const blob = new Blob();
const blobURL = URL.createObjectURL(blob);
const B = await fetch("/path/to/server/B", {
method:"POST", body:JSON.stringify({id:blobURL})
});
const responseB = await B.text();
return responseB
}
requestA()
.then(([blobURL, responseA] => {
// do stuff with `responseA`
console.log(responseA);
// return `requestB` with `blobURL` as parameter
return requestB(blobURL)
})
.then(responseB => console.log(responseB) // do stuff with `responseB`)
.catch(err => console.error(err));