防止重复使用信用卡的最佳方法

时间:2008-09-19 15:54:17

标签: hash cryptography credit-card

我们有一个系统,我们希望阻止为两个不同的帐户注册相同的信用卡号码。由于我们不在内部存储信用卡号码 - 只是最后四位数和到期日期 - 我们不能简单地比较信用卡号码和到期日期。

我们目前的想法是在注册卡时在我们的信用卡信息系统中存储哈希(SHA-1),并比较哈希以确定之前是否使用过卡片。

通常,使用salt来避免字典攻击。我假设在这种情况下我们很容易受到攻击,所以我们应该将哈希值与哈希一起存储起来。

你们看到这种方法有什么缺陷吗?这是解决这个问题的标准方法吗?

15 个答案:

答案 0 :(得分:17)

让我们做一些数学计算:信用卡号码长16位。前七位是“主要行业”和发行人数字,最后一位是luhn校验和。留下8位“免费”,总计100,000,000个账号,再乘以潜在发行人数的数字(不太可能非常高)。有些实现可以在日常硬件上每秒进行数百万次哈希,所以不管你做什么腌制,这对暴力来说都不是什么大问题。

纯粹巧合的是,在寻找给出哈希算法基准的东西时,我找到了this article about storing credit card hashes,其中说:

  

使用哈希算法的简单单次传递存储信用卡,即使在加盐时也是如此,这是非常愚蠢的。如果哈希值受到损害,那么对信用卡号码进行暴力破解就太容易了。

     

...

     

当散列信用卡号时,必须仔细设计散列,以通过使用最强的可用加密散列函数,大盐值和多次迭代来防止暴力强制。

完整的文章非常值得仔细阅读。不幸的是,结果似乎是任何使得存储哈希信用卡号码“安全”的情况也会使搜索重复数据的成本过高。

答案 1 :(得分:10)

我认为人们正在考虑设计这个问题。使用像sha-256这样的盐渍的,高度安全的(例如“计算上昂贵的”)散列,以及每个记录的独特盐。

您应首先进行低成本,高精度检查,然后仅在检查结果时进行高成本的最终检查。

第1步:

查找与最后4位数的匹配(也可能是exp。日期,尽管那里有一些可能需要解决的细微之处)。

第2步:

如果简单检查命中,请使用salt,获取哈希值,进行深入检查。

cc#的最后4位是最独特的(部分原因是因为它还包括LUHN校验位),因此深入检查的百分比将与最终不匹配(误报率)非常非常低(百分之几),相对于天真的“每次进行哈希检查”设计,可以节省大量的开销。

答案 2 :(得分:5)

存储信用卡号码的简单SHA-1,方式易于破解(特别是因为已知最后4位数字)。我们公司遇到了同样的问题:这就是我们解决问题的方法。

第一个解决方案

  1. 对于每张信用卡,我们存储最后4位数字,到期日期,长随机盐(50字节长)以及CC编号的盐渍哈希。我们使用bcrypt哈希算法,因为它非常安全,可以根据需要调整为CPU密集型。我们将其调整为非常昂贵(在我们的服务器上每个哈希大约1秒!)。但我想你可以使用SHA-256代替并根据需要迭代多次。
  2. 当输入新的CC编号时,我们首先查找所有现有的CC编号,这些编号以相同的4位数结尾并具有相同的到期日期。然后,对于每个匹配的CC,我们检查其存储的盐渍散列是否与从其盐和新CC号计算的盐渍散列相匹配。换句话说,我们会检查hash(stored_CC1_salt+CC2)==stored_CC1_hash
  3. 由于我们的数据库中有大约10万张信用卡,我们需要计算大约10个哈希值,因此我们得到的结果大约需要10秒钟。在我们的例子中,这很好,但你可能想稍微调整bcrypt。不幸的是,如果你这样做,这个解决方案将不那么安全。另一方面,如果您将bcrypt调整为更加CPU密集型,则需要更多时间来匹配CC编号。

    即使我认为这个解决方案方式比简单地存储CC号的未加盐的哈希更好,但它不会阻止一个非常有动力的盗版者(谁设法获得数据库的副本)打破一张信用卡平均2至5年的时间。因此,如果您的数据库中有100k的信用卡,并且盗版者拥有很多的CPU,那么他每天都可以恢复一些信用卡号码!

    这让我相信你不应该自己计算哈希:你必须将其委托给其他人。这是第二个解决方案(我们正在迁移到第二个解决方案)。

    第二个解决方案

    只需让您的付款服务提供商为您的信用卡生成别名。

    1. 对于每张信用卡,您只需存储您要存储的任何内容(例如最后4位数字和到期日期)信用卡号别名。
    2. 当输入新的信用卡号码时,您联系您的支付提供商并给它CC号码(或者您将客户端重定向到支付提供商,他直接在支付提供商的网站上输入CC号码)。作为回报,您将获得信用卡别名!而已。当然,您应该确保您的支付提供商提供此选项,并且生成的别名实际上是安全的(例如,确保他们不只是计算信用卡号码上的SHA-1!)。现在,如果他想要恢复信用卡号码,盗版者必须破坏您的系统您的支付提供商的系统。
    3. 这很简单,速度快,安全性好(至少如果您的支付服务提供商是这样)。我看到的唯一问题是它会将您与您的支付提供商联系起来。

      希望这有帮助。

