Why does your API still use HTTP Basic Auth?

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

  • As an API and security professional I've found to be secure and performant one should do the following with APIs:

    - Dont listen on HTTP, force HTTPS (Tune your ciphers! see https://www.ssllabs.com/ssltest/index.html)

    - 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 work at Stripe.)

    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.

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

    Summary of the article: http://tldr.io/tldrs/516fc7e340d4a84c4100020e/why-the-hell-d...

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

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

  • 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?

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

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

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

  • Agree that the practice of just redirecting http requests to https is a bad idea.

    Hope this can help raise awareness why it's bad.

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

  • undefined

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

    http://railscasts.com/episodes/352-securing-an-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.

  • Closing port 80, great suggestion.

    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.

  • I am a firm believer that all APIs should be available over Basic Auth, even if it's only a small subset to access your own data.

    Slinging OAuth shouldn't be necessary to hack your own fun scripts and get your own data.

    foauth.org

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

  • Obviously it is user agent failure. Why would you want transmit private information clear text?

    Edit: File bug to browser makers. Personally use http proxy to remove auth and cookies from insecure traffic.

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

  • Because I need the user's password in plaintext for example.

  • 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?

  • Too much focus on closing port 80. They could just host something else on port 80.