Home > Blockchain >  Why does my GetObjectRequest return a 403 Access Denied error when my Lambda has access to the S3 bu
Why does my GetObjectRequest return a 403 Access Denied error when my Lambda has access to the S3 bu

Time:10-12

I have a Lambda function that makes a GetObject request to an S3 bucket.

However, I'm getting the following error:

AccessDenied: Access Denied
    at deserializeAws_restXmlGetObjectCommandError (/node_modules/@aws-sdk/client-s3/dist-cjs/protocols/Aws_restXml.js:6284:41)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    at /node_modules/@aws-sdk/middleware-serde/dist-cjs/deserializerMiddleware.js:6:20
    at /node_modules/@aws-sdk/middleware-signing/dist-cjs/middleware.js:11:20
    at StandardRetryStrategy.retry (/node_modules/@aws-sdk/middleware-retry/dist-cjs/StandardRetryStrategy.js:51:46)
    at /node_modules/@aws-sdk/middleware-logger/dist-cjs/loggerMiddleware.js:6:22
    at GetS3Data (/src/input.ts:21:26)
    at Main (/src/main.ts:8:34)
    at Runtime.run [as handler] (/handler.ts:6:9) {
  Code: 'AccessDenied',
  RequestId: '3K61PMQGW4825D3W',
  HostId: '5PpmWpu2I4WZPx37Y0pRfDAcdCmjX8fchuE HLpUzy7uqoJirtb9Os0g96kWfluM/ctkn/mEC5o=',
  '$fault': 'client',
  '$metadata': {
    httpStatusCode: 403,
    requestId: undefined,
    extendedRequestId: '5PpmWpu2I4WZPx37Y0pRfDAcdCmjX8fchuE HLpUzy7uqoJirtb9Os0g96kWfluM/ctkn/mEC5o=',
    cfId: undefined,
    attempts: 1,
    totalRetryDelay: 0
  }
}

I've given access to the Lambda function to make this request.

What is the issue?

serverless.ts

import type { AWS } from "@serverless/typescript";

const serverlessConfiguration: AWS = {
    service: "affiliations",
    frameworkVersion: "2",
    custom: {
        esbuild: {
            bundle: true,
            minify: false,
            sourcemap: true,
            exclude: ["aws-sdk"],
            target: "node14",
            define: { "require.resolve": undefined },
            platform: "node",
        },
    },
    plugins: ["serverless-esbuild"],
    provider: {
        name: "aws",
        region: "us-east-2",
        runtime: "nodejs14.x",
        apiGateway: {
            minimumCompressionSize: 1024,
            shouldStartNameWithService: true,
        },
        environment: {
            AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1",
            NODE_OPTIONS: "--enable-source-maps --stack-trace-limit=1000",
        },
        lambdaHashingVersion: "20201221",
        vpc: {
            securityGroupIds: ["<redacted>"],
            subnetIds: ["redacted>"],
        },
        iam: {
            role: {
                statements: [
                    {
                        Effect: "Allow",
                        Action: ["s3:GetObject"],
                        Resource: "<redacted>",
                    },
                ],
            },
        },
    },
    useDotenv: true,
    // import the function via paths
    functions: {
        run: {
            handler: "handler.run",
            timeout: 300,
            events: [
                {
                    sns: {
                        arn: "<redacted>",
                    },
                },
            ],
        },
    },
};

module.exports = serverlessConfiguration;

s3.ts

export const GetS3Data = async (payload: GetObjectRequest) => {
    try {
        const response = await S3Service.getObject(payload);

        const result = await new Promise((resolve, reject) => {
            const data = [];
            response.Body.on("data", (chunk) => data.push(chunk));
            response.Body.on("err", reject);
            response.Body.once("end", () => resolve(data.join("")));
        });

        return [result, null];
    } catch (err) {
        Logger.error({
            method: "GetS3Data",
            error: err.stack,
        });

        return [null, err];
    }
};

package.json

"@aws-sdk/client-s3": "^3.36.0",

CodePudding user response:

Forgot to add /* to the end of the resource

Resource: "<redacted>/*",

CodePudding user response:

Your 403 Access Denied error is masking a 404 Not Found error, as your code & Serverless config looks perfectly fine & should work as expected provided you've specified the resource correctly.


If you do not have correct s3:ListBucket permissions, the S3 endpoint will not return a 404 Not Found error if the object does not exist for the specified key.

GetObject's API reference highlights this nuance:

If you have the s3:ListBucket permission on the bucket, Amazon S3 will return an HTTP status code 404 ("no such key") error.

If you don’t have the s3:ListBucket permission, Amazon S3 will return an HTTP status code 403 ("access denied") error.

This is to prevent attackers from enumerating public buckets & knowing what objects actually exist in the bucket.

The absence of a 404, in this case, is not allowing information to be leaked on if the object exists or not (just like an Invalid Credentials message on a login page as opposed to Invalid Password which indicates a user with the provided username exists).


Provide the Lambda with permission to carry out the s3:ListBucket action to unmask the 404 error and/or ultimately double-check your GetObjectRequest to make sure the key is being specified correctly for an object that does exist:

iam: {
    role: {
        statements: [
            {
                Effect: "Allow",
                Action: ["s3:GetObject", "s3:ListBucket"],
                Resource: "<redacted>",
            },
        ],
    },
}
  • Related