From 6dec60b6e6fe912f5e4b63062fa8d4a2ac1e1b45 Mon Sep 17 00:00:00 2001 From: jiuker <2818723467@qq.com> Date: Tue, 26 Sep 2023 03:35:25 +0800 Subject: [PATCH] fix: check post policy like AWS S3 (#18074) --- cmd/post-policy_test.go | 12 +++++++----- cmd/postpolicyform.go | 43 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/cmd/post-policy_test.go b/cmd/post-policy_test.go index 957f3342b..5f956d80f 100644 --- a/cmd/post-policy_test.go +++ b/cmd/post-policy_test.go @@ -593,11 +593,13 @@ func newPostRequestV2(endPoint, bucketName, objectName string, accessKey, secret signature := calculateSignatureV2(encodedPolicy, secretKey) formData := map[string]string{ - "AWSAccessKeyId": accessKey, - "bucket": bucketName, - "key": objectName + "/${filename}", - "policy": encodedPolicy, - "signature": signature, + "AWSAccessKeyId": accessKey, + "bucket": bucketName, + "key": objectName + "/${filename}", + "policy": encodedPolicy, + "signature": signature, + "X-Amz-Ignore-signature": "", + "X-Amz-Ignore-AWSAccessKeyId": "", } // Create the multipart form. diff --git a/cmd/postpolicyform.go b/cmd/postpolicyform.go index f6bfb9574..340c83b5e 100644 --- a/cmd/postpolicyform.go +++ b/cmd/postpolicyform.go @@ -30,7 +30,9 @@ import ( "time" "github.com/bcicen/jstream" + "github.com/minio/minio-go/v7/pkg/encrypt" "github.com/minio/minio-go/v7/pkg/set" + xhttp "github.com/minio/minio/internal/http" ) // startWithConds - map which indicates if a given condition supports starts-with policy operator @@ -51,6 +53,18 @@ var startsWithConds = map[string]bool{ "$x-amz-date": false, } +var postPolicyIgnoreKeys = map[string]bool{ + "Policy": true, + xhttp.AmzSignature: true, + xhttp.ContentEncoding: true, + http.CanonicalHeaderKey(xhttp.AmzChecksumAlgo): true, + http.CanonicalHeaderKey(xhttp.AmzChecksumCRC32): true, + http.CanonicalHeaderKey(xhttp.AmzChecksumCRC32C): true, + http.CanonicalHeaderKey(xhttp.AmzChecksumSHA1): true, + http.CanonicalHeaderKey(xhttp.AmzChecksumSHA256): true, + http.CanonicalHeaderKey(xhttp.AmzChecksumMode): true, +} + // Add policy conditionals. const ( policyCondEqual = "eq" @@ -265,6 +279,22 @@ func checkPostPolicy(formValues http.Header, postPolicyForm PostPolicyForm) erro if !postPolicyForm.Expiration.After(UTCNow()) { return fmt.Errorf("Invalid according to Policy: Policy expired") } + // check all formValues appear in postPolicyForm or return error. #https://github.com/minio/minio/issues/17391 + checkHeader := map[string][]string{} + ignoreKeys := map[string]bool{} + for key, value := range formValues { + switch { + case ignoreKeys[key], postPolicyIgnoreKeys[key], strings.HasPrefix(key, encrypt.SseGenericHeader): + continue + case strings.HasPrefix(key, "X-Amz-Ignore-"): + ignoreKey := strings.Replace(key, "X-Amz-Ignore-", "", 1) + ignoreKeys[ignoreKey] = true + // if it have already + delete(checkHeader, ignoreKey) + default: + checkHeader[key] = value + } + } // map to store the metadata metaMap := make(map[string]string) for _, policy := range postPolicyForm.Conditions.Policies { @@ -292,6 +322,10 @@ func checkPostPolicy(formValues http.Header, postPolicyForm PostPolicyForm) erro formCanonicalName := http.CanonicalHeaderKey(strings.TrimPrefix(policy.Key, "$")) // Operator for the current policy condition op := policy.Operator + // Multiple values should not occur + if len(checkHeader[formCanonicalName]) >= 2 { + return fmt.Errorf("Invalid according to Policy: Policy Condition failed: [%s, %s, %s]. FormValues have multiple values: [%s]", op, policy.Key, policy.Value, strings.Join(checkHeader[formCanonicalName], ", ")) + } // If the current policy condition is known if startsWithSupported, condFound := startsWithConds[policy.Key]; condFound { // Check if the current condition supports starts-with operator @@ -311,6 +345,15 @@ func checkPostPolicy(formValues http.Header, postPolicyForm PostPolicyForm) erro return fmt.Errorf("Invalid according to Policy: Policy Condition failed: [%s, %s, %s]", op, policy.Key, policy.Value) } } + delete(checkHeader, formCanonicalName) + } + + if len(checkHeader) != 0 { + logKeys := make([]string, 0, len(checkHeader)) + for key := range checkHeader { + logKeys = append(logKeys, key) + } + return fmt.Errorf("Each form field that you specify in a form (except %s) must appear in the list of conditions.", strings.Join(logKeys, ", ")) } return nil