
How not to write an API - gebe
http://ghost.teario.com/how-not-to-write-an-api/
======
Mithaldu
Short version: [http://criticker.com](http://criticker.com) sells access to
their API for apps. Any API account can retrieve a list of all users it
registered on the site, then retrieve the cleartext password for each user it
created.

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.

~~~
theboss
I dont think you realize how common the WTFs in this situation are. If you are
dealing with a reputable company, this is super super WTF.

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.

~~~
Mithaldu
My day job is web developer and i sit in an IRC channel where roughly half the
traffic is making fun of security issues of sites. Such a glorious combination
of fuckups doesn't come about that often. I'm honestly more apalled that the
passwords are in plaintext than that they expose them like that.

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.

~~~
theboss
I'm not sure I agree with ``carelessness, undeserved self-confidence'' but I
definitely agree with ignorance.

I think the best thing is that people writing code just don't understand the
internals of how a lot of web attacks work and why the best practices for
security prevent them.

I reported two account hijack vulnerabilities on startups this weekend and was
met with ``What is CSRF?''.

I think the reason for this is security people keep to themselves and work as
consultants. Instead of making my own company, I can go around finding people
who need what I know and get easy low-risk money.

Also, there really is no place to hire a ``security person'' at a early stage
startup. If a security guy DOES get hired, it's either part of an IR team, an
internal pentest team, and if this is the case then there's already way too
much code written to even check it for the most basic best practices (you
might use some tool for static analysis but are you really going to check out
the thousands of issues?)

I don't like bashing PHP, because I think it is actually a phenomenal language
for people who know what they are doing, but it is definitely a security
death-trap for those who don't.

~~~
camus2
@Killswitch

Problem is PHP is a templating language, not a generic purpose one. Other
languages use frameworks for webdevs that usually provide basic security
features like auto escaping output,orms by default(so no sql strings),csrf on
forms...

PHP doesnt ,so it's easier to shoot yourself in the foot.

~~~
jimlei
You mean like Symfony, Laravel, etc? Frameworks that a lot of PHP developers
use these days...

~~~
camus2
You dont need a framework to do PHP webdev, in every other languages,you
do.That's my point, PHP IS a templating language,no Symfony,Zend or Laravel
can change that. If i write "print" in Python it wont output the result back
to HTTP like PHP does. Ruby or Java dont have <?ruby or <?java tags, you get
my point.

~~~
smacktoward
_> Ruby or Java dont have <?ruby or <?java tags_

I dunno about Ruby, but Java certainly does:
[http://en.wikipedia.org/wiki/JavaServer_Pages](http://en.wikipedia.org/wiki/JavaServer_Pages)

------
damon_c
Whenever I get that plaintext password "vibe" on a site, I like to make my
password something somewhat degrading go the site; like "thisSiteSux!", but
slightly more vulgar. It's not my fault if they see it.

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 :)

~~~
MarkTee
Did you make sure to feign forgetfulness and have them read it back to you?

------
Osiris
Could someone with a solid security background provide a example of how to
properly handle the issues that this API fails so badly at?

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.

~~~
JangoSteve
I wouldn't say I have a solid security background, but there are four best-
practices I can think of that would have prevented the security
vulnerabilities outlined by this post:

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.

~~~
eru
You can also include an extra random number in the hash, and require that
within the window of acceptable timestamps the random numbers have to be
unique.

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.

~~~
Genmutant
Do you have specifics on why strcat would be vulnerable? Or is it just because
"abc"+"def" == "abcd" \+ "ef"?

~~~
eru
That, and length extension attacks
([https://en.wikipedia.org/wiki/Length_extension_attack](https://en.wikipedia.org/wiki/Length_extension_attack)).

------
mercurial
Somebody is trying to outshine Mt. Gox in terms of amateurism. I wouldn't be
surprised to find a number of other vulnerabilities (SQL injection ?). Who the
hell thinks it's OK to store non-encrypted passwords in this day and age? It's
not like you don't have a major security breach every month...

Also, I like the 'handler.php' endpoint returning some kind of ugly pseudo-
SOAP. Ugh.

~~~
scoot
" _Who the hell thinks it 's OK to store non-encrypted passwords in this day
and age?"_ You'd be surprised:
[http://plaintextoffenders.com/](http://plaintextoffenders.com/)

...and an amazing number of finance organisations who can't handle non alpha-
numeric characters in passwords, indicating failure to hash.

~~~
mschuster91
Or they want to avoid the problems that can arise with multiple systems having
to authenticate at the system - some of which might use non-exchangeable ways
of encoding stuff like Umlauts.

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.

~~~
mschuster91
Self-reply, as the edit window is out of order: don't forget mobile devices
(the support of software IMEs for anything outside the alphanumeric range is
spotty to say the best), and as you're talking about financial institutions,
also think about ATMs and PoS terminals where the only thing you have
worldwide available is a 0-9 keyboard.

------
PythonicAlpha
It seems to me, that many companies think, that computer science is just
plainly simple. You can just put any task to a new bachelor or even student
that claims he can program.

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.

------
aashishkoirala
I don't care if this comes off as trolling, but here it is: as I read through
this, I thought to myself, much like the author, "how appaling!" \- then I saw
the word "PHP" \- and went "oh, well, that figures".

~~~
Killswitch
I don't care if this comes off as trolling, but here it is: as I read through
this, I thought to myself, much like the author, "how appaling!" \- then I saw
the word "PHP" \- and went "oh, well that means there's gonna be a bunch of
people hating on a language because one developer doesn't know what he's doing
and happens to be using that language".

~~~
hamburglar
All the php hate I've seen over the years is because of _one_ guy who doesn't
know what he's doing? ;)

~~~
Killswitch
Oh yeah, I forgot, because of the low barrier of entry, PHP is the only
language in the history of all programming languages where there are people
who don't know what they are doing. Forgot that all Python, Ruby, etc devs are
gods gift to programming and don't make mistakes. My good, your bad.

~~~
hamburglar
upvoting you despite your inability to take a bit of good-natured ribbing

~~~
Killswitch
Sorry, just as a PHP developer who understands his language isn't perfect but
is growing and fixing pains, it annoys me to see people who don't use or
haven't used the language since PHP4 talk trash about it as if they've been a
PHP developer for years and use it daily.

Rarely do you see PHP developers talking about the pitfalls of big frameworks
and other languages.. For example XSS vulnerabilities that recently plagued
Rails.

I too have upvoted both your replies to show no ill will. Beautiful day here
in Chicago and I took a little time off to cruise the city before coming back
and getting back to work. Really puts you in a good mood.

~~~
hamburglar
PHP's public image problem is mostly due to the direction from which you can
approach it. You can be the maintainer of a static HTML page who knows nothing
at all about programming, and someone can help you dip your toe into PHP by
showing you some simple tricks, and suddenly you have the ability to start
adding code here and there, copy/paste style, to your content. The results are
predictable. And for better or for worse, the language has been given some
ill-advised features in order to cater to this type of user.

And you can approach it in the opposite direction, as well: as a programmer
who knows what he's doing, you need to choose a language and a platform to run
your app on, and PHP is ubiquitous and perfectly capable, and you know which
ill-advised language features to avoid, so it's a reasonable choice.

The difference between PHP and most other languages for building web sites is
that with most other languages, you simply can't follow the first path. Web
servers that let you just drop snippets of ruby into HTML are not everywhere
(I assume that exists in some ill-advised apache module, but I haven't
personally seen it). You need to know a significant amount up front to get
your code running, so the "never even considered programming before" portion
of the population is naturally suppressed. It doesn't mean they don't exist or
that somehow making a language more difficult to use automatically makes its
users better, it just means that a certain type of amateur user is naturally
suppressed, so there is proportionally fewer of them.

Note the similarities: ActionScript/Flash is actually a pretty cool piece of
technology [let's ignore their poor track record with regard to security holes
in the runtime, since that's an orthogonal issue]. And yet it's terrible to
program in for a seasoned developer because the community is so chock full of
artists and amateurs who just dipped their toe in to add some minimal
interactivity to their drawings that you often have a hard time finding docs
that are written above the copy/paster level. And well-written ActionScript
code is a relative rarity as a result. I'm sure that on at least some level,
any seasoned dev who primarily uses PHP can relate.

~~~
Killswitch
> The difference between PHP and most other languages for building web sites
> is that with most other languages, you simply can't follow the first path.

I agree, but funnily enough, I started at the first path, and if it wasn't for
that path in 2001, I wouldn't have made it to the second path.

------
minimaxir
_< RequestProcessingTime>-0.036264</RequestProcessingTime>_

Wait, did their API return a negative processing time?

~~~
greglindahl
When you trade security for speed, amazing things can happen!

~~~
viraptor
What are the chances of code reviews for free time? (processing_time = start -
end ?)

------
d64f396930663ee
It's a felony in the US to do what the author did here, right? Not that
there's any indication where they're from, I'm just curious.

~~~
feralmoan
I'm going to say FU to the industry and buy a horse ranch if it is! These were
all public documented endpoints and 'worked as intended'. criticker is next-
level incompetence, that's pretty much the point.

~~~
jamestnz
Well saddle-up, my friend ;-)

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.

[1]
[https://news.ycombinator.com/item?id=4808676](https://news.ycombinator.com/item?id=4808676)

~~~
feralmoan
Really wish there were better defense attorneys onboard in these cases because
at their core these instances boggle the logical mind.

------
philjackson
Despite the warning to the company back in 2010, I'm not sure he should be
publishing this. He's putting the 2000-odd users at risk by teaching us how to
get their passwords and usernames like that, it's even worse if we can get at
email addresses too. I would bet the majority of those registered reuse the
passwords.

~~~
bm1362
It's not particularly sensitive data being jeopardized- isn't it just movie
reviews?

~~~
choult
Given the prevalence of password reuse, exposing user passwords is never a
good idea.

~~~
mikeash
Just to drive this home a little more: a lot of your users will use the exact
same e-mail address and password on your site that they use for their bank.
And while they shouldn't do that, they will, and that's why you should use
best practices to protect your users' credentials even if their account on
_your_ site is completely unimportant.

~~~
bm1362
Ah, yes, I missed the "I would bet the majority of those registered reuse the
passwords." from parent.

------
victorhooi
Well, it seems they took the entire API down in response:

[http://api.criticker.com/](http://api.criticker.com/)

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...

------
austinz
Wait, just to be clear - so anyone who downloads this app can trivially
retrieve the username and password for all 2000+ users of the app? Did I
misunderstand the article?

~~~
k1kingy
Basically yes. As he did, he managed to get the API key by doing a TCP dump.

From there he was able to use the key to get the users and plaintext
passwords. Very much wtf.

------
dgarrett
From [http://api.criticker.com/](http://api.criticker.com/)

> Due to a security breach, the Criticker APIs have been taken off-line for an
> unspecified amount of time.

> We apologize for the inconvenience.

------
octatone2
What the, why? Who thought this matched any sane API authentication pattern?

------
jbeja
It give me chills when i read this quote:

 _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._

------
rdegges
If anyone from the Criticker team is here on HN, I'd be happy to help you guys
get this resolved -- my company Stormpath
([https://stormpath.com/](https://stormpath.com/)) provides a really secure
way to handle user accounts.

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.

~~~
austinz
This looks like something a lot of startups could benefit from, even those who
can't be arsed to care one whit about security otherwise.

By the way, the alt-text for the portraits on your "About" page needs to be
fixed.

~~~
chunsaker
Thanks! The website is shiny new, and I added this to our list of bugs to
squash. Any and all bugs/suggestions welcome --> claire@stormpath.com

------
SideburnsOfDoom
In case anyone is wondering, there are ways to use API keys securely, i.e.
without sending them in plaintext on each request.

One common way is OAuth signed requests:
[http://hueniverse.com/2008/10/beginners-guide-to-oauth-
part-...](http://hueniverse.com/2008/10/beginners-guide-to-oauth-part-iv-
signing-requests/) There should be an OAuth library for the language that you
are using.

~~~
jcromartie
Ways like HTTPS?

~~~
SideburnsOfDoom
That is a different way. Which is most appropriate when is commented on here:
[https://news.ycombinator.com/item?id=7371484](https://news.ycombinator.com/item?id=7371484)

------
LukeB_UK
I hope that the author notified Criticker about these issues before putting
them out there on the internet. Not doing so would be extremely irresponsible
and is sort of screwing over the users of Cricketer.

~~~
mwfunk
They've already screwed over their users to such a degree with this
implementation that the only sane thing to do is to warn all of the user base
to stop using it at once and never go back.

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!

~~~
icebraining
Not trying to excuse Criticker, but from my POV as a user, this isn't exactly
Mt.Gox or a bank; it's a website to rate movies, and all the information you
put there is already public. All someone can do with my password is rate
movies on my behalf.

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.

~~~
makaveli8
A lot of people reuse passwords across multiple sites - so nab their plain-
text password + find out their email address, and you likely able to log into
various other services using their credentials.

------
Sami_Lehtinen
Well, bad APIs and security is the norm, unfortunately. As example
DigitalOcean doesn't use signatures, and uses static API key. Would you
consider that to be secure? Especially if we aknowledge all the weakness of
SSL/TLS/HTTPS.
[https://plus.google.com/+SamiLehtinen/posts/1qFhf9fAbU6](https://plus.google.com/+SamiLehtinen/posts/1qFhf9fAbU6)

------
tyrick
Raw password storage is more common than we like to believe. A simple way for
webapps to communicate that raw passwords are not being stored would be
convenient. A small 'NORAWPW' image in the footer perhaps. it would ease my
worries, especially with cryptocurrency related webapps.

~~~
psquid
Unless the footer image in question was awarded by some kind of auditing body
(with links back to their page so a visitor could verify the site really was
audited), an idea like that has no fangs - for example, what's to stop a site
storing them in plaintext because "it's easier, and we'll fix it when we have
time" but throwing the image in the footer anyway to ensure they don't lose
users in the meantime?

------
akfanta
Despite all the plaintext password nonsense, I still can't wrap my head around
the fact that there is an API call for getting passwords. What legitimate use
could it possibly be?

------
faazshift
Wow... storing passwords in plain text? Even worse, non-encrypted data
transport and client accessible credentials? Those "programmers" should be
shot!

~~~
nyrina
If we started killing people for making mistakes, none of us would be alive.

------
famo
My opinion is that any company building an API should run at least one bug
bounty on it before releasing it to the public.

------
thailehuy
I'm not sure what is worse, the bad API/app design, or the blog post publicly
sharing how to abuse it...

~~~
ephemeralgomi
For future reference, the bad API/app design is worse.

------
agapos
I am waiting for an API for a specifically designed API-maker software named
"Yo dawg".

------
Kiro
How should an app utilizing an API send the API key so it can't be hijacked
with tcpdump?

~~~
CornishPasty
By using HTTPS/SSL. But the onus of that is on the API provider...

~~~
thedufer
It also doesn't help that much - you can still look up the api key in the
package, which isn't a whole lot harder. You could probably sign your own
cert, tell your device to trust it, and MITM it, too.

------
joetech
Someone please change the title to "How not to disclose a vulnerability"

------
teemo_cute
To those of your interested on the topic, leanpub has an ebook you can get for
free here:

[https://leanpub.com/yourapiisbad](https://leanpub.com/yourapiisbad)

~~~
Killswitch
Here's another good one, mainly in PHP [https://leanpub.com/build-apis-you-
wont-hate](https://leanpub.com/build-apis-you-wont-hate)

------
digitalpacman
This post is more about security than just APIs... dislike title. Also.. I
don't see how this is an issue. If the user signs up via your app... and you
wanted their password. You have it. Sure it's a big deal if someone steals
your key... but if you always do it over SSL, they have to steal the "phone"
or the "app" that you use. And if they steal the phone... they can use things
like "email reset password", because email will most likely be logged in
anyway.

~~~
makaveli8
The problem is that the app uses the same API key no matter what device it is
installed on. If you download the app today you can find the API key and use
it to retrieve the passwords for any users that have signed up to Criticker
using the app - just as the author has done.

