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

时间:2020-12-11 18:25:59

We have a system where we want to prevent the same credit card number being registered for two different accounts. As we don't store the credit card number internally - just the last four digits and expiration date - we cannot simply compare credit card numbers and expiration dates.

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

Our current idea is to store a hash (SHA-1) in our system of the credit card information when the card is registered, and to compare hashes to determine if a card has been used before.

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

Usually, a salt is used to avoid dictionary attacks. I assume we are vulnerable in this case, so we should probably store a salt along with the hash.

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

Do you guys see any flaws in this method? Is this a standard way of solving this problem?

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

15 个解决方案

#1


17  

Let's do a little math: Credit card numbers are 16 digits long. The first seven digits are 'major industry' and issuer numbers, and the last digit is the luhn checksum. That leaves 8 digits 'free', for a total of 100,000,000 account numbers, multiplied by the number of potential issuer numbers (which is not likely to be very high). There are implementations that can do millions of hashes per second on everyday hardware, so no matter what salting you do, this is not going to be a big deal to brute force.

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

By sheer coincidence, when looking for something giving hash algorithm benchmarks, I found this article about storing credit card hashes, which says:

纯粹巧合的是,在寻找给出散列算法基准的东西时,我发现这篇关于存储信用卡哈希的文章,其中说:

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.

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

The full article is well worth a thorough read. Unfortunately, the upshot seems to be that any circumstance that makes it 'safe' to store hashed credit card numbers will also make it prohibitively expensive to search for duplicates.

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

#2


10  

People are over thinking the design of this, I think. Use a salted, highly secure (e.g. "computationally expensive") hash like sha-256, with a per-record unique salt.

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

You should do a low-cost, high accuracy check first, then do the high-cost definitive check only if that check hits.

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

Step 1:

