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

时间:2020-03-06 14:26:40  来源:igfitidea点击:

我们有一个系统,希望防止两个不同帐户注册相同的信用卡号。由于我们内部不存储信用卡号,仅存储最后四位数字和有效期,因此我们不能简单地比较信用卡号和有效期。

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

通常,使用盐来避免字典攻击。我认为在这种情况下我们很脆弱,因此我们应该将盐与哈希值一起存储。

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

解决方案

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

加盐的哈希应该可以正常工作。每用户拥有一个盐分系统应该具有足够的安全性。

比较散列是一个很好的解决方案。但是,请确保我们不只是对所有信用卡号加相同的常量盐。在每张卡上使用不同的盐(例如有效期限)。这应该使我们完全不受字典攻击的影响。

从这篇《编码恐怖》文章中:

Add a long, unique random salt to each password you store. The point of a salt (or nonce, if you prefer) is to make each password unique and long enough that brute force attacks are a waste of time. So, the user's password, instead of being stored as the hash of "myspace1", ends up being stored as the hash of 128 characters of random unicode string + "myspace1". You're now completely immune to rainbow table attack.

几乎是个好主意。

仅存储散列是一个好主意,它已经在密码世界中使用了数十年。

加盐似乎是个好主意,确实使蛮力攻击对攻击者而言要困难得多。但是,当我们实际检查以确保新的CC唯一时,盐将花费我们很多额外的精力:我们必须将新CC的SHA-1数SHA-1次,其中N是我们拥有的盐的数量已用于我们要与之比较的所有CC。如果确实选择了好的随机盐,则必须对系统中其他所有卡进行哈希处理。所以现在是我们在做蛮力。因此,我想说这不是可扩展的解决方案。

我们会看到,在密码世界中,盐没有增加任何成本,因为我们只想知道明文+盐哈希是否等于我们为该特定用户存储的内容。要求实际上是完全不同的。

我们必须权衡一下自己的利益。如果盐被盗,添加盐并不能保证数据库的安全,只会使解码变得更加困难。有多少难度?如果将攻击从需要30秒更改为需要一天,则我们一无所获-仍将被解码。如果将其从一天更改为30年,我们将获得一些值得考虑的东西。

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

如果将卡号的后4位与持卡人的姓名(或者姓氏)和有效期相结合,则我们应具有足够的信息以使记录唯一。哈希对安全性很有好处,但是我们是否不需要存储/调用盐来复制哈希以进行重复检查?

我认为上述建议的一个好的解决方案是存储一个散列值,例如卡号,到期日期和名称。这样,我们仍然可以进行快速比较...

@科里·金

SHA 1本身并没有损坏。文章显示的是,有可能在不到蛮力的时间内生成两个具有相同哈希值的字符串。我们仍然无法在合理的时间内生成等于SPECIFIC哈希的字符串。两者之间有很大的不同。

Sha1损坏在这里不是问题。
所有折断的意思是,比我们期望的更容易计算冲突(两个具有相同sha1的数据集)。
这对于基于sha1接受任意文件可能是一个问题,但与内部哈希应用程序无关。

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

使数据更安全的一种方法是使每个操作的计算量都很大。例如,如果我们两次md5,则攻击者需要更长的时间才能破解代码。

生成有效的信用卡号并尝试对每个可能的到期日期进行收费都是相当琐碎的。然而,这在计算上是昂贵的。如果我们使散列破解变得更加昂贵,那么任何人都不会去打扰;即使他们有盐,哈希和我们使用的方法。

我认为人们已经开始考虑此设计。使用盐分,高度安全(例如,"计算上昂贵")的哈希(如sha-256),并按记录记录唯一的盐。

我们应该首先进行低成本,高精度检查,然后仅在该检查命中时再进行昂贵的最终检查。

步骤1:

寻找与最后4位数字匹配的内容(可能还有到期日期,尽管那里可能需要解决一些细微问题)。

第2步:

如果简单检查成功,请使用salt,获取哈希值,然后进行深度检查。

ccare的最后4位数字是唯一的(部分原因是它还包括LUHN检查数字),因此我们将进行的最终不会匹配的深度检查的百分比(假阳性率)将非常低(不到百分之一),相对于朴素的"每次都执行哈希检查"设计,可以节省大量的开销。

让我们做一些数学运算:信用卡号为16位数字。前七个数字是"主要行业"和发行者编号,最后一个数字是luhn校验和。剩下的8位数字是"免费",总计1亿个帐号乘以潜在发行人号码的数量(不太可能很高)。有一些实现可以在日常硬件上每秒执行数百万个哈希的实现,因此,无论我们进行什么繁琐的工作,这对于蛮力来说都不会是什么大问题。

碰巧的是,当寻找一些给出哈希算法基准的东西时,我发现了这篇关于存储信用卡哈希值的文章,其中说:

Storing credit cards using a simple single pass of a hash algorithm, even when salted, is fool-hardy.  It is just too easy to brute force the credit card numbers if the hashes are compromised.
  
  ...
  
  When hashing credit card number, the hashing must be carefully designed to protect against brute forcing by using strongest available cryptographic hash functions, large salt values, and multiple iterations.

全文值得一读。不幸的是,结果似乎是,任何使存储散列信用卡号"安全"的情况都将使搜索重复项的费用过高。

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

第一个解决方案

  • 对于每张信用卡,我们存储最后4位数字,到期日期,长随机盐(50字节长)和CC号的加盐哈希值。我们使用bcrypt哈希算法是因为它非常安全,并且可以根据需要调整为CPU密集型。我们将其调整为非常昂贵(服务器上每个哈希大约1秒!)。但是我想我们可以改用SHA-256,并根据需要进行多次迭代。
  • 输入新的抄送号码后,我们首先查找所有以相同的4位数字结尾且具有相同到期日期的现有抄送号码。然后,对于每个匹配的CC,我们检查其存储的盐腌哈希是否与根据其盐和新CC编号计算的盐腌哈希匹配。换句话说,我们检查是否hash(stored_CC1_salt + CC2)== stored_CC1_hash

由于我们的数据库中大约有10万张信用卡,因此我们需要计算大约10个散列,因此大约10秒钟即可得到结果。在我们的情况下,这很好,但是我们可能希望将bcrypt调低一点。不幸的是,如果我们这样做,此解决方案的安全性将降低。另一方面,如果将bcrypt调整为占用更多CPU资源,则将需要更多时间来匹配CC编号。

尽管我认为此解决方案比简单地存储CC号的未加盐哈希更好,但它并不能防止非常有动机的海盗(设法获得数据库的副本)在平均时间内破坏一张信用卡2至5年。因此,如果数据库中有10万张信用卡,并且海盗拥有大量CPU,那么他每天就可以收回一些信用卡号!

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

第二解决方案

只需让付款服务提供商为信用卡生成一个别名即可。

  • 对于每张信用卡,我们只需存储要存储的任何内容(例如最后4位数字和有效期),再加上信用卡号别名。
  • 输入新的信用卡号后,请与付款提供商联系并为其提供抄送号(或者将客户重定向到付款提供商,然后他直接在付款提供商的网站上输入该抄送号)。作为回报,我们将获得信用卡别名!而已。当然,我们应该确保付款提供商提供此选项,并且所生成的别名实际上是安全的(例如,确保他们不只是简单地计算信用卡号上的SHA-1!)。现在,如果海盗想要恢复信用卡号,则海盗必须破坏系统以及支付提供商的系统。

简单,快速,安全(嗯,至少在付款服务提供商是这样的情况下)。我看到的唯一问题是,它将我们与付款提供商绑定在一起。

希望这可以帮助。