Skip to content
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

Reject DATA Frames for invalid path requests #4405

Merged
merged 6 commits into from Sep 1, 2022

Conversation

ikhoon
Copy link
Contributor

@ikhoon ikhoon commented Sep 1, 2022

Motivation:

If a request with an invalid path such as /?download=../../secret.txt
and a DATA Frame is received, a ClassCastException can be raised.

java.lang.ClassCastException: class com.linecorp.armeria.server.EmptyContentDecodedHttpRequest cannot be cast to class com.linecorp.armeria.server.DecodedHttpRequestWriter (com.linecorp.armeria.server.EmptyContentDecodedHttpRequest and com.linecorp.armeria.server.DecodedHttpRequestWriter are in unnamed module of loader org.springframework.boot.loader.LaunchedURLClassLoader @301eda63)
  at com.linecorp.armeria.server.Http2RequestDecoder.onDataRead(Http2RequestDecoder.java:265)
  at io.netty.handler.codec.http2.Http2FrameListenerDecorator.onDataRead(Http2FrameListenerDecorator.java:36)
  at io.netty.handler.codec.http2.Http2EmptyDataFrameListener.onDataRead(Http2EmptyDataFrameListener.java:49)
  at io.netty.handler.codec.http2.DefaultHttp2ConnectionDecoder$FrameReadListener.onDataRead(DefaultHttp2ConnectionDecoder.java:307)
  at io.netty.handler.codec.http2.Http2InboundFrameLogger$1.onDataRead(Http2InboundFrameLogger.java:48)

Although a response is immediately sent by HttpServerHandler for
the invalid path when the headers are received and try to close the stream,
onDataRead() callback could be triggered by the following DATA frame.
Because the stream could not be closed yet.

