From 535f97ba61fdd458e2947aba856a8864234679c3 Mon Sep 17 00:00:00 2001 From: ruspaul013 <61197032+ruspaul013@users.noreply.github.com> Date: Thu, 27 Jul 2023 21:44:56 +0300 Subject: [PATCH] check if metadata headers/url values are equal with signed headers (#17737) --- cmd/signature-v4-utils.go | 25 ++++++++++++ cmd/signature-v4-utils_test.go | 71 +++++++++++++++++++++++++++++++++- cmd/signature-v4.go | 8 +++- 3 files changed, 102 insertions(+), 2 deletions(-) diff --git a/cmd/signature-v4-utils.go b/cmd/signature-v4-utils.go index c635583a0..224df7fc6 100644 --- a/cmd/signature-v4-utils.go +++ b/cmd/signature-v4-utils.go @@ -260,3 +260,28 @@ func signV4TrimAll(input string) string { // unicode.IsSpace() internally here) to one space and return return strings.Join(strings.Fields(input), " ") } + +// checkMetaHeaders will check if the metadata from header/url is the same with the one from signed headers +func checkMetaHeaders(signedHeadersMap http.Header, r *http.Request) APIErrorCode { + // check values from http header + for k, val := range r.Header { + if stringsHasPrefixFold(k, "X-Amz-Meta-") { + if signedHeadersMap.Get(k) == val[0] { + continue + } + return ErrUnsignedHeaders + } + } + + // check values from url, if no http header + for k, val := range r.Form { + if stringsHasPrefixFold(k, "x-amz-meta-") { + if signedHeadersMap.Get(http.CanonicalHeaderKey(k)) == val[0] { + continue + } + return ErrUnsignedHeaders + } + } + + return ErrNone +} diff --git a/cmd/signature-v4-utils_test.go b/cmd/signature-v4-utils_test.go index c125bfcd0..b5893cc71 100644 --- a/cmd/signature-v4-utils_test.go +++ b/cmd/signature-v4-utils_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2021 MinIO, Inc. +// Copyright (c) 2015-2023 MinIO, Inc. // // This file is part of MinIO Object Storage stack // @@ -339,3 +339,72 @@ func TestGetContentSha256Cksum(t *testing.T) { } } } + +// Test TestCheckMetaHeaders tests the logic of checkMetaHeaders() function +func TestCheckMetaHeaders(t *testing.T) { + signedHeadersMap := map[string][]string{ + "X-Amz-Meta-Test": {"test"}, + "X-Amz-Meta-Extension": {"png"}, + "X-Amz-Meta-Name": {"imagepng"}, + } + expectedMetaTest := "test" + expectedMetaExtension := "png" + expectedMetaName := "imagepng" + r, err := http.NewRequest(http.MethodPut, "http://play.min.io:9000", nil) + if err != nil { + t.Fatal("Unable to create http.Request :", err) + } + + // Creating input http header. + inputHeader := r.Header + inputHeader.Set("X-Amz-Meta-Test", expectedMetaTest) + inputHeader.Set("X-Amz-Meta-Extension", expectedMetaExtension) + inputHeader.Set("X-Amz-Meta-Name", expectedMetaName) + // calling the function being tested. + errCode := checkMetaHeaders(signedHeadersMap, r) + if errCode != ErrNone { + t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrNone, errCode) + } + + // Add new metadata in inputHeader + inputHeader.Set("X-Amz-Meta-Clone", "fail") + // calling the function being tested. + errCode = checkMetaHeaders(signedHeadersMap, r) + if errCode != ErrUnsignedHeaders { + t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrUnsignedHeaders, errCode) + } + + // Delete extra metadata from header to don't affect other test + inputHeader.Del("X-Amz-Meta-Clone") + // calling the function being tested. + errCode = checkMetaHeaders(signedHeadersMap, r) + if errCode != ErrNone { + t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrNone, errCode) + } + + // Creating input url values + r, err = http.NewRequest(http.MethodPut, "http://play.min.io:9000?x-amz-meta-test=test&x-amz-meta-extension=png&x-amz-meta-name=imagepng", nil) + if err != nil { + t.Fatal("Unable to create http.Request :", err) + } + + r.ParseForm() + // calling the function being tested. + errCode = checkMetaHeaders(signedHeadersMap, r) + if errCode != ErrNone { + t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrNone, errCode) + } + + // Add extra metadata in url values + r, err = http.NewRequest(http.MethodPut, "http://play.min.io:9000?x-amz-meta-test=test&x-amz-meta-extension=png&x-amz-meta-name=imagepng&x-amz-meta-clone=fail", nil) + if err != nil { + t.Fatal("Unable to create http.Request :", err) + } + + r.ParseForm() + // calling the function being tested. + errCode = checkMetaHeaders(signedHeadersMap, r) + if errCode != ErrUnsignedHeaders { + t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrUnsignedHeaders, errCode) + } +} diff --git a/cmd/signature-v4.go b/cmd/signature-v4.go index 59678d6a8..7e94a967d 100644 --- a/cmd/signature-v4.go +++ b/cmd/signature-v4.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2021 MinIO, Inc. +// Copyright (c) 2015-2023 MinIO, Inc. // // This file is part of MinIO Object Storage stack // @@ -229,6 +229,12 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s return errCode } + // Check if the metadata headers are equal with signedheaders + errMetaCode := checkMetaHeaders(extractedSignedHeaders, r) + if errMetaCode != ErrNone { + return errMetaCode + } + // If the host which signed the request is slightly ahead in time (by less than globalMaxSkewTime) the // request should still be allowed. if pSignValues.Date.After(UTCNow().Add(globalMaxSkewTime)) {