Look for matches to the last 4 digits (and possibly also the exp. date, though there's some subtleties there that may need addressing).

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

Step 2:

If the simple check hits, use the salt, get the hash value, do the in depth check.

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

The last 4 digits of the cc# are the most unique (partly because it includes the LUHN check digit as well) so the percentage of in depth checks you will do that won't ultimately match (the false positive rate) will be very, very low (a fraction of a percent), which saves you a tremendous amount of overhead relative to the naive "do the hash check every time" design.

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

#3


5  

Do not store a simple SHA-1 of the credit card number, it would be way to easy to crack (especially since the last 4 digits are known). We had the same problem in my company: here is how we solved it.

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

First solution

  1. For each credit card, we store the last 4 digits, the expiration date, a long random salt (50 bytes long), and the salted hash of the CC number. We use the bcrypt hash algorithm because it is very secure and can be tuned to be as CPU-intensive as you wish. We tuned it to be very expensive (about 1 second per hash on our server!). But I guess you could use SHA-256 instead and iterate as many times as needed.
  2. 对于每张信用卡,我们存储最后4位数,到期日期,长随机盐(50字节长)和CC号的盐渍哈希值。我们使用bcrypt哈希算法,因为它非常安全,可以根据需要调整为CPU密集型。我们将其调整为非常昂贵(在我们的服务器上每个哈希大约1秒!)。但我想你可以使用SHA-256代替并根据需要迭代多次。

  3. When a new CC number is entered, we start by finding all the existing CC numbers that end with the same 4 digits and have the same expiration date. Then, for each matching CC, we check whether its stored salted hash matches the salted hash calculated from its salt and the new CC number. In other words, we check whether or not hash(stored_CC1_salt+CC2)==stored_CC1_hash.
  4. 当输入新的CC编号时,我们首先查找所有现有的CC编号,这些编号以相同的4位数结尾并具有相同的到期日期。然后,对于每个匹配的CC,我们检查其存储的盐渍散列是否与从其盐和新CC号计算的盐渍散列相匹配。换句话说,我们检查是否hash(stored_CC1_salt + CC2)== stored_CC1_hash。

Since we have roughly 100k credit cards in our database, we need to calculate about 10 hashes, so we get the result in about 10 seconds. In our case, this is fine, but you may want to tune bcrypt down a bit. Unfortunately, if you do, this solution will be less secure. On the other hand, if you tune bcrypt to be even more CPU-intensive, it will take more time to match CC numbers.

由于我们的数据库中有大约10万张信用卡,我们需要计算大约10个哈希值,因此我们得到的结果大约需要10秒钟。在我们的例子中,这很好,但你可能想稍微调整bcrypt。不幸的是,如果你这样做,这个解决方案将不那么安全。另一方面,如果将bcrypt调整为更加CPU密集型,则需要更多时间来匹配CC编号。

Even though I believe that this solution is way better than simply storing an unsalted hash of the CC number, it will not prevent a very motivated pirate (who manages to get a copy of the database) to break one credit card in an average time of 2 to 5 years. So if you have 100k credit cards in your database, and if the pirate has a lot of CPU, then he can can recover a few credit card numbers every day!

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

This leads me to the belief that you should not calculate the hash yourself: you have to delegate that to someone else. This is the second solution (we are in the process of migrating to this second solution).

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

Second solution

Simply have your payment provider generate an alias for your credit card.

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

  1. for each credit card, you simply store whatever you want to store (for example the last 4 digits & the expiration date) plus a credit card number alias.
  2. 对于每张信用卡,您只需存储您想要存储的任何内容(例如最后4位数和到期日期)以及信用卡号别名。

  3. when a new credit card number is entered, you contact your payment provider and give it the CC number (or you redirect the client to the payment provider, and he enters the CC number directly on the payment provider's web site). In return, you get the credit card alias! That's it. Of course you should make sure that your payment provider offers this option, and that the generated alias is actually secure (for example, make sure they don't simply calculate a SHA-1 on the credit card number!). Now the pirate has to break your system plus your payment provider's system if he wants to recover the credit card numbers.
  4. 当输入新的信用卡号码时,您联系您的支付提供商并给它CC号码(或者您将客户端重定向到支付提供商,他直接在支付提供商的网站上输入CC号码)。作为回报,您将获得信用卡别名!而已。当然,您应该确保您的支付提供商提供此选项,并且生成的别名实际上是安全的(例如,确保他们不只是计算信用卡号码上的SHA-1!)。现在,如果他想要恢复信用卡号码,盗版者必须破坏你的系统和你的支付提供商的系统。

It's simple, it's fast, it's secure (well, at least if your payment provider is). The only problem I see is that it ties you to your payment provider.

它很简单,速度快,安全性好(至少如果您的支付服务提供商是这样)。我看到的唯一问题是它将您与支付提供商联系在一起。

Hope this helps.

希望这可以帮助。

#4


3  

PCI DSS states that you can store PANs (credit card numbers) using a strong one-way hash. They don't even require that it be salted. That said you should salt it with a unique per card value. The expiry date is a good start but perhaps a bit too short. You could add in other pieces of information from the card, such as the issuer. You should not use the CVV/security number as you are not allowed to store it. If you do use the expiry date then when the cardholder gets issued a new card with the same number it will count as a different card. This could be a good or bad thing depending on your requirements.

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

An approach to make your data more secure is to make each operation computationally expensive. For instance if you md5 twice it will take an attacker longer to crack the codes.

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

Its fairly trivial to generate valid credit card numbers and to attempt a charge through for each possible expiry date. However, it is computationally expensive. If you make it more expensive to crack your hashes then it wouldn't be worthwhile for anyone to bother; even if they had the salts, hashes and the method you used.

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

#5


2  

@Cory R. King

@Cory R. King

SHA 1 isn't broken, per se. What the article shows is that it's possible to generate 2 strings which have the same hash value in less than brute force time. You still aren't able to generate a string that equates to a SPECIFIC hash in a reasonable amount of time. There is a big difference between the two.

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

#6


2  

I believe I have found a fool-proof way to solve this problem. Someone please correct me if there is a flaw in my solution.

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

  1. Create a secure server on EC2, Heroku, etc. This server will serve ONE purpose and ONLY one purpose: hashing your credit card.
  2. 在EC2,Heroku等上创建一个安全的服务器。这个服务器只有一个目的,只有一个目的:哈希你的信用卡。

  3. Install a secure web server (Node.js, Rails, etc) on that server and set up the REST API call.
  4. 在该服务器上安装安全Web服务器(Node.js,Rails等)并设置REST API调用。

  5. On that server, use a unique salt (1000 characters) and SHA512 it 1000 times.
  6. 在该服务器上,使用一个唯一的盐(1000个字符)和SHA512 1000次。

That way, even if hackers get your hashes, they would need to break into your server to find your formula.

这样,即使黑客得到你的哈希,他们也需要闯入你的服务器才能找到你的公式。

#7


1  

Comparing hashes is a good solution. Make sure that you don't just salt all the credit card numbers with the same constant salt, though. Use a different salt (like the expiration date) on each card. This should make you fairly impervious to dictionary attacks.

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

From this Coding Horror article:

从这篇Coding Horror文章:

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.

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

#8


1  

Almost a good idea.

几乎是个好主意。

Storing just the hash is a good idea, it has served in the password world for decades.

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

Adding a salt seems like a fair idea, and indeed makes a brute force attack that much harder for the attacker. But that salt is going to cost you a lot of extra effort when you actually check to ensure that a new CC is unique: You'll have to SHA-1 your new CC number N times, where N is the number of salts you have already used for all of the CCs you are comparing it to. If indeed you choose good random salts you'll have to do the hash for every other card in your system. So now it is you doing the brute force. So I would say this is not a scalable solution.

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

You see, in the password world a salt adds no cost because we just want to know if the clear text + salt hashes to what we have stored for this particular user. Your requirement is actually pretty different.

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

You'll have to weigh the trade off yourself. Adding salt doesn't make your database secure if it does get stolen, it just makes decoding it harder. How much harder? If it changes the attack from requiring 30 seconds to requiring one day you have achieved nothing -- it will still be decoded. If it changes it from one day to 30 years you have achived someting worth considering.

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

#9


0  

Yes, comparing hashes should work fine in this case.

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

#10


0  

A salted hash should work just fine. Having a salt-per-user system should be plenty of security.

盐渍哈希应该工作得很好。拥有salt-per-user系统应该具有足够的安全性。

#11


0  

SHA1 is broken. Course, there isn't much information out on what a good replacement is. SHA2?

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

#12


0  

If you combine the last 4 digits of the card number with the card holder's name (or just last name) and the expiration date you should have enough information to make the record unique. Hashing is nice for security, but wouldn't you need to store/recall the salt in order to replicate the hash for a duplicate check?

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

#13


0  

I think a good solution as hinted to above, would be to store a hash value of say Card Number, Expiration date, and name. That way you can still do quick comparisons...

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

#14


0  

Sha1 broken is not a problem here. All broken means is that it's possible to calculate collisions (2 data sets that have the same sha1) more easily than you would expect. This might be a problem for accepting arbitrary files based on their sha1 but it has no relevence for an internal hashing application.

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

#15


0  

If you are using a payment processor like Stripe / Braintree, let them do the "heavy lifting".

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

They both offer card fingerprinting that you can safely store in your db and compare later to see if a card already exists:

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

  • Stripe returns fingerprint string - see doc
  • Stripe返回指纹字符串 - 请参阅doc

  • Braintree returns unique_number_identifier string - see doc
  • Braintree返回unique_number_identifier字符串 - 请参阅doc

#1


17  

Let's do a little math: Credit card numbers are 16 digits long. The first seven digits are 'major industry' and issuer numbers, and the last digit is the luhn checksum. That leaves 8 digits 'free', for a total of 100,000,000 account numbers, multiplied by the number of potential issuer numbers (which is not likely to be very high). There are implementations that can do millions of hashes per second on everyday hardware, so no matter what salting you do, this is not going to be a big deal to brute force.

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

By sheer coincidence, when looking for something giving hash algorithm benchmarks, I found this article about storing credit card hashes, which says:

纯粹巧合的是,在寻找给出散列算法基准的东西时,我发现这篇关于存储信用卡哈希的文章,其中说:

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.

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

The full article is well worth a thorough read. Unfortunately, the upshot seems to be that any circumstance that makes it 'safe' to store hashed credit card numbers will also make it prohibitively expensive to search for duplicates.

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

#2


10  

People are over thinking the design of this, I think. Use a salted, highly secure (e.g. "computationally expensive") hash like sha-256, with a per-record unique salt.

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

You should do a low-cost, high accuracy check first, then do the high-cost definitive check only if that check hits.

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

Step 1:

Look for matches to the last 4 digits (and possibly also the exp. date, though there's some subtleties there that may need addressing).

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

Step 2:

If the simple check hits, use the salt, get the hash value, do the in depth check.

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

The last 4 digits of the cc# are the most unique (partly because it includes the LUHN check digit as well) so the percentage of in depth checks you will do that won't ultimately match (the false positive rate) will be very, very low (a fraction of a percent), which saves you a tremendous amount of overhead relative to the naive "do the hash check every time" design.

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

#3


5  

Do not store a simple SHA-1 of the credit card number, it would be way to easy to crack (especially since the last 4 digits are known). We had the same problem in my company: here is how we solved it.

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

First solution

  1. For each credit card, we store the last 4 digits, the expiration date, a long random salt (50 bytes long), and the salted hash of the CC number. We use the bcrypt hash algorithm because it is very secure and can be tuned to be as CPU-intensive as you wish. We tuned it to be very expensive (about 1 second per hash on our server!). But I guess you could use SHA-256 instead and iterate as many times as needed.
  2. 对于每张信用卡,我们存储最后4位数,到期日期,长随机盐(50字节长)和CC号的盐渍哈希值。我们使用bcrypt哈希算法,因为它非常安全,可以根据需要调整为CPU密集型。我们将其调整为非常昂贵(在我们的服务器上每个哈希大约1秒!)。但我想你可以使用SHA-256代替并根据需要迭代多次。

  3. When a new CC number is entered, we start by finding all the existing CC numbers that end with the same 4 digits and have the same expiration date. Then, for each matching CC, we check whether its stored salted hash matches the salted hash calculated from its salt and the new CC number. In other words, we check whether or not hash(stored_CC1_salt+CC2)==stored_CC1_hash.
  4. 当输入新的CC编号时,我们首先查找所有现有的CC编号,这些编号以相同的4位数结尾并具有相同的到期日期。然后,对于每个匹配的CC,我们检查其存储的盐渍散列是否与从其盐和新CC号计算的盐渍散列相匹配。换句话说,我们检查是否hash(stored_CC1_salt + CC2)== stored_CC1_hash。

Since we have roughly 100k credit cards in our database, we need to calculate about 10 hashes, so we get the result in about 10 seconds. In our case, this is fine, but you may want to tune bcrypt down a bit. Unfortunately, if you do, this solution will be less secure. On the other hand, if you tune bcrypt to be even more CPU-intensive, it will take more time to match CC numbers.

由于我们的数据库中有大约10万张信用卡,我们需要计算大约10个哈希值,因此我们得到的结果大约需要10秒钟。在我们的例子中,这很好,但你可能想稍微调整bcrypt。不幸的是,如果你这样做,这个解决方案将不那么安全。另一方面,如果将bcrypt调整为更加CPU密集型,则需要更多时间来匹配CC编号。

Even though I believe that this solution is way better than simply storing an unsalted hash of the CC number, it will not prevent a very motivated pirate (who manages to get a copy of the database) to break one credit card in an average time of 2 to 5 years. So if you have 100k credit cards in your database, and if the pirate has a lot of CPU, then he can can recover a few credit card numbers every day!

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

This leads me to the belief that you should not calculate the hash yourself: you have to delegate that to someone else. This is the second solution (we are in the process of migrating to this second solution).

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

Second solution

Simply have your payment provider generate an alias for your credit card.

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

  1. for each credit card, you simply store whatever you want to store (for example the last 4 digits & the expiration date) plus a credit card number alias.
  2. 对于每张信用卡,您只需存储您想要存储的任何内容(例如最后4位数和到期日期)以及信用卡号别名。

  3. when a new credit card number is entered, you contact your payment provider and give it the CC number (or you redirect the client to the payment provider, and he enters the CC number directly on the payment provider's web site). In return, you get the credit card alias! That's it. Of course you should make sure that your payment provider offers this option, and that the generated alias is actually secure (for example, make sure they don't simply calculate a SHA-1 on the credit card number!). Now the pirate has to break your system plus your payment provider's system if he wants to recover the credit card numbers.
  4. 当输入新的信用卡号码时,您联系您的支付提供商并给它CC号码(或者您将客户端重定向到支付提供商,他直接在支付提供商的网站上输入CC号码)。作为回报,您将获得信用卡别名!而已。当然,您应该确保您的支付提供商提供此选项,并且生成的别名实际上是安全的(例如,确保他们不只是计算信用卡号码上的SHA-1!)。现在,如果他想要恢复信用卡号码,盗版者必须破坏你的系统和你的支付提供商的系统。

It's simple, it's fast, it's secure (well, at least if your payment provider is). The only problem I see is that it ties you to your payment provider.

它很简单,速度快,安全性好(至少如果您的支付服务提供商是这样)。我看到的唯一问题是它将您与支付提供商联系在一起。

Hope this helps.

希望这可以帮助。

#4


3  

PCI DSS states that you can store PANs (credit card numbers) using a strong one-way hash. They don't even require that it be salted. That said you should salt it with a unique per card value. The expiry date is a good start but perhaps a bit too short. You could add in other pieces of information from the card, such as the issuer. You should not use the CVV/security number as you are not allowed to store it. If you do use the expiry date then when the cardholder gets issued a new card with the same number it will count as a different card. This could be a good or bad thing depending on your requirements.

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

An approach to make your data more secure is to make each operation computationally expensive. For instance if you md5 twice it will take an attacker longer to crack the codes.

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

Its fairly trivial to generate valid credit card numbers and to attempt a charge through for each possible expiry date. However, it is computationally expensive. If you make it more expensive to crack your hashes then it wouldn't be worthwhile for anyone to bother; even if they had the salts, hashes and the method you used.

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

#5


2  

@Cory R. King

@Cory R. King

SHA 1 isn't broken, per se. What the article shows is that it's possible to generate 2 strings which have the same hash value in less than brute force time. You still aren't able to generate a string that equates to a SPECIFIC hash in a reasonable amount of time. There is a big difference between the two.

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

#6


2  

I believe I have found a fool-proof way to solve this problem. Someone please correct me if there is a flaw in my solution.

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

  1. Create a secure server on EC2, Heroku, etc. This server will serve ONE purpose and ONLY one purpose: hashing your credit card.
  2. 在EC2,Heroku等上创建一个安全的服务器。这个服务器只有一个目的,只有一个目的:哈希你的信用卡。

  3. Install a secure web server (Node.js, Rails, etc) on that server and set up the REST API call.
  4. 在该服务器上安装安全Web服务器(Node.js,Rails等)并设置REST API调用。

  5. On that server, use a unique salt (1000 characters) and SHA512 it 1000 times.
  6. 在该服务器上,使用一个唯一的盐(1000个字符)和SHA512 1000次。

That way, even if hackers get your hashes, they would need to break into your server to find your formula.

这样,即使黑客得到你的哈希,他们也需要闯入你的服务器才能找到你的公式。

#7


1  

Comparing hashes is a good solution. Make sure that you don't just salt all the credit card numbers with the same constant salt, though. Use a different salt (like the expiration date) on each card. This should make you fairly impervious to dictionary attacks.

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

From this Coding Horror article:

从这篇Coding Horror文章:

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.

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

#8


1  

Almost a good idea.

几乎是个好主意。

Storing just the hash is a good idea, it has served in the password world for decades.

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

Adding a salt seems like a fair idea, and indeed makes a brute force attack that much harder for the attacker. But that salt is going to cost you a lot of extra effort when you actually check to ensure that a new CC is unique: You'll have to SHA-1 your new CC number N times, where N is the number of salts you have already used for all of the CCs you are comparing it to. If indeed you choose good random salts you'll have to do the hash for every other card in your system. So now it is you doing the brute force. So I would say this is not a scalable solution.

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

You see, in the password world a salt adds no cost because we just want to know if the clear text + salt hashes to what we have stored for this particular user. Your requirement is actually pretty different.

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

You'll have to weigh the trade off yourself. Adding salt doesn't make your database secure if it does get stolen, it just makes decoding it harder. How much harder? If it changes the attack from requiring 30 seconds to requiring one day you have achieved nothing -- it will still be decoded. If it changes it from one day to 30 years you have achived someting worth considering.

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

#9


0  

Yes, comparing hashes should work fine in this case.

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

#10


0  

A salted hash should work just fine. Having a salt-per-user system should be plenty of security.

盐渍哈希应该工作得很好。拥有salt-per-user系统应该具有足够的安全性。

#11


0  

SHA1 is broken. Course, there isn't much information out on what a good replacement is. SHA2?

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

#12


0  

If you combine the last 4 digits of the card number with the card holder's name (or just last name) and the expiration date you should have enough information to make the record unique. Hashing is nice for security, but wouldn't you need to store/recall the salt in order to replicate the hash for a duplicate check?

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

#13


0  

I think a good solution as hinted to above, would be to store a hash value of say Card Number, Expiration date, and name. That way you can still do quick comparisons...

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

#14


0  

Sha1 broken is not a problem here. All broken means is that it's possible to calculate collisions (2 data sets that have the same sha1) more easily than you would expect. This might be a problem for accepting arbitrary files based on their sha1 but it has no relevence for an internal hashing application.

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

#15


0  

If you are using a payment processor like Stripe / Braintree, let them do the "heavy lifting".

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

They both offer card fingerprinting that you can safely store in your db and compare later to see if a card already exists:

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

  • Stripe returns fingerprint string - see doc
  • Stripe返回指纹字符串 - 请参阅doc

  • Braintree returns unique_number_identifier string - see doc
  • Braintree返回unique_number_identifier字符串 - 请参阅doc