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

Remove default port from host when calculating signature #1618

Merged
merged 14 commits into from
Nov 14, 2017

Conversation

s12v
Copy link
Contributor

@s12v s12v commented Oct 27, 2017

Solves #1537

Stumbled upon this issue when working on elasticsearch signatures (elastic/beats#5466). Default elasticsearch port is 9200, I had to include "non-standart" 443 to hostname, and then it calculates wrong signature.

@jasdel
Copy link
Contributor

jasdel commented Nov 1, 2017

Thanks for creating this PR @s12v. I'm looking into the v4 signing spec to verify that this will not go against any of the v4 specification.

@jasdel
Copy link
Contributor

jasdel commented Nov 1, 2017

From the V4 signing specification and test suite i'm not seeing any mention of excluding port from the Host header when computing the signature's canonical string. And per the HTTP RFC the port should be included in the header if its non standard.

I'm curious if the failure you're seeing because the a "standard" port of 443 (TLS) is being included in the hostname.

@jasdel
Copy link
Contributor

jasdel commented Nov 1, 2017

Related to aws/aws-cli#2883

@s12v
Copy link
Contributor Author

s12v commented Nov 1, 2017

@jasdel, makes sense, thanks for looking into it.
Host header includes port, so SDK has to modify header? Or maybe just calculate correct signature. I'm wondering how does it work in the java sdk...

@s12v
Copy link
Contributor Author

s12v commented Nov 1, 2017

I changed the code quite a bit and moved tests to functional_test.go, please have a look

Copy link
Contributor

@jasdel jasdel 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 updating this PR! Stripping off the port conditionally looks good. Just a couple comments about checking if the request value is not nil. And if you ran into situations where this was possible.

@@ -696,6 +698,81 @@ func (ctx *signingCtx) removePresign() {
ctx.Query.Del("X-Amz-SignedHeaders")
}

// Set ctx.Request.Host to host:port for non-standard ports, and to host for explicit standart ports
func (ctx *signingCtx) sanitizeHost() {
if ctx.Request == nil || ctx.Request.URL == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

did you run into situations where the ctx.Request value was nil? I think this value is required for the signer to function. In that case i think it would be better to fail loudly instead of silently bypass the nil value.

Copy link
Contributor Author

@s12v s12v Nov 2, 2017

Choose a reason for hiding this comment

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

@jasdel, I assume it's always set, but I don't know :) Removing the check.

What do you think about ctx.Request.URL? Should be also always set?


// Returns host without port number
func hostname(req *http.Request) string {
if req == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same question as above in sanitizeHost with nil request value.

return aws.URLHostname(req.URL)
}

// Copy of url.stripPort()
Copy link
Contributor

Choose a reason for hiding this comment

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

could you update this to mention directly that this was copied for the Go standard library url package. Would also be also to include the go version too.

return hostport[:colon]
}

// Copy of url.portOnly()
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto as above

@s12v
Copy link
Contributor Author

s12v commented Nov 2, 2017

@jasdel, changed.

I'm thinking about one more thing: current implementation modifies ctx.request.Host when needed. It's possible to achieve the same result in func (ctx *signingCtx) buildCanonicalHeaders(..), without touching the original request. Which is probably better... what do you think?

Please check the next commit to see what I mean.

@s12v s12v changed the title Remove port from host when calculating signature Remove default port from host when calculating signature Nov 2, 2017
@jasdel
Copy link
Contributor

jasdel commented Nov 2, 2017

I was thinking about this as well. I think the original request must be modified because if the HTTP request sent doesn't use the exact Host header value the signature will fail. I don't think the Go HTTP client automatically strip off the port in the Host header. At least httputil.DumpRequest shows it with the port on the host header. I've not looked at this in wireshark yet though.

https://play.golang.org/p/Z7opUaTA-F

If the Go HTTP client doesn't automatically strip off the "standard" port form the Host header I think this needs to be performed by the SDK.

Maybe a better place for this logic would be when the request is created instead of the signer. The signer wouldn't need any special logic if the Host value on the HTTPRequest is already formatted "correctly".

@s12v
Copy link
Contributor Author

s12v commented Nov 2, 2017

I was thinking about this as well. I think the original request must be modified because if the HTTP request sent doesn't use the exact Host header value the signature will fail

I think it will not fail (at least it was working for me with AWS ES). Probably, on the receiving side default port is removed before signing. However, it's not explicitly documented anywhere, and maybe it even works differently for different services... no idea.

But I agree, that on the other hand, it's better to have signature and host header consistent.
If you'd like, I can revert the last commit.

Maybe a better place for this logic would be when the request is created instead of the signer.

Not sure, the signer accepts http.Request.

@jasdel
Copy link
Contributor

jasdel commented Nov 6, 2017

Thanks for the update @s12v I think with ES you're right the port is probably being removed automatically server side before signature verification. We probably cannot rely on this for all services though. I've heard mentions about standard port in host header causing failures for some services.

Thanks a lot for taking the time and putting together this PR by the way!

Maybe a better place for this logic would be when the request is created instead of the signer.

Not sure, the signer accepts http.Request.

Good point. This logic would be required in the signer anyways to handle the case a user uses the signer directly with a http.Request. So we know the logic needs to per performed in the signer for the Sign and Presign maybe case. From a library structural point of view it would also be good if the SDK's internals generated the request.Request's Host header correctly.

@jasdel
Copy link
Contributor

jasdel commented Nov 6, 2017

To Resolve this how about we move the stripDefaultPort et al functions/logic to the request package, and export stripDefaultPort named as SanitizeHostForHeader. The SDK's request.New could use this when building a new SDK request. Setting the Request's HTTPRequest.Host member.

For the use case where a user manually calls v4.Signer.Sign|Presign, the methods should use request.SanitizeHostForHeader to update the http.Request's Host member.

I think this would allow the logic to be performed in both request building, and ensure the Host header is constructed correctly for anonymous requests, (aka unsigned).

@s12v
Copy link
Contributor Author

s12v commented Nov 6, 2017

@jasdel, sounds good, moved to request

@jasdel
Copy link
Contributor

jasdel commented Nov 14, 2017

Thanks for putting this PR together @s12v. the change looks good. I'll go ahead and merge this in. It will be tagged in our next release.

@jasdel jasdel merged commit 3f5879a into aws:master Nov 14, 2017
jasdel added a commit that referenced this pull request Nov 14, 2017
@awstools awstools mentioned this pull request Nov 14, 2017
@s12v
Copy link
Contributor Author

s12v commented Feb 20, 2018

@jasdel, it seems the issue with default ports exists in aws-sdk-go-v2, do you have any plans to move the code there?

@jasdel
Copy link
Contributor

jasdel commented Feb 20, 2018

Thanks for letting us know about this issue in the v2 SDK @s12v. Would you mind creating a issue in the v2 for this. We'll get that issue ported over.

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.

2 participants