Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signed URLs - Preventing attackers #221

Closed
abhisheksoni27 opened this issue Jun 13, 2020 · 9 comments
Closed

Signed URLs - Preventing attackers #221

abhisheksoni27 opened this issue Jun 13, 2020 · 9 comments

Comments

@abhisheksoni27
Copy link

Is it possible to use Signed URLs or a query parameter that can be passed and verified by Lambda? I am aware this is possible if I were to directly serve the image via S3.

If it is not possible, what do you recommend any potential business to do, so that their image keys are not left in the open?

Even if we make the keys as such they are non-iterable, it still cannot prevent a potential attacker to issue multiple requests to the same resource but with varying types of edits.

I suppose each edit will cost us a GET Request, and consequently hog our Billing statement.

What's the best practice that you guys are following/recommend?

If possible, I'd love to work on this and contribute.

@abhisheksoni27 abhisheksoni27 changed the title Signed URLs Signed URLs - Preventing attackers Jun 13, 2020
@beomseoklee
Copy link
Member

@abhisheksoni27 Thanks for contacting us.
I understand your security concerns. Just rough idea would be to provide query strings to sign CloudFront URL. Current version does not support to cache query strings unfortunately.

We will add this one to the back log item and consider how we can deliver this one to the customer.

@gmarchand
Copy link

Hello

My customer is already using Serverless Image Handler in production, with the Thumbor version. He would want to migrate to the last version with NodeJS but the "signed urls" feature is not yet available.

Could it be possible to implement this feature and the retro compatibility with Thumbor : https://thumbor.readthedocs.io/en/latest/security.html

Here is an example of url : https://images.xxx.com/Zds-GQ9PmM8YF3FtoDrxC2LiydI=/0x29:898x478/1600x0/images/-343694.jpg

@abhisheksoni27
Copy link
Author

abhisheksoni27 commented Jul 1, 2020

@beomseoklee Thanks, Beomseok, for your response. I believe this should be prioritized at the earliest as it is leaves things open to attackers. Any potential business using this in production might have to incur insane amount of charges. Please plan this accordingly. It is my sincere request to prioritize this at the earliest.

@pch
Copy link

pch commented Jul 13, 2020

+1 to what @abhisheksoni27 said. This is a serious concern. Currently anyone can decode base64 strings and tamper with them, potentially generating thousands of variants of the same image.

JSON payload should be HMAC-signed, using a secret key shared between the client and serverless-image-handler, e.g.:

const payload = {
  bucket: 'my-bucket',
  key: 'my-image.jpg',
  edits: {
    resize: { width: 200, height: 100, fit: 'outside' },
  }
}

const sig = createHmac("sha256", "SECRET").update(JSON.stringify(payload)).digest("hex")

const signedPayload = { sig, ...payload }

base64encode(JSON.stringify(signedPayload))

If the sig field is present, lambda should verify HMAC before generating an image.

@pch
Copy link

pch commented Jul 14, 2020

My quick take on this: #227

@beomseoklee
Copy link
Member

We've added the feature in v5.1.0, but due to our internal security policy, we used AWS Secrets Manager to store the secret key and value.

@chrisbag
Copy link

chrisbag commented Nov 30, 2020

Hi @beomseoklee
i am struggling to implement the signed urls using version 5.1. with aws Secrets Manager.
I have not found any details in the implementation guide.

I am getting a 403 error "Signature does not match"

I have found in the template the following code
const hash = crypto.createHmac('sha256', secretString[process.env.SECRET_KEY]).update(path).digest('hex');
This is the code I am using to try to generate the signed url

const request = {
        bucket: S3_BUCKET,
        key: filePath,
        edits: {
          // optional
          resize: {
            width: 600,
            fit: 'cover',
          },
          //   greyscale: true,
        },
      }

      const pathStr = Buffer.from(JSON.stringify(request)).toString('base64')
      const signature = crypto
        .createHmac('sha256', 'mysecretkey')
        .update(pathStr)
        .digest('hex')
      const url = `${domain}${pathStr}?signature=${signature}

Do you know what I might be doing wrong ?

Furthermore, when looking at the proposed signing approach, there is no expiration date as on the cloudfront.signed function in the javscript amazon sdk (https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CloudFront/Signer.html#constructor-property)

Thanks a lot for your help !
Chris

@chrisbag
Copy link

chrisbag commented Dec 1, 2020

I found what I was doing wrong. Here is the link for the correct implementation https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/considerations.html

However does this mean there is no possibility to add an expiry date ?

@asgerjensen
Copy link

Not to necro, just to understand: If your public client has this “mysecret” key, how does this stop the attacker from just grabbing that and still issuing a billion requests?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants