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
71 changes: 71 additions & 0 deletions aws/request/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
err = awserr.New("InvalidEndpointURL", "invalid endpoint uri", err)
}

SanitizeHostForHeader(httpReq)

r := &Request{
Config: cfg,
ClientInfo: clientInfo,
Expand Down Expand Up @@ -606,3 +608,72 @@ func shouldRetryCancel(r *Request) bool {
errStr != "net/http: request canceled while waiting for connection")

}

// SanitizeHostForHeader removes default port from host and updates request.Host
func SanitizeHostForHeader(r *http.Request) {
host := getHost(r)
port := portOnly(host)
if port != "" && isDefaultPort(r.URL.Scheme, port) {
r.Host = stripPort(host)
}
}

// Returns host from request
func getHost(r *http.Request) string {
if r.Host != "" {
return r.Host
}

return r.URL.Host
}

// Hostname returns u.Host, without any port number.
//
// If Host is an IPv6 literal with a port number, Hostname returns the
// IPv6 literal without the square brackets. IPv6 literals may include
// a zone identifier.
//
// Copied from the Go 1.8 standard library (net/url)
func stripPort(hostport string) string {
colon := strings.IndexByte(hostport, ':')
if colon == -1 {
return hostport
}
if i := strings.IndexByte(hostport, ']'); i != -1 {
return strings.TrimPrefix(hostport[:i], "[")
}
return hostport[:colon]
}

// Port returns the port part of u.Host, without the leading colon.
// If u.Host doesn't contain a port, Port returns an empty string.
//
// Copied from the Go 1.8 standard library (net/url)
func portOnly(hostport string) string {
colon := strings.IndexByte(hostport, ':')
if colon == -1 {
return ""
}
if i := strings.Index(hostport, "]:"); i != -1 {
return hostport[i+len("]:"):]
}
if strings.Contains(hostport, "]") {
return ""
}
return hostport[colon+len(":"):]
}

// Returns true if the specified URI is using the standard port
// (i.e. port 80 for HTTP URIs or 443 for HTTPS URIs)
func isDefaultPort(scheme, port string) bool {
if port == "" {
return true
}

lowerCaseScheme := strings.ToLower(scheme)
if (lowerCaseScheme == "http" && port == "80") || (lowerCaseScheme == "https" && port == "443") {
return true
}

return false
}
42 changes: 42 additions & 0 deletions aws/request/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/aws/aws-sdk-go/awstesting/unit"
"github.com/aws/aws-sdk-go/private/protocol/jsonrpc"
"github.com/aws/aws-sdk-go/private/protocol/rest"
"github.com/aws/aws-sdk-go/aws/defaults"
)

type testData struct {
Expand Down Expand Up @@ -960,3 +961,44 @@ func TestRequest_Presign(t *testing.T) {
}
}
}

func TestNew_EndpointWithDefaultPort(t *testing.T) {
endpoint := "https://estest.us-east-1.es.amazonaws.com:443"
expectedRequestHost := "estest.us-east-1.es.amazonaws.com"

r := request.New(
aws.Config{},
metadata.ClientInfo{Endpoint: endpoint},
defaults.Handlers(),
client.DefaultRetryer{},
&request.Operation{},
nil,
nil,
)

if h := r.HTTPRequest.Host; h != expectedRequestHost {
t.Errorf("expect %v host, got %q", expectedRequestHost, h)
}
}

func TestSanitizeHostForHeader(t *testing.T) {
cases := []struct {
url string
expectedRequestHost string
}{
{"https://estest.us-east-1.es.amazonaws.com:443", "estest.us-east-1.es.amazonaws.com"},
{"https://estest.us-east-1.es.amazonaws.com", "estest.us-east-1.es.amazonaws.com"},
{"https://localhost:9200", "localhost:9200"},
{"http://localhost:80", "localhost"},
{"http://localhost:8080", "localhost:8080"},
}

for _, c := range cases {
r, _ := http.NewRequest("GET", c.url, nil)
request.SanitizeHostForHeader(r)

if h := r.Host; h != c.expectedRequestHost {
t.Errorf("expect %v host, got %q", c.expectedRequestHost, h)
}
}
}
88 changes: 88 additions & 0 deletions aws/signer/v4/functional_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,91 @@ func TestStandaloneSign_CustomURIEscape(t *testing.T) {
t.Errorf("expect %v, got %v", e, a)
}
}

