If your web application specifically parses data based on the Content-Type that it advertises itself to be, then yes, the webapp would hit a parse error. But there are many applications that don't do that.

An attacker might use JavaScript to set a "multipart/form-data" Content-Type (thereby bypassing the otherwise required OPTIONS preflight), but send JSON in the request body. Unless your web application specifically parses the body based on the Content-Type (web servers don't do this for you), then you wouldn't detect that.

Well, if your endpoint expects JSON, then at some point it will have to parse it. Even if it completely ignores the content-type header and simply always passes the request body to the JSON parser, the parser would throw.

(But I was wrong, there are ways to produce request bodies that are valid JSON even if the browser forces you into a different format, as the sibling comment demonstrated)

> ...there are ways to produce request bodies that are valid JSON even if the browser forces you into a different format...

The browser basically never forces you into a particular format. You don't even need to do the trick with the form stuff that the sibling was talking about. Consider the following JavaScript:

    var xhr = new XMLHttpRequest();
    var url = "http://localhost:12345/endpoint";
    xhr.open("POST", url, true);
    xhr.setRequestHeader('Content-Type', 'multipart/form-data');
    xhr.send('{"hello":"world"}');
No trickery required, it just does it.

[Edited to illustrate my point better.]

You can do that, but my understanding is you can't get the browser to attach cookies to your request in this way, while you can with forms. Do you agree?

I haven't actually investigated that (and I'm not able to do so right now), so I couldn't tell you for sure.

If that's the case, then yes, the forms method would be 'better'.

[deleted]

Interesting. Is this still sent as a "safe" request though or does it trigger a preflight request etc?

If it was one of the requests that would trigger a preflight normally, then yes, it would trigger a preflight. But the code as shown doesn't do that because "multipart/form-data" is one of the allowed MIME types that can bypass these preflights.

Obviously JSON is a subset of text/plain, so I don't know what people were expecting? For text/plain to mean "plaintext, excluding any string that could possibly parse as any of the other named formats that have a plaintext representation"?

Are people using JSON parser as proxy for access control? "Payload successfully parsed as JSON, therefore you are allowed to use this endpoint"?

If the JSON backend parses the payload as JSON without verifying the content type, then yes, it's a way through. There's no affirmative logic of "payload looks like json, so let it through" going on, it's just "parse this json without looking at content-type, and assume it was already subject to cross-origin restriction because it's json, right?" (same thing, just different intentions). And as we can see, that assumption fails to hold if you don't actually check the content-type.

My apps speak only JSON, so one of the first things I do is create a middleware that requires any POST/PUT/PATCH request to be application/json and reject everything else with a 415 error. That's so I can turn off the CSRF protection mechanics in the framework completely, but the two concerns are related.

I wasn't aware that plaintext was one of the whitelisted types that are allowed without a preflight request.

I guess the same trick might work with urlencoded forms, but it wouldn't work with multipart/form-data

> Are people using JSON parser as proxy for access control? "Payload successfully parsed as JSON, therefore you are allowed to use this endpoint"?

For better or worse, yes, or at least as one layer. That's one of the rationales behind the "safe" requests AFAIK.

And this wouldn't be the first time, protocols are made intentionally incompatible on the wire, so an attacker can't smuggle one inside the other. That's the entire reason for WebSocket's weird handshake dance and the "xor encoding" it applies to messages from the client.

> I wasn't aware that plaintext was one of the whitelisted types that are allowed without a preflight request.

Until now, I wasn't aware of that either. My response is about the fact you can massage the plaintext part to contain valid JSON somehow being a problem, one that apparently is a security issue in practice.

We're not talking about some clever polyglot quine like those COM executables that are somehow also valid Bash and C code and PDF files or something. text/plain is a superset of everything that can be represented by plain text, which includes approximately all code and data formats, JSON and XML included.

> And this wouldn't be the first time, protocols are made intentionally incompatible on the wire, so an attacker can't smuggle one inside the other.

I need to learn more about it, thanks for pointing it out.

Though at the surface, it reads to me like removing a feature. "Smuggling a protocol inside the other" sounds to me like an important feature, or perhaps more accurately, I find myself being part of the "attacker" population much more often than not. "Tunnel $whatever through HTTPS because corporate/ISP firewalls" is both a meme and success story for plenty a SaaS at this point.

> text/plain is a superset of everything that can be represented by plain text

Not in the context of web forms.

Just checked the spec and "text/plain" just seems to be an alias for "application/x-www-form-urlencoded" [1] - i.e. stuff that looks like

  key=value&anotherkey=anothervalue
on the wire.

Apparently though, keys and values can contain arbitrary characters and arent percent-encoded, so you can do a "quine" where the "key" is

  {"foo": "bar", "ignore": "
and the "value" is

  "}
And then the browser will happily send

  {"foo": "bar", "ignore": "="}
over the wire, which is valid json.

[1] https://html.spec.whatwg.org/multipage/form-control-infrastr...

> I guess the same trick might work with urlencoded forms, but it wouldn't work with multipart/form-data

It does, though. See my reply at https://news.ycombinator.com/item?id=48618539 .