There are so many WTFs in this whole situation that it's a wonder criticker has managed to keep the website online. Which is a shame, as it looks like a really useful website.
When you are looking at a small website/API someone made for fun or something....It can normally be badly broken in less than 2-3 minutes....and I'm not even that talented like some of the guys out there.
For this reason, I don't think "I made it for fun" or "I made it over a weekend" is a good excuse for such broken security. For tiny new apps, it's easier to use a library that does it correctly than it is to roll your own.
I cannot say i am surprised though. A general amount of carelessness, undeserved self-confidence and ignorance is a given in most websites, especially when it comes to PHP.
I think that the app can only access all users registered with its api key. Same for passwords.
You say "all users registered on the site", the api says "Note, this can't be used to lookup just any user's password – the user must have been created by the API account."
Whoever created this monstrosity should be ashamed of themselves.
Due to a security breach, the Criticker APIs have been taken off-line for an unspecified amount of time.
We apologize for the inconvenience.
[0] - http://api.criticker.com/Still, WTF!?
Once after having gotten the vibe, I ended up on phone support with the site in question. At some point I was instructed to "log back in with ummmm that uhhh same password you signed up with...." I could tell that my plaintext-dar hadn't failed me that time :)
While some developers may be able to clearly identify bad practices, best practices may not always be so clear.
I'd love to know what a best practice would be for things like authentication to an API and some of the other issues brought up here.
1. Hash the secret API token/key given to each client that is sent to the server with each API request. This will prevent attackers from being able to find out your secret token.
If you only hash the secret token though, this still won't help, as attackers could just send other API requests along with the hashed token. Instead, you will want the client to hash other data unique to the specific API request as well. For example:
http://api.example.com/api_function_name?app_id=my_app&hash={hash_value}&other=stuff
Where the hash_value is computed by the client with something like:EDIT: Clarified hash_function parameters, thanks @eru.
hash_function( secret_key, api_function_name)
The API server will then receive the request, look up the client's secret_key based on the app_id, and run the same hash_function to make sure it matches hash_value in the request.This would mean attackers couldn't reuse the hashed value to send other API calls. But they'd still be able to send calls to that function and just change the parameters to the call to get other information from that API call. So, you could also include the other API call parameters in the hash_function as well, which would mean attackers could only replay that exact API call and not change any of the parameters.
You might notice, this is still not good. So, to prevent this "replay attack", you would also generally include the current datetime in the API call as well:
hash_function( secret_key, api_function_name, datetime )
Now, attackers can't even replay the exact request, because by the time they do, the datetime will have changed and so the API server would reject the request if it was replayed later. And since the attacker still doesn't know the unhashed secret_key (since it's never been transmitted in plain text), they can't change the datetime without invalidating the hash_value.This is theoretical though, because in reality the above wouldn't work well if the clocks on the client and server were at all out of sync (and they probably will be). So, usually, you'd also have to include the current datetime of the request as another parameter in the call to let the server know exactly what datetimestamp was used in the hash_function, and the server will simply make sure the datetime is within an acceptable window of the current datetime on the server. Of course, the bigger the window, the easier to get the API working with clients, but the larger the window for allowing replay attacks.
http://api.example.com/api_function_name?app_id=my_app&hash={hash_value}&other=stuff&time=datetime
And lastly, the chosen hash_function for the API should be something not easy to brute-force (meaning don't make it easy for attackers to listen to a few API calls and be able to reverse-engineer the secret_key, since they'll already know what hash_function is from the API documentation).OK, 1 was longer than I anticipated, but the others are pretty short.
2. Another more full-proof way to prevent attackers from getting secret tokens, hashed or unhashed, would be to make all API requests work only via HTTPS.
3. Don't provide API (or any) access to user passwords.
4. Don't store user passwords in plain-text, or even via simple hashes. Instead use a cryptographically secure hashing function with salts.
Use OAuth or similar and make sure every user has their own account. That's the only answer. Don't roll your own! Especially don't roll your own when you don't have a solid security background. You have obviously heard some of the right terms, but how and where you can apply them is at least as important as using them at all.
1. Doesn't protect anything at all. Hash functions don't do anything when people have access to the program code. No matter how fancy you go with time limited hashes (and there are smarter ways to create those). At most it adds a few minutes to the reverse engineering.
2. Again, this doesn't protect against anything. HTTPS stops intercepts on the wire, not someone who has access to your app, people can still lift the secret keys and the hashing scheme from the app binary.
3.& 4. Both true, and would protect against mass stealing of the passwords like happened here, but it wouldn't prevent abuse of the API.
There are very many people who use techniques like the ones you suggest in 1. and 2. and the same very many people have vulnerable apps that usually expose all users' data to the world. There are a lot of apps that store e.g. user files or some sort of configuration not on a per user OAuth protected storage like OneDrive, DropBox or Google Drive, but either there but on just the account of the developer, or on another storage that is only authenticated with the developer's credentials. People who do that allow anyone to read and modify the data of all users, exactly the same as people are allowed to do to their own data, or more if the credentials aren't properly limited, even if it's blocked in the app.
By the way, be aware than hash(string1 + string2) constructions are often vulnerable. hash(hash(string1) + string2) is better for most hashes, I believe. But you shouldn't roll these primitives yourself, either. Just use a proper library.
Certainly signing your requests as actually having come from your application is a legitimate means of security. But there are much lower hanging fruit (in terms of available security measures). Specifically, using SSL to handle API traffic. That should absolutely always be step 0. In instance of this specific post, had the API provider enforced using SSL as their transport protocol we probably wouldn't even be having this discussion.
There's a ton of other security measures/protocols that should be taken and followed, most of which are already talked about in other parts of these comment threads. I just wanted to really point out that what you're describing above sounds an awful lot like an HMAC code.
[1] http://en.wikipedia.org/wiki/Hash-based_message_authenticati...
If you can do that, the solution is pretty simple. Do it as you would do it with any website. Simply use https (TLS) to transmit username and password and return a session cookie to use in subsequent requests. Run your API over https only.
If you don't want your users to entrust client software (i.e. apps) with their passwords then use https with OAuth.
The reason why using https alone works well for web apps is that users can trust their browsers. Browsers can know the password and they probably won't steal it.
However, if you provide an API and you expect many different client apps, including some dubious ones, to use that API on behalf of your users then users cannot trust the client software and hence you should use OAuth.
The decision doesn't depend on whether or not your application stores sensitive data, because users often use the same password for different sites. So if you like your users and you provide an API for mobile apps to use, you should use OAuth.
This is called a shared secret.
Using the shared secret, you can come up with a unique signature, that only yourself and the host can generate.
You also want to use some sort of TTL for the signature, to prevent replay attacks.
Passwords should never be stored in plaintext. They should be hashed using a cryptographically secure hashing function (bcrypt is easy enough).
Password hashes shouldn't ever be exposed to anyone.
If you need to provide login functionality, provide a method that takes a username and password.
Make sure that username and password method has a backoff time to prevent someone from partying on that api (calling it with username and password combinations)
As the password has to be sent in clear text, make sure your login api is over SSL.
As far as APIs, the good ones will hash your secret key together along with other data unique to your HTTP request, in particular the headers and the datetime. This is a good idea because: 1. you are not sending your secret key in clear text 2. it makes it difficult for a man-in-the-middle attack because they cannot just take your hash from one of your requests since it will be invalid after received or if some time has elapsed.
For an example, see how Knox sets up requests for Amazon AWS: https://github.com/LearnBoost/knox
I'm confused. What do you do with that hash then?
Aside from that, I don't see a reason for an API to be able to retrieve passwords. If passwords need to be reset then the API could maybe issue a pass reset request that would email a confirmation link.
The plain text password is something that's beyond the API design, but a one way hash is generally better with an algorithm that is recognized as being secure (not MD5).
Basically, to repeat, simply not designing your own security but using recognized libraries will typically be a better idea.
Also, I like the 'handler.php' endpoint returning some kind of ugly pseudo-SOAP. Ugh.
...and an amazing number of finance organisations who can't handle non alpha-numeric characters in passwords, indicating failure to hash.
In October 2013, I received another email from Springer with my password in plaintext, and they CCed this email to another person!!
I declined my review assignments, asked them to permanently delete my account, and I haven't heard from them since.
I discovered this when on a phone call, the agent asked me what my password was, and when I refused to tell him (but offered any other aspect of my account for identity), it took a lot of convincing to get the rep to serve me. If I were malicious, I could speculate that a little social engineering may have gotten the employee to give me the password.
EDIT : To clarify, it was a comment the rep made about my password indicated that he could see my password in clear text on his screen. I asked him, and he confirmed this.
I tweeted the CTO [1], who advised me they were working on the problem, but it was still several months away from being resolved.
This level of insecurity from a major IT service provider was both shocking, and inexcusable in my humble opinion.
[1] https://twitter.com/marty_pitt/status/223622794490019843
Or they want to avoid the customer service calls "I am in Russia and use a Euro sign in my password, how do I login?!?!?!"/"Help, I have a Macbook from my brother, where is the vertical pipe (|) symbol?" so they restrict the keyspace to azAZ09 for reducing this type of error.
When I see this kind of limitation, I usually assume that they have some old mainframe with a fixed-width 7-bit password field, that would take an enormous engineering effort to change or replace.
The post gave no indication how Cricketer was storing the passwords. They may very well be stored encrypted.
You can send plain text passwords back if you've encrypted them, you just have to decrypt them first. There's no point at all in returning the results of encrypting a password if the clients don't know how to decrypt those results. Given that the API uses plain text HTTP, I doubt that the passwords are encrypted.
What the passwords are not stored as however, are hashes. A hash is not the same as text that was encrypted. A hash is a difficult to reverse unique identifier for bit of text.
Having said all this, it is funny to see your post, and all its replies making fun of security incompetency while also being incompetent in themselves.
Yes, and security-wise that's just a slightly obfuscated version of plain text.
Some modern businesses don't make the best decisions.
There is no excuse --no excuse-- for storing passwords in plain text. Anybody who attempts to justify it deserves a swift thwack in the back of the head.
The copyright is from 2004, that's only 10 years ago. I wouldn't say plaintext passwords were a sensible decision back then.
> Some modern businesses don't make the best decisions.
Some modern businesses don't have the best priorities.
I know that it'a a footnote to the insane brokenness of the rest of it, but ugh indeed. Anyone who remembered SOAP the first time around wouldn't re-invent it badly. Or at all.
I learned the hard way, that even the creation of internal APIs of software is hard, since you can make many errors. I made many errors, after I came from university. After I made them, I knew it better, because I had to manage an other developer that had to use it and I saw what a mess it was.
External APIs are even more difficult to create, because such things as security and others have to be covered ... but still it seems many companies thing any stuff scribbled by a student in the first semester would suffice.
Wait, did their API return a negative processing time?
In seriousness, recall the weev/AT&T case[1]. As I understand it, the attack was roughly of the sophistication of making a totally unauthenticated request to:
get_user_email_address.php?id=N
(where N was from a series of sequential integers)... and apparently the feds had a colorable argument that N constituted an "access control system", and therefore the act of iterating the entire series of possible N values (and downloading the resulting data) constituted "unauthorized access to a protected system".
Not quite in the same realm as coughing up plain-text passwords, I'll admit. But clearly some relevant authorities would set the bar for "access control system" fairly low. And apparently rank incompetence on the part of the site developer/owner appears not to come into things.
Weev was convicted to 3.5 years in prison for calling a public API with lots of different keys:
http://arstechnica.com/tech-policy/2013/03/auernheimer-aka-w...
In the UK, Daniel Cuthbert was convicted for typing "../../../" in his address bar:
Suddenly the fact that you were watching documentaries or movies that let you infer their political or sexual proclivities could be determined by outsiders.
Due to a security breach, the Criticker APIs have been taken off-line for an unspecified amount of time.
We apologize for the inconvenience.
And here I was, looking forward to actually verifying if this stuff was true...
From there he was able to use the key to get the users and plaintext passwords. Very much wtf.
> Due to a security breach, the Criticker APIs have been taken off-line for an unspecified amount of time.
> We apologize for the inconvenience.
Returns the password for a user associated with the API account. Note, this can't be used to lookup just any user's password – the user must have been created by the API account.
I'll help you guys integrate, or -- if you prefer, I'd be more than happy to dive into your source and help figure out problems and get them resolved. We have a pretty huge team of security experts, and we're all more than happy to help.
I'm randall@stormpath.com if you'd like to chat.
By the way, the alt-text for the portraits on your "About" page needs to be fixed.
One common way is OAuth signed requests: http://hueniverse.com/2008/10/beginners-guide-to-oauth-part-... There should be an OAuth library for the language that you are using.
What's described in this article indicates a level of incompetence far beyond any hope of forgiveness by those users. If there was any reason at all to trust the API's designers, then what you describe would be the correct response, but this is very much a case where the only rational response is to tell everyone to leave immediately, forever. It's truly an unforgivable lapse of technical judgement.
It's not the way it is because of some honest mistake that someone made, like most security bugs are. This was by design, and it's bad enough that there's no reason to believe that the designers are capable of coming up with a better design.
EDIT: As others have pointed out, he did warn them. Even more WTF then!
I've just checked and you can obtain the password through an API call
after you register a new API user.
They designed this functionality so they clearly knew it was possible, what he didn't do was explain the impact (take public key from app -> request user password) and if he hasn't notified them since that post it's entirely possible that they never had a reason to reconsider that (awful) decision. That post 4 years ago can't really be considered "responsible disclosure".Again, this doesn't excuse them, especially since we all know people reuse passwords. I'm just saying that the site is useful even if you know everyone can get in.
http://www.criticker.com/forum/viewtopic.php?f=8&t=2063#p188...
It will also be interesting to see if the company makes any warning that the average user will understand (e.g. "don't reuse your Criticker password on other sites, especially email or financial, because your password here is not secret, at all").
in 2010! It remains unchanged.
*Beat to the point by pille
No thanks!
Stealing 2853 user's passwords, which are stored in plaintext, sent over HTTP isn't an issue? This wasn't an SQL injection, the API gives it away.