Live data from Hacker News

How not to write an API

ghost.teario.com

81–90 of 172 posts

Re: How not to write an API

#81
post #59

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.

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.

Re: How not to write an API

#82
post #38

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

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

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

Re: How not to write an API

#83
post #58
post #48

Earlier quoted context omitted.

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 i…

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

You've almost got a point there except PHP has quite a few frameworks to choose from. Mainstream ones all include, as you put it, "basic security features like auto escaping output,orms by default(so no sql strings),csrf on forms".

Symfony2's form component is one of the best I've ever seen. It has many sane defaults and is locked down out of the box. Read up on their use of data transformers and how they protect users against XSS without any extra effort by the user. Validating automatically looks for a CSRF token.

Whats more is that these are (for the most part) stand-alone components. You don't need to commit entirely to the full Symfony stack - you can just opt into using the components you need -- even the smallest project that only uses PHP as a "templating language".

Re: How not to write an API

#84
post #59

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.

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…

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.

Re: How not to write an API

#85

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.

> Who the hell thinks it's OK to store non-encrypted passwords in this day and age? 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 resu…

> You can send plain text passwords back if you've encrypted them, you just have to decrypt them first.

Yes, and security-wise that's just a slightly obfuscated version of plain text.

Re: How not to write an API

#86
post #48

Earlier quoted context omitted.

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 give…

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 i…

"I think the reason for this is security people keep to themselves and work as consultants."

???

I know there are some pretty obscure edge cases in some successful attacks, but... almost everything I see as a security issue (and stuff I've done myself) usually falls in to XSS, CSRF and SQL injection. Those were the big 3 10+ years ago, and probably still will be. This isn't some magical 'hidden' info that a handful of security consultants hoard to themselves to maximize top dollar.

Have safe password/authentication systems, prevent XSS, prevent CSRF and prevent SQL injection - you'd prevent a HUGE number of attacks for little effort. But... it takes education, and actually caring some about your job/company/product.

"Also, there really is no place to hire a ``security person'' at a early stage startup. " Why not? I don't think they need 'hiring' full time. Many startups spend inordinate amount of time on 'user experience' and 'branding' and whatnot, with the (correct) understanding that you can't easily just 'add on' UX after the fact - it's much easier to develop UX as part of the overall dev process. Why do people not think of security the same way? Regular security audits/reviews by a security consultant (1-3 hours every week or so) would go a long way towards helping inexperienced developers spot gaping/obvious security holes well before they become big problems.

Re: How not to write an API

#87
post #78
post #67

Earlier quoted context omitted.

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

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.

> Ruby or Java dont have

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

Re: How not to write an API

#88
post #59

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.

Usually with an APIkey, you have a corresponding "Secret" Key.

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.

Re: How not to write an API

#89
post #78
post #67

Earlier quoted context omitted.

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

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.

If you use them in CGI mode, you very well have a "start" tag in form of a shebang first line like #!/usr/bin/env python.

Also, shellscripts and perl are considered programming languages, and these too need a shebang line. Your point is invalid.

Re: How not to write an API

#90
post #66

Earlier quoted context omitted.

> I like to generate a hash on the client side I'm confused. What do you do with that hash then?

It is sent to the server, instead of the clear-text password. This isn't really necessary if you're using HTTPS, however.

[deleted]
Post reply on HN