This would be improved by a recognition that security involves trade offs, particular when you have existing apps in production that you don't want to break compatibility with. That isn't a laughable consideration: "A security problem in customer's code? Fire them as a customer!" Would indeed be effective at reducing their exposure but is not in their interests and has certain drawbacks with regards to one's odds of remaining employed as a security professional. Many apps - probably most apps - are not under active development, because the customer believes them to work. Sudden breaking API changes are bad news for them, possibly irreversibly bad news if e.g. the original developer can't be located to fix the problem.
A more sensible resolution to this issue, as a hypotherical security professional at an API company, would be moving more of the burden for security onto the company rather than the API consumer, by e.g. limiting the downside risk of a credentials compromise. (Similar to how banks don't say "The bad guys got your password? Sucks to be you, your balance is now $0", one would be well-advised to eventually have something on the roadmap to e.g. disallow unauthorized or poorly considered code from causing business catastrophe prior to getting a human in the loop.). You'd also want to e.g. make sure your sample code is secure of of the box, and write your first-party libraries to "just work" to the maximum extent possible, such that greenfield development would tend to be secured by default.
That's being pretty generic, but it's my understanding that many API companies actually do put quite of bit of work into internal anti-abuse tooling, for this and related reasons.
I don't think this is something you can mitigate with libraries, especially not as you need client libraries for a dozen or so languages and I (at least) wouldn't be willing to install software just to try out some service -- you pointed out in your blog how much users dread installing software, in my mind client libraries are even worse as you have to install them and their dependencies (which may or may not conflict with your own) which may involve more or less crappy tools (e.g. my JRuby build now depends on maven, because I need to use your client libraries) before you can even test the service.
- Ensure that any client libraries you supply for API interaction must verify the server certificate
- Only use the users password to obtain a long random key from the server either through an admin web interface where possible or by an API request that supplies the username and password and receives the key.
- Make authenticated requests of the API with the key
- Store any passwords as bcrypt hash in the DB for security
- Only store the key as SHA2-256 hash in your DB for performance
- Actually yes do use HTTP Basic as it's a convenient way to transmit the key (see how Stripe does it) making things easy for your users and perfectly secure as long as you prohibit plain HTTP.
- Stay the heck away from HMAC's and signing requests, you have SSL, properly tuned it's secure. HMAC is pointless here and really confuses the users.
I'm mentioning tuning your SSL ciphers a number of times in this thread. It really is quite important. For Nginx you should compile it with the latest OpenSSL (1.0.1e right now -- you can statically link it if you dont want to mess up your OS's packages) to get the latest ciphers.
Because if someone gets access to your database somehow you're not exposing all of the keys for all of your users.
While you're likely in serious trouble if someone gets your DB this helps minimize damage. Besides to authenticate a request you dont need the plaintext of the key, a hash is fine.
I guess the trade-off is here that the api key cannot then be retrieved from a GUI either (because the server doesn't actually know the original key!) .. from a UX perspective, most companies show your active keys in a GUI with ability to selectively remove or replace.
Though I can't think of a mainstream API that hasn't let me retrieve api key from GUI
Storing SHA in the database or anything not produced by PBKDF2, bcrypt or scrypt is wrong and doesn't help you much "minimizing damage." Advising SHA doesn't seem to come from a real professional.
I don't think you understand the distinction here, this is a random key, not a user generated password. The purpose of using something like bcrypt is to protect against brute forcing hashed passwords.
You cannot brute force search against a large random number that is hashed with unsalted SHA or the like. This would require you to guess the number, hash it and check to see if the hashes match. A large random number encoded as a 64 character string is simply too big to guess, even at millions of guesses per second.
Thus using bcrypt to protect your api keys does nothing but impose a serious bottleneck in how many api requests per second you may authenticate.
SHA is OK, if you have a long random key. I forget the numbers, but a decent random key (100 chars?) becomes impossible to crack before the heat death of the universe. The exponential growth of the password space eventually defeats the weakness of SHA.
The reason you need bcrypt is that user supplied passwords are crap, and can be cracked very quickly.
Yes, I agree that the length of the key and the real randomness are the most important points and that when those are fulfilled the recipe has sense. Neither explicit length minimality nor real randomness of the key were mentioned but the SHA advice was explicit to the 256 bits.
EDIT: To make it clearer: "Long" is not explicit. 256 bits is. The difference between real randomness and for example output of RND in your favourite language is also nowhere to be seen. Moreover the use of the "key" is dubious as it's not an encryption key at all.
Storing the SHA-2 hash of a 256-bit random key obfuscates the API-key, so an attacker with access to the DB can't use it, but is fast to check so will remain performant.
It doesn't matter how many machines you have or how fast you can compute the SHA. An attacker will not find the API key (work out the math).
Sorry I was a little inexact, when I said SHA-2 256 I meant specifically the SHA-2 256 bit (SHA-256) hashing algorithm as opposed to SHA-1 which is considered weak. Not the key length. Though I suspect a 64 character long random string has sufficient entropy.
This article just makes a big huff about nothing.
go to http://api.stripe.com - what you get is immediately a redirect to https, and a 401 which requests the auth. It does not request http basic over http, it requests it over https. The issue is if you are a dumb developer, you might supply authentication over http, which would make that insecure. However, what I see it is requesting is an API key, which normally with payment providers, only allows making a charge, or refunding a charge, provided you know the charge ID. So your financial risk is very low even if someone knows your API key. They also usually recommend you change your API key at least once a month.
You're only using SSL which protects you vs relay attacks (it's doing its own MAC). Authentication is being done through the HTTP request Authorization header and the attached pre shared key.
Use SSL only, tune ciphers and you're good. You're wasting time trying to overthink the problem by adding another layer on top.
If you use a broken library then you're in trouble no matter what MAC wizardry you layer on top of it. You've got a core issue that is a better place to spend time solving that coming up with some sort of MAC layer to add on top.
We've thought about closing HTTP access in the past. As with a lot of security, it's a usability trade-off.
We work hard to make sure that people only ever access Stripe over HTTPS. Most people use the Stripe API through an existing library. Since the API doesn't work over HTTP, any such library is guaranteed to use HTTPS. Stripe itself is on Chrome's built-in HSTS list, sets HSTS headers, etc.
The scenario Stevie describes is unlikely to be an issue unless the user is implementing his or her own library. In that case, though, they're almost certainly going to be using test API credentials. Stripe's API will never return a successful response when you access it over HTTP, so you'll figure out that you can't do this on the very first request.
More broadly, what exactly is being defended against isn't particularly clear. There are much easier ways for someone implementing their own library to screw up (skipping cert verification is extremely common and leaves you perpetually vulnerable to HTTPS MITM), and there are many other plausible vectors for an attacker.
Even if we did change this, we'd only have solved one small class of accidental key leaks. You could accidentally specify "stripe.com" instead of "api.stripe.com", for example, and stripe.com will of course always accept HTTP. Or you could typo the domain and send your key to someone else entirely.
If we ever did want to solve this, we'd probably do something like:
- Monitor HTTP Authorization headers sent to api.stripe.com.
- Notify the user if they ever accidentally send a secret key in the clear.
- In addition to notifying the user, we could potentially roll the key automatically (though this runs the risk of breaking production code).
But I don't think disabling HTTP helps much. (I do think it'd be good if we started monitoring the frequency with which this happens -- if it's common, we should certainly do something like the above; if it ~never happens, it presumably doesn't matter.)
Separately, I think that the HMAC-based signature scheme that Stevie proposes is something we might want to do at some stage. A very early version of Stripe actually had something very similar. The issue with all protocols in this vein is that they involve considerably more complexity on the user's end and are harder to debug. But they definitely have some nice security properties.
If we're worried about a man-in-the-middle attack sniffing insecure credential on port 80. If the host closed port 80, couldn't the man in the middle just open it up again, you know, in the middle?
Maybe one recommendation should be supported vendor supplied SDKs which don't touch HTTP, only HTTPS. Another option is to detect credentials being sent over HTTP and notifying the clients, maybe deactivating their key if action isn't taken in a period of time.
I think the idea is that if the real port 80 never answers, real customers are highly unlikely to set up their apps in the vulnerable state in the first place. That said, having port 80 open, but returning an appropriate error code to all attempts to use the API, would probably have the same effect (while still answering on port 80, for anyone who just wants to ping it to see "are they up" --- one of the excuses the OP has heard for leaving port 80 open).
Right, but the problem will be caught when the app is still in development. Then the devs will fix the app and by the time it gets to end-users, it won't do that anymore.
This is an important issue, but as the article says, Auth Basic over SSL is perfectly secure (if you don't redirect 80 to 443 of course). And since it is so simple to set up and use, I can see why some providers would use it (I do for example).
You see? When you put it that way it sounds stupid; otherwise it has beautiful words like "butchering" that convey additional feelings about the subject :)
Don't use the "signature" gem. Rails already provides a MessageVerifier class that wraps OpenSSL's HMAC; the "signature" wrapper uses "==" to compare HMAC values, and is thus timeable.
Really, don't use HMAC-of-the password authentication tokens at all, though. They're not nearly as secure as TLS.
Better still, don't use passwords in your API at all. Passwords are for humans. APIs use keys.
Better still, don't use passwords in your API at all. Passwords are for humans. APIs use keys.
You make great points, but just as a point of clarification (because I saw numerous people confused by this yesterday), this is merely nomenclature, no? What you are calling a key is really a long, random password (in Amazon's case the secret Access key).
Nomenclature is important. As the joke says, naming things is one of the only two difficult problems in computing. [1]
A good password is a long string of random characters, and an API key is a long string of random characters, but they are not the same because the semantics are different. Passwords are typically chosen by customers, changed (let's be honest) very rarely and on a haphazard schedule, often reused across a bunch of different systems no matter how frantically we wave our hands, and (most important of all) have "user" semantics and scope: a password lets a user log in and do logged-in-user stuff.
Whereas an API key can be issued by the system, and is thereby guaranteed (assuming a clueful programmer) to be long and random and unique and not shared with the customer's Gmail account and bank account. You can expire it often. You can issue more than one per customer. (With passwords, this is theoretically possible but practically impossible; customers barely understand how to handle one password.) You can issue it to entities that aren't themselves customers, and that don't have "user" semantics. You can scope it to particular uses, issuing a key that is only good for checking balances but not for initiating transactions, for example.
---
[1] The other being cache coherency. And avoiding off-by-one errors when enumerating lists.
You also hash the two differently in your database. With passwords you want to bcrypt (or similar) them to protect against exposure of the DB and an attacker brute forcing the hashes. Since this is virtually impossible for long random strings you're able to just SHA2-256 the keys for DB storage. This becomes important because you're likely checking the key every API request and bcrypt would be a significant API rate limiter.
Doesn't exactly the same concerns arise? A single purpose may be a major purpose, such as "transfer monies between accounts", and aren't exactly the same concerns relevant between passwords and keys?
If someone gets a list of unencrypted (or poorly encrypted) passwords, they have the rights of the user. If someone gets a list of unencrypted (or poorly encrypted) keys, they have the rights of the API caller. The concerns seem identical.
The difference is that you can, and should, rotate your API keys automatically and fairly frequently (I will leave it to experts to tell us how frequently is prudent.)
Moreover, when you think your DB has been compromised you can flush all the API keys and reissue them. Depending on the application this may be more or less of a pain for the customers, but at least it is possible, because the keys are random and generated by you and not shared across services.
These facts mean that API keys, properly implemented, have a much smaller attack surface. You need not worry, for example, that a three-year-old backup file that someone harvested out of the trash at your ISP has valid API keys on it. The window for attack is, in fact, limited to "someone has stolen a very fresh copy of my live DB, but I have not yet realized it, and there is still something valuable left to compromise other than the data in my app's live DB." And this window often isn't very important. Often, closing it is akin to reinforcing the barn door after the horse has bolted and the barn has burned to the ground.
Passwords, on the other hand, get reused. You are using bcrypt to hash passwords not just to protect your app, but to protect your customer Gmail accounts that share the same password.
In common usage the user will take an server generated API key and embed it within their application that makes API requests. Take a very typical example of a merchant selling snowboards and using Stripe to process the payments as an example.
Since this API key is embedded within their app it'll require a programmer to change (and customers often may be non technical with sites built on contract). These are things you dont want to force customers to change with any frequency as it can cause pain and expense. You dont want to ever be forced to flush all your API keys, it could be a serious disaster.
Now there's protocols that rollover access keys hourly or the like such as OAuth a but that's a complex and often unnecessary use case that's beyond most API implementors. See how Google does OAuth 2.0 with their APIs for an example.
Since it's secure, simple and performant to hash API keys stored in your DB and just compared hashed values for authentication why would you ever not want to do it?
If you lose the database containing either your API keys or hashes of your API keys, you need to revoke the keys. Not doing so is negligent.
The correct next step after discovering that you may have lost custody of API key information is to ensure that the keys are worthless, not to scramble to try to protect the keys.
This is another reason you want to be clear about the difference between a "key" and a "password", because passwords are sticky. A benefit of working with keys is that they are not; you can zorch a key with minimal provocation.
If you lose your DB you're in trouble regardless. We're talking about the level of trouble here. I'd rather be faced with a 30 day key rollover of all my users than a forced immediate change.
I dont see any reason to continue the argument. It's clear to me that you should hash your API keys in your DB.
Would you mind enlightening us? If you're using API keys over TLS+HTTP Basic Auth, storing the plain API key in your database means that anyone with readonly access to the API key table can now use those accounts. (Ignoring IP filtering or something.)
If an attacker gets access to the key table, you are boned. Revoke all your keys. Make the keys worthless; don't take half-measures to try to protect them.
... I agree if you know an attacker has access then you should revoke. Just like if you know an employee has been compromised. But why wouldn't you store them hashed, anyways? So that if an employee has to debug something and selects more than they should, your system is not compromised?
Also, I'd say that a SHA256 hash of a 256-bit key is hardly a "half-measure". Nothing short of compromising your RNG or SHA2 will make those hashes useful to authenticate.
Arbitrary distinction. A password can be expired and forced to comply with complexity rules. A key is a server-side generated password, and therefore offers less ability for users to implement their own password policies. Good for users that don't care, bad for users that do care.
This is not arbitrary at all and has definition by industry standards. Passwords are used by people. Keys by machines. This requires different security policies. Passwords will have complexity rules and shorter expiration policies such as 90 days. Keys should have rollover policies around a year or so.
You say that it has definition by industry standards, but no such standards exist: There is no standard on the complexity or expiration policy of either passwords or keys. There is no standard on rollover policies.
Incorrect. The PCI-DSS is an industry standard that everyone who touches credit cards must adhere to, which I suspect is a good portion of the audience here.
See section 8.5.9 for the password change policy. Section 3.6.4 for the key rollover policy (which references NIST 800-57 that basically says 1-3 years for most keys.
There's other standards (such as the 1-5 years for an SSL certificate from the issuer) and tons of best practices out there. In short you should be rolling over keys and expiring passwords on some sort of schedule. My point was that because humans use passwords they are conventionally seen as needing quicker expiration than keys.
The PCI-DSS deals specifically with cryptographic keys used to protect CC details. It is in no way a standard for API keys. Even then it is enormously vague in its rules.
There are lots of organizations with notions about passwords and keys and ciphers, etc. However in the context of this discussion I don't believe there is anything that could be considered a standard.
>> Better still, don't use passwords in your API at all. Passwords are for humans. APIs use keys.
> this is merely nomenclature, no? What you are calling a key is really a long, random password
Both passwords and keys are shared secrets. A password is a secret a human remembers, and can prove he/she remembers. A key is something a machine stores and can prove it has.
(Actually, an API key can also be a token, eg: "{ hash: " + hash(key+salt+date(now))+"valid from: " + date(now) + "salt: " + salt + "}" -- that is -- if you know the key, you can verify that that the token is indeed valid from the date claimed (and you could add serial numbers and other stuff as well). Note that the user doesn't need to know the key in this case, the user only needs a copy of the token).
There's a very good reason for using HTTP basic auth: if you want to store password hashes on your server using a modern scheme like bcrypt or PBKDF2, you can't use digest authentication. The author suggests rolling your own authentication scheme based on HMAC-SHA, but then all the clients need to implement this scheme whereas HTTP basic auth is already part of most HTTP libraries. And do you really trust yourself to get the crypto right?
HTTP basic auth over SSL seems like the way to go - and you need to use SSL on your API anyway to prevent hijacking. The points about not allowing HTTP access to the API make sense, but avoiding basic auth entirely isn't a good security tradeoff.
I did not suggest "rolling your own crypto". HMAC is detailed in an RFC (http://tools.ietf.org/html/rfc2104), is widely used, e.g. Amazon AWS, and is part of OpenSSL. At no point do you have to "roll your own". If you don't implement this correctly the request will simply fail, the credentials won't be exposed at any point. Please spare me the FUD.
HTTP Basic Auth is actually a great solution as long as you've blocked non-SSL traffic, so I half-agree with you. Absolutely close port 80 so it's not possible to accidentally attempt basic auth with the server in the clear.
The bit I disagree with is this:
As well as being tremendously simple, HTTP Basic by itself is also tremendously insecure, i.e. it is implemented by simply Base64 encoding the username and password concatenated with a colon “:” character. It then follows that HTTP Basic should only be used, if at all, over securely encrypted connections.
I agree - HTTP Basic is…well, basic. But you imply that even over SSL, HTTP Basic Auth is only marginally acceptable, and that's not the case. Everything you do securely online is transmitted in plaintext behind SSL. If a vanilla API key behind SSL is barely secure, you're pretty much hosed.
What's always needed is simply properly used TLS (https). Even wording in the article is misleading. It's not about "closing port 80" but about "using only TLS."
Once you use TLS is the "HTTP basic" often the optimal solution. After reading the whole article, I still don't understand why would anybody try to make HTTP "secure" instead of simply using TLS. So my question to the author, paraphrasing the article title:
Why the hell simply not using TLS and thereby avoiding the "basic auth is bad" misdirection?
It's pretty much like forms based auth which is used by virtually every user facing web site with a login, wherein the data you send over is plaintext.
Isn't this is a problem with the client, curl in this case? According to the spec:
> HTTP provides a simple challenge-response authentication mechanism which may be used by a server to challenge a client request and by a client to provide authentication information.
The client should make two requests. The first with no credentials to see if the resource is protected (the challenge part). The server will then respond with a 401 Unauthorised status and a WWW-Authenticate header indicating what authentication to use. The client will then make another request with the given authentication.
The insecurity is caused by curl not doing this challenge response, it is just sending the credentials straight away.
By my reading, the spec is not that clear about it. It does say that
A user agent that wishes to authenticate itself with an origin server--usually, but not necessarily, after receiving a 401 (Unauthorized) --
implying, imo, that no prior 401 response is needed. On the other hand, the spec also says
A client SHOULD assume that all paths at or deeper than the depth of the last symbolic element in the path field of the Request-URI also are within the protection space specified by the Basic realm value of the current challenge. A client MAY preemptively send the corresponding Authorization header with requests for resources in that space without receipt of another challenge from the server.
which would imply that at least one 401 response should be received before sending credentials.
Of course the most important part of the spec is this:
The Basic authentication scheme is not a secure method of user authentication, nor does it in any way protect the entity, which is transmitted in cleartext across the physical network used as the carrier.
I don't think there's any ambiguity here...the first quote indicated that a client is absolutely allowed to send auth headers without receiving a 401 response. I read the second quote as giving client implementers a suggestion on when they should automatically send the auth headers, without actually requiring them to do so.
I agree, and the practice of sending an Authorization header without first receiving a challenge is common enough that there's a phrase for it: preemptive authentication.
There are two ways to look at it: cURL is the client, or the application's the client--in this case the guy writing the blog post. In either case, the client's not following the spec. So which way to look at it is correct?
My view is that cURL not a client. It's a lowish-level library and command line tool that can turn input into a format that an HTTP server can understand. Of course what puts a kink in this view is that it does things like follow redirects. A convenience for the user if anything. But in the end, you use cURL to build HTTP clients. It's not a client itself.
Regardless, you're right. It's a problem with the client not following the HTTP spec. However, I don't think many developers really follow the HTTP spec to begin with. For most, HTTP is URLS, headers, and a limited number of methods. I've certainly been guilty of viewing it as such in the past.
So in Platonic heaven, it's the client's fault and by extension the developer's fault. Does that means that API providers should let developers shoot themselves in the foot? Probably not.
Yes, Basic auth is the default in curl, if you provide credentials off the bat. Use --digest to default to digest auth.
If you don't provide credentials, curl will prompt you and do the authentication the server requests (if any). Using --anyauth will also let the server decide.
They're probably not going to change that default and break millions of scripts on the next apt-get upgrade.
Why the hell would you over complicate your API with things like s3 auth or other bs like oauth?
HTTP Basic Auth works pretty well with SSL and should be enough for most cases. Also, it's dead simple for the API to return a token that can be used after authentication in non-SSL endpoints of the API.
The title is a little misleading as the author is talking about HTTP vs HTTPS rather than the HTTP auth specification vs oauth (et al). Essentially his whole blog post could be reduced to this: servers shouldn't open port 80 on auth API sub-domains.
While he does have a point, I do think part of the blame lies with the client side as well. If you're dumb enough to transmit user details over clear text protocols then you really shouldn't be allowed near any sub-systems which require authentication.
I like HTTP APIs that use basic auth over SSL. Why? Because as the blogpost demonstrates nicely, curl is a wonderful tool to test, demo and debug things.
As soon as you introduce e.g. MACs to the mix, you can forget about most unix-y commandline tools.
I think we can all agree that there are better options when it comes to authentication, but I'd rather have something I can work with easily than worry about that tiny amount of possible "plain http pre redirect" mistakes.
What about using http digest auth as a middle ground? It's still curl compatible and a tiny bit more secure.
I agree, things like Oauth are also massively overkill for what should be simple jobs. The other day I was writing a script for my own personal use that needed to get some metadata from Imgur. Their API /requires/ OAuth, when even a json snippet for a public picture would have been just fine. Ended up just scraping the page as it was a one off script and was much faster than signing up, creating an application, implementing a library, etc.
You can use a subset of OAuth - I tend to use the so called "0-leg" version of OAuth, which is pretty much the same as a username/password (consumer key + secret), but with added benefits such as protection against replay attacks.
It's also pretty simple to implement on client side and server side. I usually give our clients a little library to get started with requests.
I've implemented OAuth client-side a few times in a few languages. The signing process itself may not be that difficult to understand, but there are a lot of fiddly moving pieces, the specs are painful to read, and everyone has their own unique take on the spec.
I wouldn't call it simple. It's complex to get right, and outright damaging to non-web clients except in the case where xAuth or similar is available.
While if you are just implementing Key/Token auth for an API, Digest might be `good enough` but does not escape the problems associated with Digest, such as its reliance on MD5 (broken, if your token is complex and longer than 32 chars it renders it less secure) and the requirement to store tokens in plain-text[1].
If you are implementing username/password, then Digest removes the ability to use a secure password system like Bcrypt or PBKDF2, which is a killer.
[1] You are able to store the digested tokens/passwords rather than plain-text, but if you need to protect the token/password in any way, the digest is an insecure way to do so.
I get the feeling that most people commenting here haven't actually implemented an API that is under heavy use and has strict security requirements.
One of the main problems with Basic Auth that the author fails to mention is that if your passing passwords with every request, you're also hashing that password with EVERY request (assuming your doing REST, ie no client state on the server). And if you're properly hashing passwords in your DB, you should be using a slow hash like bcrypt. That means a single request that might take 30ms now takes perhaps 500ms. And many workloads are going to require multiple hits to the API to accomplish simple tasks, so your talking about both a strain on the system, and a horrible experience from the client's perspective.
This isn't a problem specifically with HTTP basic which at the end of the day is just a HTTP request header with an encoded string.
If you're using basic auth and a long random server generated key as Stripe does instead of a user supplied pasword performance is a non issue as you'd SHA2 it in your DB not bcrypt.
This is a very good point, and is the sort of situation where API tokens can come into play and be useful. A client can make a single request to "open a session" and receive a short-lived key that is used as the password credentials for subsequent requests. Store active keys - since they are short-lived, offline attacks are less of a concern and you can probably choose to not slow-hash them. When the token expires and a request fails authentication, the client simply requests a new one and retries. The token lifetime is a much easier adjustment for which to weigh security versus performance/load considerations, compared to against broader password storage security.
This article highlights an important issue. But closing port 80 is sufficient to address this problem. Having considered this article, I think I would still design an API to use HTTPS only, with port 80 closed, with Basic Auth. There's no security problem then, and it's a whole lot easier to manage with existing tools.
(eg, as rb2k_ points out, it makes it easy to use curl to debug things).
Basic Auth should be used only with HTTPS/SSL - who does otherwise? Do not think HTTP Basic is a bad design choice with SSL for securing your service. Any security is brittle if you don't understand what you are upto. That said key based authentication is more apt for APIs.
I was playing about with Fiddler with some mobile apps on my iPhone a while ago and noticed one app was using HTTP basic auth with HTTPS but the application was ignoring SSL errors (I was using a self signed cert on my PC to perform MITM and decrypt HTTPS traffic). This is almost as bad as using HTTP and basic auth because you're basically ignoring the fact that the certificate should not be trusted (at least give the user a choice?). You would be surprised to see how many apps ignore SSL errors and allow you to use any old self signed cert.
We had to (begrudgingly) disable cert validation when we still supported older iOS versions, as it was impossible to find an affordingly-priced CA that issued compatible certificates.
A great alternative if you can't (or don't want to) close port 80 is to return a 401 for non-HTTPS requests to your API, rather than redirecting with a 301 from HTTP->HTTPS.
This has the side effect of making sure that HTTP clients can never silently redirect, and it's impossible for a developer to ever get their code working without explicitly using SSL. If it never works in development, they'll never ship such code to production.
I tend to use an api_key, because it's easier to manage. I'll setup a route to authenticate, where I'll use basic auth to return an api_key, and the remainder of the api either requires an api_key or no authentication at all.
For Rails developers, Ryan Bates does a great job of explaining a few ways to secure your api.
I agree that HTTP Basic over SSL is probably fine for requests from a server, in order to prevent sniffing of the connection.
However, I've recently been thinking about how to use a third-party API directly from client-side Javascript, or even from a resident application. If you're doing that, then you really need some way to use keys with validity that is limited by time or scope. In that case, HMAC offers some real advantages.
If people can accidentally send plain text password to Stripe for a 403 response and don't care about it, I can't see why people can't accidentally send plain text password to the HMAC-SHA protected port 80.
They are the same thing, both will not work.
(Unless you close the port 80, using HMAC-SHA can't solve the issue.)
The problem it seems to me with Stripe and others is that they stuff the API key in as the Basic Auth username. Wouldn't is be safer to use, say, a merchant ID as the username and then the API key as the password (since the password is not transmitted on the errant HTTP)?
This may be decent advice for security purposes but unless I can test your API directly from the commandline using CURL I am unlikely to ever get as far as trying out your service.
In other words, use HTTP BASIC (or similar) if you want new users.
Until you can point me to an alternative authentication scheme that I can expect api consumers to generally have ready-made support for, it's pretty well the only good suggestion in this article.
Offer a test bed for your API, where people can test their clients. That way developers will start to use SSL before they they go in production, and no production passwords should leak that way.
http basic auth is implemented in the server, not in the webapp, that's one fine reason.
redirecting from http to https is ALWAYS an issue, http basic auth or not.
now then again it'd be nice to have a "better auth" than basic auth that is still not part of the web app.. but yeah.
If you need the user's password in plaintext (or something easily derived from it), then your authentication scheme shouldn't send it over the network at all: there are better ways to handle that scenario.
Also keep in mind that a machine storing plaintext passwords has the same security requirements as a Kerberos KDC, and those requirements are very, very high. Don't do this unless you know what you're doing, and even if you do, you should seriously consider a different scheme (or just using Kerberos itself: they've already gotten things right, so you don't have to risk getting things wrong).
You should almost never know a users plaintext password. Outside of a small set of scenarios you're likely doing something very wrong if you need a users plaintext passwords. Passwords should almost always be bcrypt'd.
So if you offer a https only API and anyone makes the mistake of using http instead, what should be the response of your system? A simple error message with 404 code and without special/identifying headers?
Oh but it IS the problem with the provider. Or rather, it will be when some crazy PR storm hits the interwebz with "$YOURCOMPANYNAME leaked passwords!" when someone comes up with some clever way to hack/manipulate traffic with XSS or something.
I was able to fetch private authtokens of a Wii game because port 80 was open on one of nintendo's servers(I assume it's there to test in plain text and just didn't close it later) and I was able to fool the software into using port 80 instead of port 443. That wouldn't have worked if port 80 was closed.
No, the whole problem is a http server (ANY http server, serving ANY content) listening on port 80.
Once the tcp connection is accepted on port 80, "dumb" clients (like curl) can just come barging through the door shouting plaintext auth credentials without knocking first, and no http server can stop them from doing that (because that is how the http protocol works).
The only way to stop them from doing that is rejecting connections on port 80. (Dropping packets looks even more like service outage, which was mentioned.)
The author made this seem outlandish, but really it's a reasonable and easily done way to go about things. Just have a special subdomain for your API (I don't know, api.*.com) and only listen on 443 with it. Done.
That's not enough. The problem is that a client which mistakenly sends a request to Port 80 will send the Authentication request, which, as has already been established, includes (basically) cleartext credentials. Even if whatever you're hosting on Port 80 returns an error, the damage is already done. To prevent that, you have to abort the connection before the client even has a chance to send the request, i.e. closing Port 80.
This means that if you're going to use HTTP Basic authentication, you need to serve your API and your Website from different hosts.
The article covers this: The browser sends the authentication info in the initial request, so by the time the server responds, the damage is already done.
A more sensible resolution to this issue, as a hypotherical security professional at an API company, would be moving more of the burden for security onto the company rather than the API consumer, by e.g. limiting the downside risk of a credentials compromise. (Similar to how banks don't say "The bad guys got your password? Sucks to be you, your balance is now $0", one would be well-advised to eventually have something on the roadmap to e.g. disallow unauthorized or poorly considered code from causing business catastrophe prior to getting a human in the loop.). You'd also want to e.g. make sure your sample code is secure of of the box, and write your first-party libraries to "just work" to the maximum extent possible, such that greenfield development would tend to be secured by default.
That's being pretty generic, but it's my understanding that many API companies actually do put quite of bit of work into internal anti-abuse tooling, for this and related reasons.