[#615] Use UNSIGNED_PAYLOAD to check sign

Use `UNSIGNED_PAYLOAD` to check signature if x-amz-content-sha256 isn't provided as signed header

https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html

" You include the literal string UNSIGNED-PAYLOAD when constructing a canonical request, and set the same value as the x-amz-content-sha256 header value when sending the request to Amazon S3"

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
Denis Kirillov 2025-01-28 17:58:29 +03:00 committed by Alexey Vanin
parent 510b0a1005
commit 3d3dd00211
6 changed files with 142 additions and 23 deletions

View file

@ -8,6 +8,7 @@ import (
"net/http"
"net/http/httptest"
"net/url"
"os"
"strings"
"testing"
"time"
@ -26,6 +27,9 @@ import (
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
smithyauth "github.com/aws/smithy-go/auth"
"github.com/aws/smithy-go/logging"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
@ -298,6 +302,7 @@ func TestAuthenticate(t *testing.T) {
for _, tc := range []struct {
name string
region string
prefixes []string
request *http.Request
err bool
@ -308,10 +313,23 @@ func TestAuthenticate(t *testing.T) {
prefixes: []string{addr.Container().String()},
request: func() *http.Request {
r := httptest.NewRequest(http.MethodPost, "/", nil)
err = defaultSigner.SignHTTP(ctx, awsCreds, r, UnsignedPayload, service, region, time.Now())
require.NoError(t, err)
return r
}(),
region: region,
},
{
name: "valid sign with hash",
prefixes: []string{addr.Container().String()},
request: func() *http.Request {
r := httptest.NewRequest(http.MethodPost, "/", nil)
r.Header.Set(AmzContentSHA256, "")
err = defaultSigner.SignHTTP(ctx, awsCreds, r, "", service, region, time.Now())
require.NoError(t, err)
return r
}(),
region: region,
},
{
name: "no authorization header",
@ -418,12 +436,27 @@ func TestAuthenticate(t *testing.T) {
request: func() *http.Request {
r := httptest.NewRequest(http.MethodPost, "/", nil)
r.Header.Set(AmzExpires, "60")
signedURI, _, err := defaultSigner.PresignHTTP(ctx, awsCreds, r, UnsignedPayload, service, region, time.Now())
require.NoError(t, err)
r.URL, err = url.ParseRequestURI(signedURI)
require.NoError(t, err)
return r
}(),
region: region,
},
{
name: "valid presign with hash",
request: func() *http.Request {
r := httptest.NewRequest(http.MethodPost, "/", nil)
r.Header.Set(AmzExpires, "60")
r.Header.Set(AmzContentSHA256, "")
signedURI, _, err := defaultSigner.PresignHTTP(ctx, awsCreds, r, "", service, region, time.Now())
require.NoError(t, err)
r.URL, err = url.ParseRequestURI(signedURI)
require.NoError(t, err)
return r
}(),
region: region,
},
{
name: "presign, bad X-Amz-Credential",
@ -480,6 +513,56 @@ func TestAuthenticate(t *testing.T) {
err: true,
errCode: errors.ErrBadRequest,
},
{
name: "presign using original aws sdk",
request: func() *http.Request {
cli := s3.NewPresignClient(s3.New(s3.Options{
Credentials: credentials.NewStaticCredentialsProvider(awsCreds.AccessKeyID, awsCreds.SecretAccessKey, ""),
UsePathStyle: true,
BaseEndpoint: aws.String("http://localhost"),
Region: region,
Logger: logging.NewStandardLogger(os.Stdout),
ClientLogMode: aws.LogSigning,
}))
res, err := cli.PresignGetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String("bucket"),
Key: aws.String("object"),
})
require.NoError(t, err)
r := httptest.NewRequest(http.MethodGet, "http://localhost", nil)
r.URL, err = url.ParseRequestURI(res.URL)
require.NoError(t, err)
return r
}(),
region: region,
},
{
name: "presign sigv4a using original aws sdk",
request: func() *http.Request {
cli := s3.NewPresignClient(s3.New(s3.Options{
Credentials: credentials.NewStaticCredentialsProvider(awsCreds.AccessKeyID, awsCreds.SecretAccessKey, ""),
UsePathStyle: true,
BaseEndpoint: aws.String("http://localhost"),
Region: region,
Logger: logging.NewStandardLogger(os.Stdout),
ClientLogMode: aws.LogSigning,
AuthSchemeResolver: resolver{},
}))
res, err := cli.PresignGetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String("bucket"),
Key: aws.String("object"),
})
require.NoError(t, err)
r := httptest.NewRequest(http.MethodGet, "http://localhost", nil)
r.URL, err = url.ParseRequestURI(res.URL)
require.NoError(t, err)
return r
}(),
},
} {
t.Run(tc.name, func(t *testing.T) {
creds := tokens.New(bigConfig)
@ -495,13 +578,19 @@ func TestAuthenticate(t *testing.T) {
} else {
require.NoError(t, err)
require.Equal(t, accessKeyID, box.AuthHeaders.AccessKeyID)
require.Equal(t, region, box.AuthHeaders.Region)
require.Equal(t, tc.region, box.AuthHeaders.Region)
require.Equal(t, secret.SecretKey, box.AccessBox.Gate.SecretKey)
}
})
}
}
type resolver struct{}
func (r resolver) ResolveAuthSchemes(context.Context, *s3.AuthResolverParameters) ([]*smithyauth.Option, error) {
return []*smithyauth.Option{{SchemeID: smithyauth.SchemeIDSigV4A}}, nil
}
func TestHTTPPostAuthenticate(t *testing.T) {
const (
policyBase64 = "eyJleHBpcmF0aW9uIjogIjIwMjUtMTItMDFUMTI6MDA6MDAuMDAwWiIsImNvbmRpdGlvbnMiOiBbCiBbInN0YXJ0cy13aXRoIiwgIiR4LWFtei1jcmVkZW50aWFsIiwgIiJdLAogWyJzdGFydHMtd2l0aCIsICIkeC1hbXotZGF0ZSIsICIiXQpdfQ=="