Bug: invalid HTTP responses on invalid data

Nginx seems to offer some strange behaviour when it receives invalid
tokens in the HTTP request. If an unexpected character is sent then
the stream will immediately terminate, returning an HTML page for the
400 error. More importantly, this response does not contain a valid
HTTP 400 code.

For example if I send a request with a lower case “get” method, I’d
expect the webserver to return an HTTP 400 message, but with nginx we
find:

D:\tools\netcat>echo “a” | nc 127.0.0.1 80

400 Bad Request

400 Bad Request


nginx/0.7.65

Similarly with an OPTIONS * HTTP/1.1 request (which is a valid HTTP
request - as it should return options valid to the server, not to a
URI):
D:\tools\netcat>echo “OPTIONS * HTTP/1.1” | nc 127.0.0.1 80

400 Bad Request

400 Bad Request


nginx/0.7.65

I can understand why the stream is immediately terminated, that it
minimises risk of out of band data. But the response should include a
valid HTTP code and be conformant to the RFC, just like a request for
TEST / HTTP/1.1 does:
HTTP/1.1 400 Bad Request
Server: nginx/0.7.65
Date: Wed, 17 Mar 2010 11:03:27 GMT
Content-Type: text/html
Content-Length: 173
Connection: close

400 Bad Request

400 Bad Request


nginx/0.7.65

Hello!

On Wed, Mar 17, 2010 at 11:03:59AM +0000, david lodge wrote:

D:\tools\netcat>echo “a” | nc 127.0.0.1 80

400 Bad Request

400 Bad Request


nginx/0.7.65

This is perfectly valid HTTP/0.9 response to invalid HTTP/0.9
request. No problem here.

This seems to be bug indeed. nginx doesn’t support OPTIONS, and
doesn’t correctly recognize this as HTTP/1.1 request as it
contains ‘*’ (which is only allowed in OPTIONS).

The same will happen for CONNECT method with authority specified.

Probably these two should be recognized and return 501.

Maxim D.

At Wed, 17 Mar 2010 15:10:03 +0300,
Maxim D. [email protected] wrote:

This is perfectly valid HTTP/0.9 response to invalid HTTP/0.9
request. No problem here.

Why not take requests on default 1.0+?


wbr, Kirill

Nginx seems to offer some strange behaviour when it receives invalid
tokens in the HTTP request. If an unexpected character is sent then
the stream will immediately terminate, returning an HTML page for the
400 error. More importantly, this response does not contain a valid
HTTP 400 code.
D:\tools\netcat>echo “a” | nc 127.0.0.1 80

This is perfectly valid HTTP/0.9 response to invalid HTTP/0.9
request. No problem here.

That’d make sense, though looking at RFC1945 it does seem that it
should only return a HTTP/0.9 response if a simple-request is sent:

Simple-Request = “GET” SP Request-URI CRLF

Which is sort of messed up as it terminates the stream as soon as it
reads an out-of-bands character, so even if I send “get / HTTP/1.1”
I’ll still get the HTTP/0.9 message (as the connection will terminate
of the “g”).

(Of course it is feasible, though unlikely, that an HTTP method could
be called “get” which wouldn’t be the same as “GET”.)

Similarly with an OPTIONS * HTTP/1.1 request (which is a valid HTTP
request - as it should return options valid to the server, not to a
URI):
D:\tools\netcat>echo “OPTIONS * HTTP/1.1” | nc 127.0.0.1 80
This seems to be bug indeed. nginx doesn’t support OPTIONS, and
doesn’t correctly recognize this as HTTP/1.1 request as it
contains ‘*’ (which is only allowed in OPTIONS).

The problem being that as soon as nginx sees an "" in the URI
location it will terminate the stream, e.g. if you give a URI of
http://127.0.0.1/fred?jim=

The RFCs are contradictory on this - 1945 (HTTP/1.0 and 0/9) says that

  • is valid in a URI; 2616 (HTTP/1.1) says that * is a special case
    that refers to the server, not to a specific resource.

Probably these two should be recognized and return 501.

Would 405 (method not allowed) be better for an unknown method than
501 (not implemented)?

This is not a serious issue and I only found it because I’m testing
edge cases on web servers. Most normal use wouldn’t even see this
behaviour (though it is possible for a badly implemented agent to send
a * as a cgi parameter, in which case it could become prominent).

dave

Hello!

On Wed, Mar 17, 2010 at 03:13:44PM +0300, Kirill A. Korinskiy wrote:

At Wed, 17 Mar 2010 15:10:03 +0300,
Maxim D. [email protected] wrote:

This is perfectly valid HTTP/0.9 response to invalid HTTP/0.9
request. No problem here.

Why not take requests on default 1.0+?

RFC 1945 explicitly says:

The version of an HTTP message is indicated by an HTTP-Version field
in the first line of the message. If the protocol version is not
specified, the recipient must assume that the message is in the
simple HTTP/0.9 format.

This basically means that HTTP/1.0 support requires this
behaviour.

Maxim D.