Hacker News new | ask | show | jobs
by wyuenho 2899 days ago
> Why create your own custom error protocol when you can use HTTP error codes?

Because HTTP error codes are fixed and have well-defined meanings for HTTP that may not map well to your application. Repurposing HTTP error codes conflates your application-specific errors unless it maps to an exact subset of one of the predefined errors.

> Why create your own authentication mechanism when you can use HTTP authentication over TLS (or client certificates, or etc.)?

Do you have a client cert? I've never encountered any server that supports client certs in my entire 15-year career. Also, TLS is not a part of HTTP and HTTP only supported Basic Auth until fairly recently.

4 comments

Repurposing HTTP error codes conflates your application-specific errors unless it maps to an exact subset of one of the predefined errors.

...and beware even the errors that match exactly! I've seen multiple systems in the wild that treat 404 as an application-specific error for "content not found". It sounds perfectly reasonable at first, but it puts you one misconfigured reverse proxy away from inadvertently broadcasting to the world that all your content is gone.

This can have data-corrupting effects. If an external system deletes resources via a reliable message queue and treats 404 as a success condition, they run the risk of dropping these messages and creating an inconsistent state. I've seen it happen.

Keep application-specific error messages distinguishable from the 'plumbing'.

That seems like a bug with the external system. 4xx means the request needs to be corrected to achieve success. 2xx means success, even if success means "deleted".

If a system treated 5xx responses as successful, you'd consider that a problem with the system's response handling, not a problem with your own communication of state. So why consider abuse of 4xx to be a problem with the communication protocol?

The external system didn't define the API; the service did. If you poke around on stackoverflow, you'll find quite a number of answers recommending this pattern. It's a bad idea, but it's not uncommon.
Recommending what, that a `DELETE /foo/bar` should return a 404 on success? That's flat-out wrong. It should return a 204 No Content. A subsequent `GET /foo/bar` should return a 404, but presumably the message queue isn't doing a subsequent GET but is inspecting the result of the DELETE.
Ok. But again, a bad design choice doesn’t mean using HTTP error codes is a bad idea. It means that like with anything else you need to know your tools.
If it's put in a message queue, use a 202 Accepted somewhere, if it's added with HTTP requests ? 201 Deleted only if deleted in real-time. Then check later for proper deletion on or push status of the job to the consumer. 404 as a success condition does not seem to be reliable to me
So.much.experience and wisdom in this answer. I learned something new today. Thank you.
> If an external system deletes resources via a reliable message queue and treats 404 as a success condition, they run the risk of dropping these messages and creating an inconsistent state. I've seen it happen.

This would reflect an incorrect design on the part of the external system.

For example:

  PUT /queue/_hyn3
  => {"name": "_hyn3"}

  GET /queue
  <= {"name": "_hyn3"}

  ... processing ...

  GET /queue/_hyn3
  404
404 is not a success condition; it's also not necessarily a permanent error.

Queues might be "reliable", but the consumers should never be assumed to be, and so it's good to have consumers provide a "complete" state update somewhere, and ideally, if the queue is able, for the queue to restore the incomplete item if the consumer fails to complete it in a specific (lengthy) period of time, because the consumer itself might have ceased to exist or failed for some other reason.

If you need to ensure that each message is consumed once and only once, you need to ensure that your queue also has something like a "completed state":

  PUT /queue/_hyn3
  => {"name": "_hyn3"}

  GET /queue
  <= {"name": "_hyn3"}

  ... processing ...

  PUT /status/_hyn3
  {"name": "_hyn3", "status": "completed"}

  GET /queue/_hyn3
  404

  GET /status/_hyn3
  {"status": "completed"}
Obviously, there's an infinite number of ways to handle the semantics here, and certainly REST is not constrained in any way to just JSON, and perhaps you would prefer to put completed in the URL, but the point is that the architecture needs to support such a use case. TL;DR: don't rely on 404 as a permanent error, but it's absolutely appropriate for a document/content/file not found, even if the content will be found later or was found earlier.
> Do you have a client cert? I've never encountered any server that supports client certs in my entire 15-year career. Also, TLS is not a part of HTTP and HTTP only supported Basic Auth until fairly recently.

Client certificates are very common for API authentication (well-known examples: Kubernetes, Puppet, Saltstack, service meshes like Envoy and Consul...)

