Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Great article! We've updated the OpenAI API to 403 on HTTP requests instead of redirecting.

  $ curl http://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer 123" \
  -d '{}'

  {
    "error": {
      "type": "invalid_request_error",
      "code": "http_unsupported",
      "message": "The OpenAI API is only accessible over HTTPS. Ensure the URL starts with 'https://' and not 'http://'.",
      "param": null
    }
  }


You may want to disable path resolution as well.

http://api.openai.com/v1/chat/completions/../bar responds with error messages about http://api.openai.com/v1/chat/bar which might suggest some path traversal vulnerability that could be exploited.

Generally an API client is not going to need .. to be resolved in a path. It should return 400 - Bad Request (deceptive routing).


Doesn’t returning a 403 on HTTP break HSTS?

https://security.stackexchange.com/questions/122441/should-h...

Doesn’t HSTS require only responding to a user via HTTPS (even for error codes).


HSTS is intended for browsers. For API clients the correct behavior (following curl's lead) is probably to never follow/make any redirects by default.


The HSTS header is only effective when it's received over HTTPS. And if it has taken effect, the client won't try to access HTTP anymore, so it won't even know what response it would have gotten from HTTP.


What about this then? When the request is made over insecure HTTP, revoke the API key used, but then send the usual redirect response for HSTS. Then, when/if the request gets repeated over HTTPS, notice the key is revoked and so respond to that one with 403.


If your goal is to waste people's time, cause them to question their sanity, and guarantee that they're way too pissed off when they finally figure out what happened that instead of teaching others about how important it is too use HTTPS from the start, they talk about how awful your API is and how much they hate your company for a terrible design, then yes this sounds like a good plan.


If it were a generic 403, sure. But if the 403 message said something to the effect of "this API key is no longer valid because it has previously been observed over insecure HTTP", then wouldn't that be fine?


Yes agreed, I think that would improve it, although depending on the situation it may still cause substantial pain. Also apologies, after re-reading my previous comment it seemed unnecessarily harsh toward you, though that wasn't my intention!

On the subject though, an example of how it could cause a lot of pain, many bigger corps don't allow developers to have "real" API keys, and they certainly can't generate new ones themselves, so this might mean one slip-up with curl results in at best a ticket with another team. It also might end up bringing down production in a horrible way, for example if the dev or an ops person is debugging and curls the endpoint from a pod in prod and forgets to explicitly put the https, curl will default to http which would then immediately cause the prod key to be revoked with no second chances. That could even happen on a GET request, which normally GETs are supposed to be safe/side-effect free! If you're operating at a big scale, that could be utterly disastrous, causing a widespread production outage immediately.

If it's a dev that is just testing a key locally that isn't used anywhere else, then it's obviously less of an issue, but taking that into account starts to balloon the complexity around your token revocation code.


HSTS is a note to the browser to insist on TLS when hitting a website. It is sent as a header, with a timescale, regardless of http/https.


Why not just stop listening on port 80, period?


It’s a good option, but you can’t give users a reason for the failure. They might even assume your service is broken.


I stopped listening on port 80 for everything… nobody’s complained yet! Maybe because they can’t find the service though.


I think it's fair to assume j. random user isn't typing "http://api.example.net" into their web browser.

leading www perhaps, leading api no.


You'd be surprised... generally if there's a dedicated hostname for the API, I would expect / to either display or redirect to API docs.

Also, doesn't help when you're reverse proxying /api to $API/api


Posting on this forum means you are probably not J. Random User. I mean specifically anyone who will not grasp the difference between http:// and https://api.example.com.


I've done that myself and have consumed many others who have done it, and I don't think it's better. Much better to get a response that tells you to use https for the API. (for browser also a redirect is a must for UX, though our context here is API)


Because the whole point is a mitm can compromise it, and the mitm can listen on 80 regardless if you turn yours off.


Thank you for sharing! I think this sort of thing is what makes HN great.

Have you rolled this out to prod yet? Did you check how many users this might effect? I can imagine some (probably amateur) apps are going to break when this hits, so some notice might be nice.

I'm not asking those questions critically, mainly wanting to facilitate a full discussion around the pros and cons (I think the pros are are much stronger personally).


This is better as it allow you to immediately notice that there's an issue. However it still facilitates api key exposing on the initial request.


How would the endpoint prevent that?


Not listening on port 80, such that the user gets a connection refused, would result in the client not sending the api key over the wire at all.

I personally think listening, accepting that user mistakes can expose API keys to MITMs, and returning the user-facing error is better than a "connection refused" error, but it is a tradeoff.


If anybody is looking to copy in a public API, please return 400 and don't misuse a standard code.


Why do you think 403 is the wrong error code? Based on the spec it seems entirely appropriate to me:

> HTTP 403 provides a distinct error case from HTTP 401; while HTTP 401 is returned when the client has not authenticated, and implies that a successful response may be returned following valid authentication, HTTP 403 is returned when the client is not permitted access to the resource despite providing authentication such as insufficient permissions of the authenticated account.[a]

> Error 403: "The server understood the request, but is refusing to authorize it." (RFC 7231)


I thought the best response from the article was the 426 Upgrade Required. That way you can throw in an Upgrade: https field in the response. It makes it immediately clear that something weird is going on. 403s are common, my first thought would be a badly-configured API key which lacks the expected permissions.


Because it's not an authorization error.

You do not throw 403 if the client is authorized to access whatever resource it's trying to.

The 426 on the sibling comment is great, though. But if you don't find an error code for your case, you don't go and redefined a well defined one. You use 400 (or 500).


400 is usually for a malformed request. It seems like in this case the request is well formed, it's just not allowed. 403 seems reasonable if the user isn't authorized to make a request to the URL, which they aren't. Some APIs return redirects which also seems pretty reasonable.


But that also implies that some user would be authorized to make a request to the HTTP port (or that the resource does exist, which in this case it doesn’t).

IMO, 400 is more accurate, but really either could be acceptable, so long as the client is notified of the error. But, I wouldn’t automatically redirect the client. That’s what we are trying to avoid.


Good point.

I guess this might depend a little on the implementation. In some cases the http endpoint may exist but may only be accessible to a sidecar container via localhost. For example, if the sidecar terminates https.


404 would also work, since the resource does not exist at the http: address.


True, but 404 has trained us to look hard at the URL part, not the protocol part.

Whereas 403 or 400 are less likely to have so automated built-in handling on the client side.


Well in that case really anything is a 403.


Not sure how I feel about this (extremely arbitrary) distinction. 400 Bad Request maybe implies that no matter how many times you retry the request, it will never succeed. 403 Forbidden maybe implies that some external change in the authentication system could perhaps allow the same request to succeed in the future? So I guess in that lens I can see the logic, but again, seems extremely arbitrary.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: