Skip to content

check body length #255

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

Merged
merged 8 commits into from
Jun 16, 2020
Merged

Conversation

artemredkin
Copy link
Collaborator

Adds checking for supplied body length.

Motivation:
We need to guard against incorrect body length since we can re-use connection, this is a potential security issue.

Modifications:

  • Adds a check in HTTPTaskHandler that if validated headers contain Content-Length, user sends exactly that amount of body data.
  • Two tests

Result:
Closes #251

@artemredkin artemredkin requested a review from weissi June 15, 2020 11:10
@artemredkin artemredkin added this to the 1.2.0 milestone Jun 15, 2020
Copy link
Contributor

@weissi weissi left a comment

Choose a reason for hiding this comment

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

Thank you! Looks like a great start but I think there's some EL confusion and lacking assertions.

@@ -794,11 +796,19 @@ extension TaskHandler: ChannelDuplexHandler {
assert(head.version == HTTPVersion(major: 1, minor: 1),
"Sending a request in HTTP version \(head.version) which is unsupported by the above `if`")

self.expectedBodyLength = head.headers[canonicalForm: "content-length"].first.flatMap { Int($0) }
Copy link
Contributor

Choose a reason for hiding this comment

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

this needs to error if there's more than one CL header

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

shouldn't validateHeaders do that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I can add a return from validate with body length, as an alternative

Copy link
Contributor

@weissi weissi Jun 15, 2020

Choose a reason for hiding this comment

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

I'm happy if we have a test that proves that we error if the user sends two content-lengths. In this code, I think we should just add an

assert(head.headers[canonicalForm: "content-length"].count <= 1)

to be sure. That makes the code much easier to read because you can immediately dismiss that thought if you see the assertion.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it's even better, we remove all content length headers and insert them ourselves :) except one case, I'll add a test for that (and an assert).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That will happen only if the stream has no length is not specified. In all other cases we actually ignore the user-provided headers, you think we should fail on those?

Copy link
Contributor

Choose a reason for hiding this comment

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

@artemredkin wait, why are we looking for the first CL header then if this code is only run if the user specified no CL headers?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this code is run always, if the content-length was not specified, then expectedLength will be nil and final check will not be executed

Copy link
Contributor

Choose a reason for hiding this comment

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

alright, let's just add the assertion&test (if not present for all the possible cases) in case somebody modifies the sanitisation code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done!

@@ -836,6 +846,7 @@ extension TaskHandler: ChannelDuplexHandler {
}

return promise.futureResult.map {
self.actualBodyLength += part.readableBytes
Copy link
Contributor

Choose a reason for hiding this comment

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

  • assertion about the correct EL missing
  • test that would fail this assertion right now probably missing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

moved the increment to where it is certainly on correct EL

XCTAssertEqual(error as! HTTPClientError, HTTPClientError.bodyLengthMismatch)
}
// Quickly try another request and check that it works.
XCTAssertNoThrow(try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "/get").wait())
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, I know I wrote this test but we should check the correct connectionNumber and requestNumber from

internal struct RequestInfo: Codable {
    var data: String
    var requestNumber: Int
    var connectionNumber: Int
}

just to be sure we get a fresh connection.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

hmm, do we even need this part? we already have a test that checks that, no?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ah, I think I know what you mean!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done!

}
// Quickly try another request and check that it works. If we by accident wrote some extra bytes into the
// stream (and reuse the connection) that could cause problems.
XCTAssertNoThrow(try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "/get").wait())
Copy link
Contributor

Choose a reason for hiding this comment

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

similar here, we should validate request/connection number.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done!

@@ -357,8 +361,8 @@ internal struct HTTPResponseBuilder {
}
}

let globalRequestCounter = NIOAtomic<Int>.makeAtomic(value: 0)
let globalConnectionCounter = NIOAtomic<Int>.makeAtomic(value: 0)
//let globalRequestCounter = NIOAtomic<Int>.makeAtomic(value: 0)
Copy link
Contributor

Choose a reason for hiding this comment

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

comments

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

oops, fixed, thanks!

Copy link
Contributor

@weissi weissi left a comment

Choose a reason for hiding this comment

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

Thank you! LGTM

@artemredkin
Copy link
Collaborator Author

@swift-server-bot test this please

@artemredkin artemredkin force-pushed the check_body_length branch 6 times, most recently from c161ff5 to 610af18 Compare June 16, 2020 07:44
@artemredkin
Copy link
Collaborator Author

@swift-server-bot test this please

2 similar comments
@artemredkin
Copy link
Collaborator Author

@swift-server-bot test this please

@artemredkin
Copy link
Collaborator Author

@swift-server-bot test this please

@artemredkin artemredkin merged commit 606ab0e into swift-server:master Jun 16, 2020
@artemredkin artemredkin deleted the check_body_length branch June 16, 2020 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

we don't check if the user supplies the correct number of bytes when specifying content-length
2 participants