Surely all these APIs do not demand client certs right? In the case of Kubernetes, a typical server cert is self-signed, so half of TLS's security measures are defeated, to mitigate that you have to white-list clients by checking their certs, but surely this is more trouble than getting a real server cert and sending your credentials in HTTP?
Server and client certificates belong to unrelated certificate hierarchies. You can have your server cert signed by $whateverCA and still run your own mini-CA to issue client certs and validate client certs against.

TLS client certificates are strictly better than passwords because they don't provide an impersonator with a wildcard (and if you are running over the internet, especially with mobile devices, then you can get into that situation w/o being subject of a targeted attack). There are fairly few ways to achieve that other than client certs (SRP and SAE come to mind, both of which have virtually no deployment).

This makes a lot more sense than the answers below thank you.
Mutual strict TLS is quite common in medium and high security environments.
the master certificate is self signed yes.

this self signed certificate now has to sign the client certificate(s). Otherwise, the clients aren't allowed to address the master.

At least thats how i've come across it. Its actually pretty common in the infrastructure world. Your OPS team can probably tell you which services utilize it in your software stack, though you as a developer probably never had to worry about it.

Though a lot of services only use a singular client certificate across all nodes and just revoke the hole chain for rotations

Sounds like these OPS teams don't know what they are doing, but I'm not sure what you are saying is correct. Conceivably, in an ideal situation, the org has an internal intermediate CA created using a cert signed by a real root CA, and that intemediate CA's cert is in every client machine the org provisions to the employees, so the employees' machine can verify the server they are connecting to is authentic. For mutual authentication, the client certs lets the servers authenticate the other way around. Using a straight up self-signed server cert is a one way mechanism from server to client, it's easily spoofed, this makes no sense to me.
its nonetheless the way its being done. a few examples:

* remote docker daemon execution

* setting up a kubernetes cluster with kubeadm and creating certificates for remote kubectl execution (this one might be related to the previous one. I'm not sure as i don't know anything about the kubernetes internals.)

* rabbitmq cluster iirc. At least thats how you're supposed to set it up with the sensu monitoring framework

* previously mentioned puppet does its as well, though the process is mostly invisible to the user

i'm not sure how its supposedly possible to spoof though?

the master needs to sign a certificate for clients. IF the master is compromised, everything is compromised. Thats true no matter which authorization protocol you're using

I don't know how in your experience how these orchestration servers are setup, but in my experience, these clusters are all on the Cloud, which means accessing them requires going out to the public internet. In the off chance someone is actively MITMing the client's connection to the server, which isn't exactly hard, all that the attacker has to do is to present any cert signed with the same subject and issuer and whatnot except the public key. How would the client know if all the restriction is to accept a self-signed cert? There's got to be some sort of pinning mechanism like OCSP must-staple or verification step the client has to do to authenticate the server or the entire org's kubernetes client will be DoS'ed. I don't even have to compromise anything. You are saying self-signed server cert is how it is typically done, I'm questioning whether that's how it's supposed to be done.

The kubernete's document says:

  For an identity provider to work with Kubernetes it must:

  1. Support OpenID connect discovery; not all do.
  2. Run in TLS with non-obsolete ciphers
  3. Have a CA signed certificate (even if the CA is not a commercial CA or is self signed)

I interpret it as saying you should perferrably use a real CA or a well-behaved internal intermediate CA. Anybody remotely familiar with how TLS works will probably tell you the same. If what you are saying is correct, it's a serious problem in our industry.
In properly-set-up Kubernetes, no aspect of TLS is defeated. Instead, trusted CA certs (usually self-generated) are made freely available to everyone and then those are used to validate everything. If anyone is using self-signed certs and disabling TLS validation, then their implementation is probably insecure.
s/probably/provably/, tbh
I was going to say "definitely" but then I had the idea that they could be using some kind of crazy encapsulating proxy on localhost and then still using secure transport to go across the real network... but yeah no... no one should be running k8s on real servers without (fully-validated) TLS.
Puppet uses client certs, clients request them on an initial run and an admin can approve them in an included CA. There is an API and a CLI interface.

I'm always surprised by the resistance to client certs and all the gleeful usage of pre shared keys I find. OWASP top ten bad ideas for as long as I've been writing software.

Regarding detailing errors, we have this, FWIW: https://datatracker.ietf.org/doc/rfc7807/
There's still free ranges of error codes you can use, eg. 460+ https://httpstatuses.com/