Доступ запрещен при выполнении файла PUT с использованием предварительно подписанного URL-адреса S3

Проблема: как я могу успешно выполнить запрос файла PUT на предварительно подписанный URL-адрес, в котором корзина имеет следующие разрешения:

  • Блокировать публичный доступ = отключено для всех 4 опций
  • Список контроля доступа = публичное чтение
  • Политика сегмента = пусто
  • Конфигурация CORS = PUT

Вот конкретный пример того, что я сделал …

Я выполнил POST для своей presign конечной точки …

> POST /dev/presign HTTP/2
> Host: 9xugkdzvch.execute-api.ap-southeast-1.amazonaws.com
> user-agent: insomnia/2020.2.2
> content-type: application/json
> accept: */*
> content-length: 51

* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!

| {
|   "name": "profile_1.png",
|   "type": "image/png"
| }

он вернулся

{
  "statusCode": 200,
  "headers": {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Credentials": true
  },
  "body": {
    "error": false,
    "data": "https://profilephoto-upload-dev.s3.ap-southeast-1.amazonaws.com/profiles/profile_1.png?AWSAccessKeyId=ASIA4IWFXQ2GLT2FGUUA&Content-Type=image%2Fpng&Expires=1593475190&Signature=JO4pNtbXmb56OYiClZhil4hse0c%3D&x-amz-meta-username=foobar&x-amz-security-token=IQoJb3JpZ2luX2VjEIj%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaDmFwLXNvdXRoZWFzdC0xIkcwRQIhAP5kW%2Fj6Y5BiLgAhec3w6bv17CL8QuEabOy7hhhtK81JAiA7pS6IkGE3WtnIcD78%2BOytl1luSdLBkI9fg%2F2GzxpApirUBQghEAAaDDg0MzMwMjAxMjU1NiIMdbMwBimx3k0miN5lKrEFfUAFtMJep90tfYQqwW09Od1HTSaUKkVUqP0eOaQg89vEnplJ%2BCQwKopOF8Ed8AiFAFQ9qJcbtmg39wEqIBa5Z0oOdfDEphRXDmVWX%2BCQvdCCoPbbZeaK2tBg4PN2f%2FL8OBa4AebUMCwnVJVneA7Pf5p7hVKKeP%2BePDfMWYOpChS9smp0UjfoVSKYDDA1GDS4eE%2FTN02KjnLdVOWPqoAOm%2FurDzbs%2FYe8B2xnC6R0%2FpbMrj2j59bHB9d8f2GcKXOfiHpeKff%2BLNYYsSmEAApsiW2N05ZMTAWBZQTUyWy53jFhYrZU11uo8RlzKoJOiI6pNoufk4CtbdtVYLxdvQZuxQm7TRJNMLDAQQcL6qR07N0sxHMeo7IIe5QNLT%2B%2FUwqG4X3FJxhYHnSvP7CRT%2FtlS%2Fxutl3rNpthpxDplRBDMtc5rSig6H%2Bb9O1vFZKAG%2B%2F9J%2BE4sCWfckh7kANCYA%2BY3bDHvkTj%2BKKWckzpQB4yLyPD4L%2FbiUDm9T520C9ROwXXhMAun%2BI2hxidauOTkyx%2Bzny5M8zTMN8gvVcxlIHgQHCYy6776WdI2qk%2FmKxYM814s74zt4o3AhtIDTJfEJV0NriqcwxOzZqsnGXPBP9fxri5t57M%2B%2BeONpjcFJpRhj%2B1Os1omCe3CL1R84RsrVPfc8FzGu12yM15FeI0Qb5duqgH3kWdc6wEflRsGPWPfVqHdWfj8euhGQXwsE2m4tcglZdYCpxWfMnbF%2Foz8TOJv9b3ODFrYNTtaeG9arUbLZ8CPqWH8Nyag4MNRgZAZRJNHbc4eBKqi9YHp1p%2FCillnFgmoPMwccyziv%2BGAPkjtS9KCLb0XWEwJwJLgLWNllVka9g%2BlODt6HK2mPb0zPLpsQuDI5qQwnBlSWPbhqwpoWyatvmV7aPaeF3dJm5yBVKAMyAwqPbp9wU6zQLBnlCtwUNRh0HMSUwfPWoFlpweESBTUzjEQFRui7g874ksQtlF51dpUsdXF7JI1kV%2FWr8A%2BxhxlMEjxyhoTj70hUVO7V55I7Ppx3Iyz5AlBAoh2nu4NZccHY4JYjU3de%2FYWVa0jfBSxP0WU%2F5GHbwbENjCpgEMhpDVcFf409UN8ecQi53nQh8ytjsDJMTwYalBM8ys1BfXdvLMkL2dtBFTfHtVjIGtrOkTxfH3CtbT24CzAtWrXxBCY%2BDd97o3oi6ztloybTJtxhI8Zdb6vojsFm95QVmVAVO5PaeZ%2BgHOoqHFHJ%2BXG1kbV8OexmT1S1bAXuu%2Fa07HDjJzqBYovvzWa%2BaG9pG8SKiw0N%2Bb5mdxMDdcGEqVIMwUK7mf7sY06Z%2Fbl1SQvErfpEsgA67C1Qtrgb0fKetVMlQR4Zq%2F53H%2FrFrys2OAnPXUfVLiqWY%3D",
    "message": null
  }
}

Затем я использовал URL-адрес в data для выполнения запроса PUT, и он не удался с AccessDenied

PUT /profiles/profile_1.png?AWSAccessKeyId=ASIA4IWFXQ2GPP2VWPFE&Content-Type=image%2Fpng&Expires=1593513254&Signature=vmXn5%2Fk%2Bk3hh98B8OeNlU%2F8XetY%3D&x-amz-meta-username=foobar&x-amz-security-token=IQoJb3JpZ2luX2VjEJL%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaDmFwLXNvdXRoZWFzdC0xIkcwRQIhAK6oeGhiy%2BuWwonWYViWa5pzJxZlSwB5MWjuLcZas6NvAiBmArs%2B8noZacruG0dlWrUyIUm30WFLKUYACVW2xMCFmyrUBQgrEAAaDDg0MzMwMjAxMjU1NiIMgQFekIbk3lBaS%2BmyKrEFBrdMyiCwa1iKVcgFxO%2BW1RTCu5M9aKotfJtBCxtW8gGkUY8VsMsVLPYUDWrJU07z7QjdRIK5xR3%2BdWtkK%2FvW6ZULjqrtVwiDbwnncug7FvvzWBCzc2oOVUFenSBNXhr%2FGnlCbqk6mFyqkOtCeFffOYtls19tU56GsSa1aaTwdwQcRld02G1TJCgj6egjgxSoHqDFbntlHU84vWcW5wi1O4YX4NwAEhWmiLC%2BU6RsfbR0NA%2BzJHlRGShJhEAY1pxOmm3533DAzSjlhj3yvTJN8whT4jFUEvn0jWFCLb3u9rC99%2FwjgtPH0jlU%2BqESTP0VlATuCU%2F5by1iTyX4TLRFyF0vCqx5%2BEePH0SdZeVDEqi%2BIgXIkOGG8fzJQkxLu536uCioSojmRYqiG16Osu9mAiP08LEGsIquzsIoIZmWve3P56gwk0pM28TWYz%2BhZtynJCXAIDOtm1gZ7j14ho1SrpIazM6fJ95NgmuqJfud%2BIGbXdytPZQj1jRsj2fH3vlw66LTXN6AKBVPy6yTf45fMBfP2mywSZwkQtjEQ6hVN76nKD6Z1jZ%2F2Pbk%2FpFUwtcfYbGeKDIEpnV6h2%2BvsiaaNmos9eIYLkt6R9glYvInnIYvp6TOKJoicZgv%2BChr1lY8L728wCXnKLXxHTnv1YTnE5z%2FmtMbG4ZxYkj0ioaJt6kS2%2B3Lh%2BXdrbnwK7gbn3rFGT9EaTyeLSOymIB%2BHzAAfxB902g0c4jV6Em6B9xUxUgfXjHY1I5Gla1t36lEgAe7rlfngUlhoomJjDpMCw4IbzFnjvjXdAIzj7cJjmY%2FPxhdy4gp3pRvcgTkJ%2BV9DV8x2vMSgZS1qX5v%2FMQ%2BSQLStK5aY%2FjKqYkBt03102M1NovHpYiaV4XdE6%2F53unpdEyKtsKjUwYGLebd21kUhHkfoX4woqPs9wU6zQJwdh2wOJcRW9Wp6GmVLOyxfI23RxkAgygOjN5R1AUr%2F%2BehPrSr9iQsYjI%2FWzJqi7Ohzr3kZqjXuL4Yi1UKyRF6vyKckSDMQb371HypXkopMuoPxmNpZqI42IwO8%2BM0ktrJfnJ7Kp4i3EDw9eujy6Cuau5fsD8v6s0c3m5hNu1gmESfdmZr1%2BR%2FCVbifqgeLLwyGak96UyO%2BapQ4kOU6tu5uNskL1ewx%2F2K0esKq6qEz1IXA%2Fx5n5dioiom3GI9QVRfrtF7Z%2BhqBLipVfHMR2Pb4N3egoqBj80ux9u0UzYZ8rLst7Ns0tlJzmCHGxh0py9M7kUPAQmVMdQE4rAlP2FjMQiMWpOCiqo4uwADjKrlwinhSc98LPGYTxJ%2F8QveSfixbeZmgdC1gJkOAMUEdgnoh%2BeRB1dHR%2B3SavrFp1EIudMDIUxd4AxZSDYN%2F%2FY%3D HTTP/1.1
> Host: profilephoto-upload-dev.s3.ap-southeast-1.amazonaws.com
> User-Agent: insomnia/2020.2.2
> Content-Type: image/png
> Accept: */*
> Content-Length: 578256
<?xml version="1.0" encoding="UTF-8"?>
<Error>
  <Code>AccessDenied</Code>
  <Message>Access Denied</Message>
  <RequestId>A6037105527AC3DE</RequestId>
  <HostId>KqRDTOrRWDTjnQesY9ZIbBb95/lmqLmro7YLETogaA6qu4n0Jq8znwtExBxdpjDhgXr2Lg/MyqU=</HostId>
