-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle too long URIs and too large header fields in HTTP::Request.from_io #8013
Handle too long URIs and too large header fields in HTTP::Request.from_io #8013
Conversation
3701e66
to
ff268b4
Compare
ff268b4
to
a8db1e4
Compare
1MiB is way too large. |
Go does the same, so it might be fine. |
No, we should make the limits configurable and leave the defaults as-is. Maybe doubled, but no larger. It's so easy to DoS a server, and the current values work for any sane usecase. If someone's already designed an insane API which you can't break, you should be able to raise the limits. But the defaults should be left as-is. |
So, if you hit However, it might make sense to have a compromise and choose a smallish value but let the user configure it if they really need it. |
I forgot to mention this in my previous review: |
@asterite Do you mean max header size or max request line size? Or both? |
Probably both. |
Going with the same as NGINX is OK with me. I'd be fine with copying just the 32k without the 8k per-header too. Realistically, most everything running crystal is gonna pass through NGINX on the way. |
In this case Crystal wouldn't need to enforce any limits at all. In fact, it might even be counterproductive because increasing the limits in NGINX wouldn't have any effect unless they're increased in the app server as well. I think that's why golang has such high limits: they expect their HTTP server to be exposed behind a reverse proxy. For this use case, you want high limits in order to accept everything coming from the frontend. From that line of thought, you could say that an app server supposed to be exposed without a reverse proxy needs to take special precautions anyway. One of them would be to decrease the default limits. Given that Crystal and Go are used for writing the same kinds of software, I'd be inclined to follow Go's example here and use very high limits. |
I don't follow that we should support DoSing the server by default because we assume that we're behind NGINX. While we're behind NGINX most of the time right now, I want to make sure that Crystal is robust with good defaults for graduating beyond that! If we cannot agree to new limits, then perhaps we can all agree to make this configurable, then change limits later. |
The question is whether the stdlib can and should provide that. But disussing limits and long-term goals is not the purpose of this PR. So I agree to leave this part of the discussion out. |
I've added these limits as class properties of
These properties are currently the only way to configure this, making them global values. I'm not sure whether we would need more fine-grained control, for example per I've also changed |
702634d
to
f0fc36b
Compare
Thank you! But I was thinking about this as properties of an I know an app will usually have just one http server, but I usually try to avoid global variables. One reason is that it's easier to test (no need to remember unsetting the property after each test). |
I'd definitely find it far cleaner to have them on |
f0fc36b
to
faec19e
Compare
I've moved configuration to |
|
||
# EOF | ||
break unless request | ||
|
||
if request.is_a?(HTTP::Request::BadRequest) | ||
response.respond_with_error("Bad Request", 400) | ||
if request.is_a?(HTTP::Status) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if request.is_a?(HTTP::Status) | |
if status = request.as?(HTTP::Status) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried that, unfortunately it doesn't work because it doesn't restrict the type of request
to HTTP::Request
.
I figure this should probably work, though because HTTP::Status
runs into the branch and returns.
This change allows Request.from_io to directly return a HTTP::Status which might designate a different status code than 400 Bad Request.
f2acbbe
to
6239126
Compare
This change removes
HTTP::Request::BadRequest
which was just a status type to signal an error in the request parser. Instead,Request.from_io
directly returns anHTTP::Status
which can designate a different status code than400 Bad Request
.When a URI is too long (max size is configurable, defaults to 1MB), it returns
414 Request-URI too long
.When the header fields are to large (
HTTP::MAX_HEADER_SIZE
= 1MB), it returns431 Request Header Fields Too Large
.Fixes #7838