答案 3 :(得分:3)

PCI DSS表示您可以使用强大的单向哈希存储PAN(信用卡号)。它们甚至不要求它被腌制。那就是说你应该用每张卡的唯一值加盐。到期日是一个良好的开端,但也许有点太短。您可以从卡中添加其他信息,例如发卡行。您不应使用CVV /安全号码,因为您不允许存储它。如果您确实使用了到期日,那么当持卡人获得一张具有相同号码的新卡时,它将被视为另一张卡。根据您的要求,这可能是好事也可能是坏事。

使数据更安全的方法是使每个操作的计算成本都很高。例如,如果你md5两次,则攻击者需要更长时间来破解代码。

生成有效的信用卡号码并尝试每个可能的到期日收费都相当简单。但是,它的计算成本很高。如果你破坏哈希的成本更高,那么任何人都不值得烦恼;即使他们有盐,哈希和你使用的方法。

答案 4 :(得分:2)

@Cory R. King

SHA 1本身并没有被破坏。文章显示的是,可以生成2个字符串,这些字符串具有相同的哈希值,而不是暴力时间。您仍然无法在合理的时间内生成等同于SPECIFIC哈希的字符串。这两者之间有很大的不同。

答案 5 :(得分:2)

我相信我找到了解决这个问题的万无一失的方法。如果我的解决方案存在缺陷,请有人纠正我。

  1. 在EC2,Heroku等上创建一个安全的服务器。这个服务器只有一个目的,只有一个目的:哈希你的信用卡。
  2. 在该服务器上安装安全的Web服务器(Node.js,Rails等)并设置REST API调用。
  3. 在该服务器上,使用一个唯一的盐(1000个字符)和SHA512 1000次。
  4. 这样,即使黑客得到你的哈希,他们也需要闯入你的服务器才能找到你的公式。

答案 6 :(得分:1)

比较哈希是一个很好的解决方案。但是,请确保您不要仅使用相同的恒定盐来填充所有信用卡号码。在每张卡上使用不同的盐(如失效日期)。这应该会让你完全不受字典攻击。

来自this Coding Horror article

  

为您存储的每个密码添加一个长而独特的随机盐。盐(或者nonce,如果你愿意的话)的要点是使每个密码都是唯一的,并且足够长,以至于暴力攻击是浪费时间。因此,用户的密码,而不是存储为“myspace1”的散列,最终被存储为随机unicode字符串+“myspace1”的128个字符的散列。你现在完全不受彩虹桌攻击的影响。

答案 7 :(得分:1)

几乎是一个好主意。

存储哈希是一个好主意,它已经在密码世界中服务了几十年。

添加salt 似乎就像一个公平的想法,并且确实使攻击者更加难以进行暴力攻击。但是当你实际检查以确保新的CC是唯一的时,那盐就会花费你很多额外的努力:你必须将你的新CC号数量SHA-1次,其中N是你拥有的盐的数量已经用于所有与之比较的CC。如果您确实选择了好的随机盐,则必须为系统中的每个其他卡执行哈希。所以现在你正在做蛮力。所以我想说这不是一个可扩展的解决方案。

你会看到,在密码世界中,盐不会增加任何成本,因为我们只想知道明文+盐是否为我们为这个特定用户存储的内容。您的要求实际上非常不同。

你必须自己权衡利弊。添加盐不会使数据库安全,如果它被盗,它只会使解码更难。多难多少?如果它将攻击从需要30秒改为需要一天你就没有取得任何成果 - 它仍将被解码。如果它从一天变为30年,那么你已经考虑了一些值得考虑的问题。

答案 8 :(得分:0)

是的,比较哈希在这种情况下应该可以正常工作。

答案 9 :(得分:0)

盐渍哈希应该可以正常工作。拥有salt-per-user系统应该具有足够的安全性。

答案 10 :(得分:0)

SHA1是broken。当然,没有太多关于什么是好的替代品的信息。 SHA2?

答案 11 :(得分:0)

如果您将卡号的最后4位数与卡持有人的姓名(或姓氏)和到期日相结合,您应该拥有足够的信息以使记录具有唯一性。哈希对于安全性很好,但是你不需要存储/调用盐来复制哈希以进行重复检查吗?

答案 12 :(得分:0)

我认为上面提到的一个好的解决方案是存储卡号,有效期和名称的哈希值。这样你仍然可以做快速比较......

答案 13 :(得分:0)

Sha1 损坏在这里不是问题。 所有破坏的方法是,可以比您预期的更容易计算碰撞(具有相同sha1的2个数据集)。 这可能是基于sha1接受任意文件的问题,但它对内部散列应用程序没有任何意义。

答案 14 :(得分:0)

如果您使用像Stripe / Braintree这样的支付处理器,请让他们进行“繁重的工作”。

他们都提供卡片指纹识别,您可以安全地存储在您的数据库中,然后比较以查看卡片是否已存在:

  • 条纹返回fingerprint字符串 - 请参阅doc
  • Braintree返回unique_number_identifier string - 请参阅doc