</Error>

Для данного: Вот мой presign код …

import S3 = require("aws-sdk/clients/s3")
import * as mime from "mime"

const createPresignedPost = async (key: string, contentType: string): Promise<string> => {
  const s3 = new S3()
  const params: S3.Types.PutObjectRequest = {
    Bucket: "profilephoto-upload-dev",
    Key: `profiles/${key}`,
    ContentType: contentType,
    Metadata: {username: "foobar"}
  }
  return s3.getSignedUrlPromise("putObject", params)
  .then(some => {
    console.log(`returned: ${some}`)
    return some
  })
}

app.post('/presign', async (req, res) => {
  const headers = {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Credentials": true
  }
  try {
    const key = `${req.body['name']}`
    const mimeType = mime.getType(req.body['name'])!
    const presignedPostData = await createPresignedPost(key, mimeType)
    res.status(200).send({
      statusCode: 200,
      headers,
      body: {
        error: false,
        data: presignedPostData,
        message: null
      }
    })
  } catch (error) {
    console.trace(JSON.stringify(error))
    res.status(500).send({
      statusCode: 500,
      headers,
      body: {
        error: true,
        data: null,
        message: error.message
      }
    })
  }
})

Этот экспресс-маршрут принадлежит созданной здесь лямбде …

  presignedpostdata:
    handler: ${self:custom.${self:custom.stage}.handler}
    role: arn:aws:iam::843302012556:role/service-role/auto_presignedpost_dev-role-w7qof39g
    events:
      - https:
          path: /presign
          method: post
          cors: true