func TestStandaloneSign_WithPort(t *testing.T) {

cases := []struct {
description string
url string
expectedSig string
}{
{
"default HTTPS port",
"https://estest.us-east-1.es.amazonaws.com:443/_search",
"AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/es/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=e573fc9aa3a156b720976419319be98fb2824a3abc2ddd895ecb1d1611c6a82d",
},
{
"default HTTP port",
"http://example.com:80/_search",
"AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/es/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=54ebe60c4ae03a40948b849e13c333523235f38002e2807059c64a9a8c7cb951",
},
{
"non-standard HTTP port",
"http://example.com:9200/_search",
"AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/es/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=cd9d926a460f8d3b58b57beadbd87666dc667e014c0afaa4cea37b2867f51b4f",
},
{
"non-standard HTTPS port",
"https://example.com:9200/_search",
"AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/es/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=cd9d926a460f8d3b58b57beadbd87666dc667e014c0afaa4cea37b2867f51b4f",
},
}

for _, c := range cases {
signer := v4.NewSigner(unit.Session.Config.Credentials)
req, _ := http.NewRequest("GET", c.url, nil)
_, err := signer.Sign(req, nil, "es", "us-east-1", time.Unix(0, 0))
if err != nil {
t.Fatalf("expect no error, got %v", err)
}

actual := req.Header.Get("Authorization")
if e, a := c.expectedSig, actual; e != a {
t.Errorf("%s, expect %v, got %v", c.description, e, a)
}
}
}

func TestStandalonePresign_WithPort(t *testing.T) {

cases := []struct {
description string
url string
expectedSig string
}{
{
"default HTTPS port",
"https://estest.us-east-1.es.amazonaws.com:443/_search",
"0abcf61a351063441296febf4b485734d780634fba8cf1e7d9769315c35255d6",
},
{
"default HTTP port",
"http://example.com:80/_search",
"fce9976dd6c849c21adfa6d3f3e9eefc651d0e4a2ccd740d43efddcccfdc8179",
},
{
"non-standard HTTP port",
"http://example.com:9200/_search",
"f33c25a81c735e42bef35ed5e9f720c43940562e3e616ff0777bf6dde75249b0",
},
{
"non-standard HTTPS port",
"https://example.com:9200/_search",
"f33c25a81c735e42bef35ed5e9f720c43940562e3e616ff0777bf6dde75249b0",
},
}

for _, c := range cases {
signer := v4.NewSigner(unit.Session.Config.Credentials)
req, _ := http.NewRequest("GET", c.url, nil)
_, err := signer.Presign(req, nil, "es", "us-east-1", 5 * time.Minute, time.Unix(0, 0))
if err != nil {
t.Fatalf("expect no error, got %v", err)
}

actual := req.URL.Query().Get("X-Amz-Signature")
if e, a := c.expectedSig, actual; e != a {
t.Errorf("%s, expect %v, got %v", c.description, e, a)
}
}
}
5 changes: 5 additions & 0 deletions aws/signer/v4/v4.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi
return http.Header{}, err
}

ctx.sanitizeHostForHeader()
ctx.assignAmzQueryValues()
ctx.build(v4.DisableHeaderHoisting)

Expand All @@ -363,6 +364,10 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi
return ctx.SignedHeaderVals, nil
}

func (ctx *signingCtx) sanitizeHostForHeader() {
request.SanitizeHostForHeader(ctx.Request)
}

func (ctx *signingCtx) handlePresignRemoval() {
if !ctx.isPresign {
return
Expand Down