我经营一个网站,我们将某些帐户标记为诈骗者,并“标记”他们的帐户和所有使用的信用卡都是坏的。我们不存储实际的信用卡值,而是存储它的校验和/MD5 算法。
我们现在一直在碰撞。存储这些值的最佳方式是什么 - 不可逆,但能够对未来值进行比较。
我认为 MD5 会是最好的,但我们在这里进行了辩论......
我经营一个网站,我们将某些帐户标记为诈骗者,并“标记”他们的帐户和所有使用的信用卡都是坏的。我们不存储实际的信用卡值,而是存储它的校验和/MD5 算法。
我们现在一直在碰撞。存储这些值的最佳方式是什么 - 不可逆,但能够对未来值进行比较。
我认为 MD5 会是最好的,但我们在这里进行了辩论......
加密安全的散列会起作用。(SHA512 或 SHA256 都可以)
但是,我会使用不与卡片一起存储的相当秘密的盐(以防止任何形式的彩虹表攻击)。
PS:
针对信用卡的彩虹表攻击可能特别有效,因为由于字符集、固定大小和校验位的限制,纯文本空间的总大小非常小。
PPS:
您不能为每个条目使用随机盐,因为您永远无法检查重复项。盐用于防止碰撞,而我们在这种情况下专门寻找碰撞。
仅仅使用一个好的哈希算法是不够安全的。如果您的列表被盗,您存储的哈希值可用于检索工作卡信息。信用卡号码的实际模式空间足够小,以至于有决心的攻击者也可以提前预先计算许多可能的哈希值,如果存在入侵或内部工作,这可能会对您的系统产生其他影响.
我建议您使用盐,并根据涉及卡号的每个数字和第一个盐值的公式计算要添加到盐中的第二个值。这确保了如果您失去对任一部分的控制,您仍然具有合理的唯一性,从而使列表的所有权变得无用。但是,该公式不应该对卡的前 6 位数字(BIN 编号)进行过多加权,并且公式的任何痕迹都不应存储在与盐或最终哈希相同的位置。
考虑一个 16 位信用卡号的结构:
6 位 BIN(银行识别号)
9 位帐号
1 位 Luhn 校验和
BIN 列表在加工业中是众所周知的,对于那些可以访问非法卡号列表的人来说,组装起来并不难。为每个发行者分配的空间进一步减少了有效 BIN 的数量。
Visa - 从 4 开始
American Express - 从 34 / 37
开始 MasterCard - 从 5
Discover/CUP 开始 - 从 6
Diner's Club 开始 - 从 35 开始
等等。
请注意,每个发行人类别中的一些分配的 BIN 信息也是稀疏的。如果攻击者知道您的大多数客户所在的位置,那么这将大大降低唯一性,因为 BIN 信息是按银行分配的。已经拥有富裕社区的小银行发行账户的攻击者只需获得一个账户并将 BIN 作为自己卡上的起点。
校验和数字是使用众所周知的公式计算的,因此可以立即将其作为唯一数据源丢弃。
拥有少量值得瞄准的 BIN,攻击者必须一次检查每个 BIN 集的 9 位数字。这是每组 10 亿个校验和和哈希运算。我手头没有任何基准测试,但我很确定每分钟 100 万次哈希运算对于 MD5 或在适当强大的机器上的任何 SHA 风格来说并非不合理。这相当于不到一天的时间来破解给定 BIN 下的所有匹配项。
最后,您也可以考虑将时间戳或访问者令牌(IP/子网)与您的哈希一起存储。捕获重复的卡号很好,但也要考虑有人用虚假卡号填充您的系统的后果。在某些时候,您需要在您知道无效的阻止卡号之间做出权衡,并为自己提供一种机制来识别和修复误用。
例如,心怀不满的员工可能会自己窃取卡信息,然后通过将有效的哈希值插入您的卡号黑名单来阻止重复业务,从而使用您的哈希机制来对付您。如果您只是存储散列,则撤销此操作非常昂贵——一旦将其转换为散列,一切都是不透明的。考虑到这一点,给自己一个方法来识别散列的来源。
也许您可以存储卡号的两个不同哈希值。两个哈希都会导致冲突的可能性几乎为零。
指出哈希“损坏”的人没有抓住重点,也许是在不理解其含义的情况下重复他们所听到的内容。当人们谈论散列被“破坏”时,他们通常意味着可以轻松生成具有相同散列计算的替代有效负载。
这“破坏”了散列,但仅用于使用散列验证数据的特定目的是它应该是什么。
这在这里并不重要,即有人设法创建一个恰好散列到与其中一张信用卡相同的值的替代数据流在攻击向量方面没有实现任何有意义或有用的东西。
此处使用哈希的风险在于信用卡号码的问题空间非常小,并且它们的彩虹表将非常便宜且易于生成。
添加盐会为已经为纯卡号生成的彩虹表添加一些保护,但它提供任何真正保护的程度取决于在您受到损害的情况下盐的“秘密”程度。如果盐暴露了,那么可以廉价地生成新的彩虹表,一切都结束了。
鉴于应用程序需要使用 salt 才能对黑名单执行检查,因此破坏黑名单数据的人很有可能也能够访问 salt。如果您有多台服务器,则可以通过确保盐和数据不在同一个“位置”来在一定程度上缓解这种情况,因此暴露一台服务器不会为某人提供他们需要的所有部分。(同样,对于备份,不要将数据和盐存储在同一个媒体上,这样人们就可以带着一盘磁带走开并获得一切)。盐只在保密时增加一些保护(在这种类型中使用)。
如果您有足够的资源安全地进行操作,那么我认为这就是要走的路。如果您在任何合理的散列函数上遇到大量冲突,则必须进行大量处理。(事实上,我很惊讶即使在那时碰撞也会成为一个问题,任何合理的哈希函数都应该在这样的小问题空间上提供不同的结果)。
使用 SHA1,尚未发现哈希冲突。
正如其他人所说,HMAC 应该是要走的路。
具有正确密钥的 HMAC-SHA-256 应该:
但是还有一件非常重要的事情:
您没有存储信用卡号码是有充分理由的。即使您可以 100% 确定您正在使用正确的加密,您可能仍然不会存储信用卡号码。为什么?一方面,因为密钥可能会泄露。
因此,您存储哈希,以便无法检索信用卡号。...正确的?
好吧,如果你使用一个普通的散列,一个包含所有可能信用卡号的散列的简单彩虹表会泄露你可能没有存储的所有原始数据。哎呀。但是你现在已经知道了。
所以我们努力做得更好。假设使用单独的盐更好,而使用 HMAC 是我们所知道的最佳方法。
考虑以下场景:
这留下了 5 位数字被暴力破解。那是微不足道的 100,000 次尝试。
如果我们使用了单独的盐,那么游戏就结束了。我们可以简单地以平均 50,000 次尝试暴力破解每个单独的卡号。
如果我们使用了 HMAC,我们似乎是安全的。但是请记住……我们选择不存储加密的卡号,因为即使使用完美的加密,密钥也可能泄露。你猜怎么着。我们的 HMAC 密钥同样会被泄露。再次使用密钥,我们可以暴力破解每个单独的卡号,平均尝试 50,000 次。因此,泄露的密钥为我们提供了信用卡号,就像我们存储了加密的卡号一样。
因此,由于信用卡号的低熵,与加密值相比,存储散列不会增加太多安全性(但 PCI 将密钥轮换要求限制为加密)。
一点观点:
好的,我们假设这里有一个泄露的密钥。极端。但是话又说回来,PCI 作为他们禁止您存储信用卡号码的理由的一部分也是如此,所以我们至少应该考虑一下。
没错,我没有考虑到找到 BIN 的多种猜测。不过,它应该是一个小常数。或者我们可以将自己限制在一个 BIN 中。
当然,PCI 审计员可能比我更宽容。
是的,如果您不存储隐藏的卡号,您会安全 10,000 倍。这很有帮助。使用它来发挥你的优势。尽管如此,如果 50K 尝试是可行的,那么 500M 也可能是可行的。在密钥泄露的情况下,仅仅让我认为数据安全是不够的。
结论:
使用 HMAC-SHA-256。了解风险。尽量少存放。小心保护您的钥匙。在硬件安全模块上花一大笔钱:-)
正如 Henri 上面已经提到的 (+1),正确的解决方案是使用带有密钥的 HMAC 等消息验证码。这正是之前有人提到的“秘盐”。(顺便说一句。盐总是公开的)。
使用 HMAC-SHA-256 (RFC2104, FIPS-198a) 等标准结构,将密钥保密并将结果(身份验证标签)存储在数据库中。
SHA-256 较大的摘要大小(256 位)应该可以防止发生任何冲突,SHA-256 是一个相当好的散列函数,随机冲突的概率是 2^-128,所以如果您在系统中遇到冲突,请告诉我!:)
如果您发现与 MD5 的冲突,为什么不使用更好的算法,例如SHA1 或 SHA256?
MD5 已经坏掉了,所以不可行。引用 Bruce Schneier 的话:“[w]e 已经知道 MD5 是一个损坏的散列函数”并且“没有人应该再使用 MD5。”
即使用 SHA512 或 SHA256 就像有人已经提出的那样。
不要费心做盐,只需使用 HMAC。我知道这是一种滥用,但是你会得到一个不错的键控哈希,这样你就可以防止冲突和彩虹表攻击。
这里的好处是即使密钥泄漏,也没有人可以解密它。对 HMAC 最有效的方法是蛮力。实际上,这里的关键是前面提到的盐。这里的好处是该算法比大多数非安全程序员所做的通常的加盐工作要好一些。
使用可能的最强哈希通常是好的。速度不是本质,缓慢实际上对任何试图暴力反转散列值的人都有效。
我个人喜欢漩涡 - 如果您使用 PHP,请查看哈希函数文档中支持的算法
Whirlpool 返回一个 128 个字符长的字符串,但您不必存储所有字符串。前 32 或 64 个字符就足够了。您也可以考虑 sha512 或 sha284。