Упомянутая роль ARN имеет эти разрешения …

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::profilephoto-upload-dev/*"
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "*"
        }
    ]
}

наконец, ведро S3 создается вот так …

    ProfilePhotoBucket:
      Type: AWS::S3::Bucket
      Properties:
          BucketName: profilephoto-upload-${self:custom.stage}
          AccessControl: PublicRead
          CorsConfiguration:
              CorsRules:
              -   AllowedHeaders: ['*']
                  AllowedMethods: ['PUT']
                  AllowedOrigins: ['*']

Я не копался глубоко в вашем коде, но думаю, вам нужны соответствующие заголовки auth в вашем запросе PUT. Подписанного URL-адреса недостаточно.   —  person deemonzter    schedule 30.06.2020

См. также:  Невозможно заставить вспомогательную функцию работать в AWS lambda nodejs

@ Брэд, не могли бы вы указать мне правильное направление, о чем appropriate auth headers ты говоришь?   —  person deemonzter    schedule 30.06.2020

Я проверю позже … У меня есть код, успешно использующий PUT от клиента. Если подумать, я могу использовать только заголовки, потому что я также указываю x-amz-acl и Cache-Control. Один или оба из них не могут быть установлены в подписанном URL-адресе.   —  person deemonzter    schedule 30.06.2020

@Brad, спасибо. Я действительно с нетерпением жду рабочего примера, потому что у меня заканчиваются идеи, как пройти через AccessDenied … AWS не предоставляет никаких полезных журналов ни на стороне консоли AWS …   —  person deemonzter    schedule 30.06.2020

Если вы можете показать фактический запрос и ответ HTTP PUT, это будет полезно.   —  person deemonzter    schedule 30.06.2020

@Brad обновил мой пост запросом PUT (проверьте выше). Это просто простой запрос PUT, указывающий на заранее подписанный URL-адрес с полезной нагрузкой файла.   —  person deemonzter    schedule 30.06.2020

Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 1
  1. deemonzter

    Я считаю, что проблема в том, что вы должны использовать s3.createPresignedPost вместо s3.getSignedUrlPromise, а затем вы должны использовать метод POST, а не PUT при отправке запроса.

    Я думаю, s3.getSignedUrlPromise требует, чтобы вы предоставили Body объекта во время подписания запроса, а s3.createPresignedPost разрешает отправку любого тела. С s3.createPresignedPost вы также можете добавить Conditions для выполнения определенных действий, например ограничить максимальный размер объекта.

    Видеть:

    Я могу попробовать вариант публикации … но чего я не понимаю, так это предоставления body объекта во время подписи, поскольку это создает большие накладные расходы для функции подписи person deemonzter; 30.06.2020

    Определенно не использовать для этого метод POST формы. Вы можете использовать PUT URL, вам просто нужны правильные заголовки. person deemonzter; 30.06.2020

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: