If your application processes credit card information of any type, there are two requirements in PCI DSS 1.2 that should scare you to death - "8.5.13 Limit repeated access attempts by locking out the user ID after not more than six attempts" and "8.5.14 Set the lockout duration to a minimum of 30 minutes or until [an] administrator enables the user ID."
In my opinion, account lockout is one of those security controls that should be used sparingly and only in the most extreme circumstances. My reasoning for this stance is that almost any lockout mechanism can be used by an attacker to purposefully deny legitimate users access to a site. In its simplest form, this attack can be accomplished with a simple shell script that submits the proper parameter names over HTTP/S through cURL. Knowing this, you should be asking yourself Why would PCI require me to implement something that could have such a dramatic effect on the availability of my site?. The short answer, from what I can gleam, is that they are more concerned about preventing a breach of sensitive information than you performing any business transactions. In essence, the lockout becomes the ultimate fail closed approach to security, in that if no one can access the site then the data stays safe.
So how can you overcome this obstacle, while still maintaining your compliance? Here are a few possibilities that I have come up with. Note that with any of the solutions below, the number of failed attempts needs to be reset to 0 upon successful login.
1. Implement the lockout mechanism to maintain a count of the number of failed login attempts in the user's unauthenticated session. If this count ever reaches the threshold (six in this case) then lock the account by setting some flag in the database. Although this appears to work, it has a major design flaw. The count of failed attempts is maintained in the session until it is written to the database when the threshold is met. For all but the most incompetent attacker, this implementation is easily bypassed. All that is required to bypass this is to submit five passwords, send a new request without the session cookie (which effectively gives you a new session token with the number of failed attempts set at 0), and submit five more passwords with the new session repeating until the password is found. Needless to say, this implementation is broken.
2. Modify the mechanism above to store the number of failed attempts in the database upon each failed attempt. This actually gets us closer to what we really want, because it allows us to persist the number of failed attempts, but can increase overheard dramatically with numerous small updates to the database which are not always efficient.
3. Implement a mechanism that does not perform a lockout, but "meets the spirit of the control." (Disclaimer: By no means am I trying to say here that you should try to skirt PCI, or that it is even possible. What I am trying to show here is something that you may be able to implement that demonstrates you are implementing a compensating control that effectively provides the same level of protection as an account lockout mechanism without the potentially large negative impact to your legitimate users.)
Obviously, the the control is being required so that sites will have something to prevent an attacker from easily brute forcing user's passwords. My question is what about a mechanism that progressively delays the success/failed response, thereby making an attacker have to wait longer with each request to find out if the supplied password was correct. Here is a sample with completely arbitrary delay offsets:
Request 1 --> Immediately return success/fail
Request 2 --> Wait 2 seconds, then return success/fail
Request 3 --> Wait 5 seconds, then return success/fail
Request 4 --> Wait 10 seconds, then return success/fail
Request 5 --> Wait 20 seconds, then return success/fail
Request 6 --> Wait 40 seconds, then return success/fail
Request 7 --> Wait 80 seconds, then return success/fail
Request 8+ --> Wait 160 seconds, then return success/fail
The key to this entire scheme is that the attacker still has to wait the full time delay before being told of the password was correct or not. At no time other than the first request should the mechanism immediately return.
4. Modify option #3 and combine it with option #2 in such a way that the database is updated with each failed attempt, but you still maintain the progressive delay determining the time to wait on the latest value pulled from the database. In addition, you could still lock out the account if a threshold was ever met. Here is some pseudocode with completely arbitrary delay offsets and an assumed threshold of 6:
Request --> if count < 1 then
count => 1;
return success/fail;
else if count < 2 then
count => 2; wait 2 seconds;
return success/fail;
else if count < 3 then
count => 3; wait 5 seconds;
return success/fail;
else if count < 4 then
count => 4; wait 10 seconds;
return success/fail;
else if count < 5 then
count => 5; wait 20 seconds;
return success/fail;
else if count < 6 then
count => 6; account_locked_flg => 1;
wait 40 seconds;
return success/fail;
else
count => count + 1;
wait 40 seconds;
return account_locked_message;
Overall, I like option #3 the best, but if you want to combine these options in other ways I haven't thought of here, or have other solutions that you have come up with feel free to drop me a comment.
10 comments:
Two words: COMPENSATING CONTROLS.
The idea is to meet the spirit of the rule, not the letter of the rule. So don't do what the PCI-DSS standard says; do what works best for you.
If it were me starting with a new site and completely new staff that were good at everything equally, including support and governance from management -- I might meet this with a compensating control that required an additional login step (i.e. additional criteria that is tied to the account) in order to perform any transaction for a certain period of time (say, 30 minutes, since that's what the standard asks for). Basically, multi-factor authentication if possible, using additional information from the account along with password and other standard criteria.
Anyone who thinks that PCI-DSS requires the letter of the rule for everything clearly hasn't worked with any Level 1 or 2 Merchants in any meaningful way.
Andre's right... option 3 is your best bet with a possible addition of option 2 in there for good measure.
Failing close is unacceptable in most business climates so PCI becomes an unreasonable request. Heck, with some companies the userIDs are simple to guess and harvest and rather than "hacking" the system it's more often just as effective to keep anyone from using it. While there is no data breach in this case, it certainly is a major risk because it exposes the business to the risk of going broke.
Another wonderful example of why compliance is never (or should never be treated) as an absolute, and why compensating controls are the magic words.
Great ideas... I like your option #3, hopefully businesses think this way, or read your blog :)
Guys,
Thanks so much for commenting.
@Andre
I am completely agree that merchants are to meet the intent of any PCI control. This is what I was getting at when I said "meet the spirit of the control" in introducing option #3. Also, I understand that PCI-DSS would never require "the letter of the rule for everything". I was just trying to present some solutions that would appear, at first glance, to do this and then present other options. Anyway, thanks for commenting. I didn't think about the second-factor authentication every thirty minutes. That looks promising too.
@Rafal
I completely agree with you that option 3 is the best choice as I say at the end of the post. I am also a firm believer, along with you it appears, that compliance does not always equal effective security. Thanks again for the post.
Interesting thoughts on this topic, and thanks for the PCI reference. But when you say that the site should "delay" its response to the client with progressively longer intervals with each failed attempt, how is that delay handled? Seems like a web server hanging onto a bunch of requests could get overwhelmed fairly easily. What's to stop some kind of DDoS attack with a bunch of invalid logins?
Jim,
What I was imagining for the delay, at least in the Java world, would be something like: Thread.sleep(delaytime);. If implemented in a servlet, this would effectively cause the thread spawned by the container to pause for a given amount of time prior to rendering the response to the browser. As such, this would occur on your application server, and not the web server. If you are running something like PHP, then this would definitely occur on your web server, and I would look at the sleep> function.
Regarding the possible DoS, there is really nothing anyone can do to truly prevent a DoS except throw large amounts of properly load balanced servers at the problem. Also remember that the delayed response does not lock the account, so a user could still login to the sight if the correct password was provided - it would just take them a few seconds to get the response back. To prevent the user from logging in, an attacker would have to gather an maintain a hold on a large amount of resources. While possible, this could take a while and is usually not worth it in the long run.
Hi your blog is cool.
I think it's a good point for debate. Looking forward to it.
How about using a CAPTCHA control after the threshold value is reached? (Assumption here is that the CAPTCHA is implemented properly and CAPTCHA itself is not vulnerable)
Jaideep,
As you say, if implemented properly it could also be a deterent, bu that would also happen to affect the legitimate user, which may not be the best thing to do.
In any case, adding it may be viable. Just be sure to "do it right".
We all encounter situations where we need to know how many accounts are currently locked out etc.
One of my Microsoft colleagues mentioned about a cool FREE and SUPPORTED tool offered by a Microsoft partner, that lets you instantly find out how many accounts are locked out, who can unlock them etc.
The tool is called Gold Finger, and it is developed by a company called Paramount Defenses. You can download it from http://www.paramountdefenses.com/goldfinger.php
We gave it a shot and really liked all the reports we could generate with it, so thought I'd mention it in case you find value in it (HEY, its FREE!)
Best,
John.
John,
Thanks for reading, but couldn't the same thing be accomplished with a siomple SQL query such as "select username from user where locked_flg = 1"?
Post a Comment