jakartaee / jakartaee/rest

@Consumes incorrectly handles requests with no content type header / potential security risk

Open
#970 21 comments 0 reactions 1 assignee Claimed by @spericas View on GitHub
spec
Dominant language
Java
Stars
400
Forks
143
PR merge metrics
No merged PRs in 30d

Description

Given the following definition:

@consumes(MediaType.APPLICATION_JSON)
@path("myresource")
@produces(MediaType.APPLICATION_JSON)
public class MyResource {

@Consumes(MediaType.APPLICATION_JSON)
@POST
@Produces(MediaType.APPLICATION_JSON)
public String login() {
return "\n--Login!\n";
}
}

A request to "/myresource" with no 'content-type' header is accepted, login() is executed and a 200 OK response is returned. This behavior was observed with two different jaxrs-api implementations, so I assume this is a jaxrs-api issue (I also sent this issue to Jersey and was told to come here).

$ curl -v -X POST localhost:8080/myresource

Connected to localhost (127.0.0.1) port 8080 (#0)
POST /myresource HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.0
Accept: /

< HTTP/1.1 200 OK
< Content-Type: application/json
< Content-Length: 10
<

--Login!

IMHO, the correct behavior would be to return "415 Unsupported Media Type" because the method and class are clearly annotated to handle only 'application/json'. A request with a missing 'content-type' header should not be interpreted as a valid/acceptable media type, it should be handled as an invalid media type because the lack of a content type header should be equivalent to null and therefore it is not included in the list of acceptable media types indicated by the developer (only 'application/json' in this case).

The spec mentions:

"3.5 [..]
An implementation MUST NOT invoke a method whose effective value of @consumes does not match the request Content-Type header.""

A missing content-type header (I'm referring to an HTTP Request that does not have a 'Content-Type' http header) cannot match any media type specified by the dev. However, currently, a missing content-type header is effectively matched to something like \*/\*, which is not a valid content-type and it's not what the dev specified.

IMHO Even if some section of the Spec says that an HTTP Request without no content-type header is assumed to be \*/\*, this is not correct IMHO, MIME sniffing is a security concern and secure applications are always encouraged to disable MIME sniffing (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) (side note: Is 'Content-type: \*/\*' valid at all? 'application/\*' etc yes, but '\*/\*' ? have you ever seen an app using it?)

Also, secure web apps/rest apis implement specific controls to reject API calls that do not have a 'content-type: application/json' header and the way things work now force the developer to create (for example) a filter to implement this behavior, which should be handled by the @consumes() annotation.

Taken from https://tools.ietf.org/html/rfc7231#section-3.1.1.5 (Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content):

"3.1.1.5. Content-Type"

[...]
"A sender that generates a message containing a payload body SHOULD
generate a Content-Type header field in that message unless the
intended media type of the enclosed representation is unknown to the
sender. If a Content-Type header field is not present, the recipient
MAY either assume a media type of "application/octet-stream"
([RFC2046], Section 4.5.1) or examine the data to determine its type."
[...]

So, if no 'content-type' is present in the http request, the app MAY assume "application/octet-stream", not "*/*".
"application/octet-stream" != "application/json" which means the method annotated with @consumes("application/json") must not be called.

The RFC also says the other option is to "examine the data to determine its type.", but Jersey, Jaxrs-api or whoever is doing this, is not examining the data, and as I mentioned before, examining the data is dangerous and should not be done, the RFC mentions this too:

[...]
"In practice, resource owners do not always properly configure their
origin server to provide the correct Content-Type for a given
representation, with the result that some clients will examine a
payload's content and override the specified type. Clients that do
so risk drawing incorrect conclusions, which might expose additional
security risks (e.g., "privilege escalation"). Furthermore, it is
impossible to determine the sender's intent by examining the data
format: many data formats match multiple media types that differ only
in processing semantics. Implementers are encouraged to provide a
means of disabling such "content sniffing" when it is used."

This paragraph talks about clients because that's the more common attack scenario, but it also applies to servers.

Thank you!

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.