public int onDataRead(
ChannelHandlerContext ctx, int streamId, ByteBuf data,
int padding, boolean endOfStream) throws Http2Exception {

Modifications:

  • Rejects the following DATA frames and HEADERS frames of invalid path
    requests with PROTOCOL_ERROR.

Result:

You no longer see ClassCastException when an invalid path request is
received.

Motivation:

If a request with an invalid path such as `/?download=../../secret.txt`
and a DATA Frame is received, a `ClassCastException` can be raised.
```
java.lang.ClassCastException: class com.linecorp.armeria.server.EmptyContentDecodedHttpRequest cannot be cast to class com.linecorp.armeria.server.DecodedHttpRequestWriter (com.linecorp.armeria.server.EmptyContentDecodedHttpRequest and com.linecorp.armeria.server.DecodedHttpRequestWriter are in unnamed module of loader org.springframework.boot.loader.LaunchedURLClassLoader @301eda63)
  at com.linecorp.armeria.server.Http2RequestDecoder.onDataRead(Http2RequestDecoder.java:265)
  at io.netty.handler.codec.http2.Http2FrameListenerDecorator.onDataRead(Http2FrameListenerDecorator.java:36)
  at io.netty.handler.codec.http2.Http2EmptyDataFrameListener.onDataRead(Http2EmptyDataFrameListener.java:49)
  at io.netty.handler.codec.http2.DefaultHttp2ConnectionDecoder$FrameReadListener.onDataRead(DefaultHttp2ConnectionDecoder.java:307)
  at io.netty.handler.codec.http2.Http2InboundFrameLogger$1.onDataRead(Http2InboundFrameLogger.java:48)
```
Although a response is immediately sent by `HttpServerHandler` for
the invalid path when the headers are received and try to close the stream,
`onDataRead()` callback could be triggered by the following DATA frame.
Because the stream could not be closed yet.
https://github.com/line/armeria/blob/master/core/src/main/java/com/linecorp/armeria/server/Http2RequestDecoder.java#L242-L244

Modification:

- Rejects the following DATA frames and HEADERS frames of invalid path
  requests with `PROTOCOL_ERROR`.

Result:

You no longer see `ClassCastException` when an invalid path request is
recevied.
@ikhoon ikhoon added the defect label Sep 1, 2022
@ikhoon ikhoon added this to the 1.19.0 milestone Sep 1, 2022
Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some minor comments/questions 🙇

@@ -182,6 +182,10 @@ public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers
ctx.fireChannelRead(req);
}
} else {
if (!(req instanceof DecodedHttpRequestWriter)) {
throw connectionError(PROTOCOL_ERROR,
Copy link
Contributor

@jrhee17 jrhee17 Sep 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question) Is it correct behavior to close the connection rather than return a 4xx response?

If a malicious user decides to continuously send these type of requests, will other requests on the same connection be penalized?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.
4xx response might be returned already by HttpServerHandler. So we don't need to return a response here.
Is it better to silently ignore the incoming DATA frames?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a malicious user decides to continuously send these type of requests, will other requests on the same connection be penalized?

For the protocol violations or unexpected errors, a connection is reset with writeErrorResponse().
For example, if a malicious user sends a wrong content-length, the connection which the request was sent will be closed as well.
However, the HttpServerHandler who handled the request did not close the connection. So it makes more sense to keep the connection.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, if a malicious user sends a wrong content-length, the connection which the request was sent will be closed as well.

Maybe I misunderstood the current codebase, but wouldn't only the stream be closed in this case?

writeErrorResponse(streamId, headers, HttpStatus.BAD_REQUEST,

Since the connection will be closed only if streamId == 0

Screen Shot 2022-09-01 at 6 44 46 PM

Anyways, sorry about the late check. I agree with the approach 👍

@ikhoon
Copy link
Contributor Author

ikhoon commented Sep 1, 2022

Thanks to @tawAsh1 who reports the bug. 🙇‍♂️

Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick fix + name change! 👍 👍 👍

@codecov
Copy link

codecov bot commented Sep 1, 2022

Codecov Report

Merging #4405 (f3471ac) into master (1be52b2) will decrease coverage by 0.01%.
The diff coverage is 38.46%.

@@             Coverage Diff              @@
##             master    #4405      +/-   ##
============================================
- Coverage     73.80%   73.79%   -0.02%     
+ Complexity    17713    17709       -4     
============================================
  Files          1509     1509              
  Lines         66198    66205       +7     
  Branches       8313     8315       +2     
============================================
- Hits          48860    48853       -7     
- Misses        13330    13345      +15     
+ Partials       4008     4007       -1     
Impacted Files Coverage Δ
.../armeria/server/AggregatingDecodedHttpRequest.java 87.50% <ø> (ø)
...om/linecorp/armeria/server/DecodedHttpRequest.java 100.00% <ø> (ø)
...armeria/server/EmptyContentDecodedHttpRequest.java 82.69% <ø> (ø)
...rp/armeria/server/StreamingDecodedHttpRequest.java 83.87% <ø> (ø)
...m/linecorp/armeria/server/Http2RequestDecoder.java 68.04% <27.27%> (-2.95%) ⬇️
...m/linecorp/armeria/server/Http1RequestDecoder.java 75.52% <100.00%> (ø)
...easy/ResteasyAsynchronousExecutionContextImpl.java 53.48% <0.00%> (-2.33%) ⬇️
...ecorp/armeria/server/grpc/StreamingServerCall.java 82.02% <0.00%> (-2.25%) ⬇️
...a/internal/common/stream/ByteBufsDecoderInput.java 85.61% <0.00%> (-2.16%) ⬇️
.../linecorp/armeria/client/Http2ResponseDecoder.java 68.53% <0.00%> (-2.10%) ⬇️
... and 15 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this! 😄

@minwoox minwoox merged commit 85a2f93 into line:master Sep 1, 2022
heowc pushed a commit to heowc/armeria that referenced this pull request Sep 24, 2022
Motivation:

If a request with an invalid path such as `/?download=../../secret.txt`
and a DATA Frame is received, a `ClassCastException` can be raised.
```
java.lang.ClassCastException: class com.linecorp.armeria.server.EmptyContentDecodedHttpRequest cannot be cast to class com.linecorp.armeria.server.DecodedHttpRequestWriter (com.linecorp.armeria.server.EmptyContentDecodedHttpRequest and com.linecorp.armeria.server.DecodedHttpRequestWriter are in unnamed module of loader org.springframework.boot.loader.LaunchedURLClassLoader @301eda63)
  at com.linecorp.armeria.server.Http2RequestDecoder.onDataRead(Http2RequestDecoder.java:265)
  at io.netty.handler.codec.http2.Http2FrameListenerDecorator.onDataRead(Http2FrameListenerDecorator.java:36)
  at io.netty.handler.codec.http2.Http2EmptyDataFrameListener.onDataRead(Http2EmptyDataFrameListener.java:49)
  at io.netty.handler.codec.http2.DefaultHttp2ConnectionDecoder$FrameReadListener.onDataRead(DefaultHttp2ConnectionDecoder.java:307)
  at io.netty.handler.codec.http2.Http2InboundFrameLogger$1.onDataRead(Http2InboundFrameLogger.java:48)
```
Although a response is immediately sent by `HttpServerHandler` for
the invalid path when the headers are received and try to close the stream,
`onDataRead()` callback could be triggered by the following DATA frame.
Because the stream could not be closed yet.
https://github.com/line/armeria/blob/b9dc1ad1c6f4cfee8aba8e50a61d203c37eb94cc/core/src/main/java/com/linecorp/armeria/server/Http2RequestDecoder.java?rgh-link-date=2022-09-01T06%3A34%3A32Z#L242-L244

Modifications:

- Rejects the following DATA frames and HEADERS frames of invalid path
  requests with `PROTOCOL_ERROR`.

Result:

You no longer see `ClassCastException` when an invalid path request is
received.
@ikhoon ikhoon deleted the invalid-request-with-data branch December 5, 2022 09:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants