Path: blob/dev/pkg/protocols/http/signer/aws-sign.go
2072 views
package signer12import (3"context"4"crypto/sha256"5"encoding/hex"6"errors"7"io"8"net/http"9"time"1011"github.com/aws/aws-sdk-go-v2/aws"12v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"13awsconfig "github.com/aws/aws-sdk-go-v2/config"14"github.com/aws/aws-sdk-go-v2/credentials"15"github.com/projectdiscovery/gologger"16"github.com/projectdiscovery/utils/errkit"17)1819const defaultEmptyPayloadHash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"2021// AWSOptions22type AWSOptions struct {23AwsID string24AwsSecretToken string25Service string26Region string27}2829// Validate Signature Arguments30func (a *AWSOptions) Validate() error {31if a.Service == "" {32return errors.New("aws service cannot be empty")33}34if a.Region == "" {35return errors.New("aws region cannot be empty")36}3738return nil39}4041// AWS v4 signer42type AWSSigner struct {43creds *aws.Credentials44signer *v4.Signer45options *AWSOptions46}4748// SignHTTP49func (a *AWSSigner) SignHTTP(ctx context.Context, request *http.Request) error {50if region, ok := ctx.Value(SignerArg("region")).(string); ok && region != "" {51a.options.Region = region52}53if service, ok := ctx.Value(SignerArg("service")).(string); ok && service != "" {54a.options.Service = service55}56if err := a.options.Validate(); err != nil {57return err58}59// contentHash is sha256 hash of response body60contentHash := a.getPayloadHash(request)61if err := a.signer.SignHTTP(ctx, *a.creds, request, contentHash, a.options.Service, a.options.Region, time.Now()); err != nil {62return errkit.Wrap(err, "failed to sign http request using aws v4 signer")63}64// add x-amz-content-sha256 header to request65request.Header.Set("x-amz-content-sha256", contentHash)66return nil67}6869// getPayloadHash returns hex encoded SHA-256 of request body70func (a *AWSSigner) getPayloadHash(request *http.Request) string {71if request.Body == nil {72// Default Hash of Empty Payload73return defaultEmptyPayloadHash74}7576// no need to close request body since it is a reusablereadercloser77bin, err := io.ReadAll(request.Body)78if err != nil {79gologger.Error().Msgf("aws signer: failed to read request body: %s", err)80}81sha256Hash := sha256.Sum256(bin)82return hex.EncodeToString(sha256Hash[:])83}8485// NewAwsSigner86func NewAwsSigner(opts *AWSOptions) (*AWSSigner, error) {87credcache := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(opts.AwsID, opts.AwsSecretToken, ""))88awscred, err := credcache.Retrieve(context.TODO())89if err != nil {90return nil, err91}92return &AWSSigner{93creds: &awscred,94options: opts,95signer: v4.NewSigner(),96}, nil97}9899// NewAwsSignerFromConfig100func NewAwsSignerFromConfig(opts *AWSOptions) (*AWSSigner, error) {101/*102NewAwsSignerFromConfig fetches credentials from both1031. Environment Variables (old & new)1042. Shared Credentials ($HOME/.aws)105*/106cfg, err := awsconfig.LoadDefaultConfig(context.TODO())107if err != nil {108return nil, err109}110credcache := aws.NewCredentialsCache(cfg.Credentials)111awscred, err := credcache.Retrieve(context.TODO())112if err != nil {113return nil, err114}115return &AWSSigner{116creds: &awscred,117options: opts,118signer: v4.NewSigner(func(signer *v4.SignerOptions) {119// signer.DisableURIPathEscaping = true120}),121}, nil122}123124var AwsSkipList = map[string]interface{}{125"region": struct{}{},126}127128var AwsDefaultVars = map[string]interface{}{129"region": "us-east-2",130"service": "sts",131}132133var AwsInternalOnlyVars = map[string]interface{}{134"aws-id": struct{}{},135"aws-secret": struct{}{},136}137138139