github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/http/signer/aws-sign.go (about)

     1  package signer
     2  
     3  import (
     4  	"context"
     5  	"crypto/sha256"
     6  	"encoding/hex"
     7  	"errors"
     8  	"io"
     9  	"net/http"
    10  	"time"
    11  
    12  	"github.com/aws/aws-sdk-go-v2/aws"
    13  	v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
    14  	awsconfig "github.com/aws/aws-sdk-go-v2/config"
    15  	"github.com/aws/aws-sdk-go-v2/credentials"
    16  	"github.com/projectdiscovery/gologger"
    17  	errorutil "github.com/projectdiscovery/utils/errors"
    18  )
    19  
    20  const defaultEmptyPayloadHash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    21  
    22  // AWSOptions
    23  type AWSOptions struct {
    24  	AwsID          string
    25  	AwsSecretToken string
    26  	Service        string
    27  	Region         string
    28  }
    29  
    30  // Validate Signature Arguments
    31  func (a *AWSOptions) Validate() error {
    32  	if a.Service == "" {
    33  		return errors.New("aws service cannot be empty")
    34  	}
    35  	if a.Region == "" {
    36  		return errors.New("aws region cannot be empty")
    37  	}
    38  
    39  	return nil
    40  }
    41  
    42  // AWS v4 signer
    43  type AWSSigner struct {
    44  	creds   *aws.Credentials
    45  	signer  *v4.Signer
    46  	options *AWSOptions
    47  }
    48  
    49  // SignHTTP
    50  func (a *AWSSigner) SignHTTP(ctx context.Context, request *http.Request) error {
    51  	if region, ok := ctx.Value(SignerArg("region")).(string); ok && region != "" {
    52  		a.options.Region = region
    53  	}
    54  	if service, ok := ctx.Value(SignerArg("service")).(string); ok && service != "" {
    55  		a.options.Service = service
    56  	}
    57  	if err := a.options.Validate(); err != nil {
    58  		return err
    59  	}
    60  	// contentHash is sha256 hash of response body
    61  	contentHash := a.getPayloadHash(request)
    62  	if err := a.signer.SignHTTP(ctx, *a.creds, request, contentHash, a.options.Service, a.options.Region, time.Now()); err != nil {
    63  		return errorutil.NewWithErr(err).Msgf("failed to sign http request using aws v4 signer")
    64  	}
    65  	// add x-amz-content-sha256 header to request
    66  	request.Header.Set("x-amz-content-sha256", contentHash)
    67  	return nil
    68  }
    69  
    70  // getPayloadHash returns hex encoded SHA-256 of request body
    71  func (a *AWSSigner) getPayloadHash(request *http.Request) string {
    72  	if request.Body == nil {
    73  		// Default Hash of Empty Payload
    74  		return defaultEmptyPayloadHash
    75  	}
    76  
    77  	// no need to close request body since it is a reusablereadercloser
    78  	bin, err := io.ReadAll(request.Body)
    79  	if err != nil {
    80  		gologger.Error().Msgf("aws signer: failed to read request body: %s", err)
    81  	}
    82  	sha256Hash := sha256.Sum256(bin)
    83  	return hex.EncodeToString(sha256Hash[:])
    84  }
    85  
    86  // NewAwsSigner
    87  func NewAwsSigner(opts *AWSOptions) (*AWSSigner, error) {
    88  	credcache := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(opts.AwsID, opts.AwsSecretToken, ""))
    89  	awscred, err := credcache.Retrieve(context.TODO())
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	return &AWSSigner{
    94  		creds:   &awscred,
    95  		options: opts,
    96  		signer:  v4.NewSigner(),
    97  	}, nil
    98  }
    99  
   100  // NewAwsSignerFromConfig
   101  func NewAwsSignerFromConfig(opts *AWSOptions) (*AWSSigner, error) {
   102  	/*
   103  		NewAwsSignerFromConfig fetches credentials from both
   104  		1. Environment Variables (old & new)
   105  		2. Shared Credentials ($HOME/.aws)
   106  	*/
   107  	cfg, err := awsconfig.LoadDefaultConfig(context.TODO())
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	credcache := aws.NewCredentialsCache(cfg.Credentials)
   112  	awscred, err := credcache.Retrieve(context.TODO())
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	return &AWSSigner{
   117  		creds:   &awscred,
   118  		options: opts,
   119  		signer: v4.NewSigner(func(signer *v4.SignerOptions) {
   120  			// signer.DisableURIPathEscaping = true
   121  		}),
   122  	}, nil
   123  }
   124  
   125  var AwsSkipList = map[string]interface{}{
   126  	"region": struct{}{},
   127  }
   128  
   129  var AwsDefaultVars = map[string]interface{}{
   130  	"region":  "us-east-2",
   131  	"service": "sts",
   132  }
   133  
   134  var AwsInternalOnlyVars = map[string]interface{}{
   135  	"aws-id":     struct{}{},
   136  	"aws-secret": struct{}{},
   137  }