帖子内容
Hackergame 将选手 ID 加上电子签名称为 token,用于题目认证选手。考虑到题目中的认证逻辑可以被选手看到,但不应该能生成他人的 token,所以不能用 HMAC。现有做法是:将选手 ID 转为字符串,与椭圆曲线签名再 base64 的结果拼接,例如“42:MEUCIQCE……”。题目需要: A1. 将收到的 token 按冒号切开。 A2. 将右侧 base64 解码后验证是否为左侧的合法签名。 A3. 用整个 token 或只用左侧隔离不同选手的环境,以及限制资源用量。 A4. 用整个 token(加盐)哈希作为题目答案。不能只用左侧是因为不应该能生成他人的答案。 关键问题是一个选手能否对应多个不同但都合法的右侧部分。如果能的话,A3 必须只用左侧,不能用整个 token。A4 一个选手会对应多个不同的题目答案,处理起来很棘手。如果不能的话就全简单了,A3 和 A4 都可以直接用整个 token。 想确保一个选手只能对应唯一 token 的话,需要注意以下细节: B1. 按冒号切开的这一步不要写出漏洞,例如不要接受“42:garbage:MEUCIQCE……”。 B2. 椭圆曲线签名可以被人为变换产生另一个不同但也能通过数学验证的签名(malleability),解决这个漏洞的标准做法是规定只有某一个合法,另一个非法。 B3. 很多 base64 库会把不同输入对应到相同输出,例如“A/B=”改成“A/ B=”、“A/B”、“A_B”、“A/C=”可能都可以得到相同输出,应当只允许标准形式。Python 所有版本的 base64 库都有此问题。 B4. 尤其不要不验证就直接相信左侧的 ID。 libsodium 可以解决以上所有问题,它不像 OpenSSL 等库提供底层算法,而是提供绝大多数程序实际需要的高层功能,考虑了各种细节。crypto_sign 函数把 ID 变成 token,crypto_sign_open 函数变回来。 C1. 不用自己切分 token 了。 C2. 它生成的 token 格式不直观,一定程度上避免绕过 crypto_sign_open 直接看 ID。 C3. crypto_sign_open 能确保一个选手对应唯一 token,遇到其他形式或验证失败都不会返回 ID。 延伸阅读: https://hdevalence.ca/blog/2020-10-04-its-25519am