Как заставить клиента отправлять правильный sha256 в качестве ключа файла при загрузке s3? (указанный URL)

#amazon-web-services #amazon-s3 #aws-sdk #pre-signed-url #aws-sdk-js

#amazon-web-services #amazon-s3 #aws-sdk #предварительно подписанный URL #aws-sdk-js

Вопрос:

Мне нужно создать подписанный URL для загрузки файла в корзину s3. Ключом файла s3 должен быть его sha256 хэш.

Тогда возникает вопрос: как я могу убедиться, что клиент отправляет действительный хэш? Я создаю подписанный URL в своей лямбда-функции и избегаю передачи файла через него, поэтому лямбда, конечно, не может вычислить хэш.

Я думаю, что смогу добиться этого, используя 2 шага:

  1. Заставьте клиента отправить его вычисленный sha256 при загрузке. Исходя из спецификации, я предполагаю, что это будет автоматически проверяться при предоставлении его в x-amz-content-sha256 заголовке.

  2. Заставьте клиента отправлять тот же хэш в лямбда, чтобы я мог заставить его быть ключом.

Сначала я попробовал это:

 s3.getSignedUrl('putObject', { Key: userProvidedSha256 }, callback)
  

Я попытался добавить условие, подобное { header: { 'X-Amz-Content-Sha256': userProvidedSha256 } } .

Но я не нашел способа добавить такое определение, чтобы оно фактически заставляло клиента отправлять X-Amz-Content-Sha256 заголовок.

Кроме того, я бы использовал тот же подход для обеспечения соблюдения фиксированного требуемого Content-Length заголовка (клиент отправляет желаемую длину на серверную часть, там мы его подписываем), но не уверен, что это сработает из-за этой проблемы.

Поскольку я обнаружил, что s3.createPresignedPost это также позволяет мне ограничить максимальный размер вложения и выглядит более гибким, я пошел по этому пути:

 const signPostFile = () => {
  const params = {
    Fields: {
      key: userProvidedSha256
    },
    Expires: 86400,
    Conditions: [
      ['content-length-range', 0, 10000000],
      { 'X-Amz-Content-Sha256': userProvidedSha256]
    ]
  }

  s3.createPresignedPost(params, callback)
}
  

Но пока это работает (это заставляет клиента отправлять принудительный заголовок sha256, и заголовок передается, см. Журнал запросов ниже), похоже, что теперь клиенту нужно добавить x-amz-content-sha256 в поля формы, а не в заголовок. Похоже, что это так, как задумано, но очевидно, что s3 не будет проверять отправленный файл на соответствие предоставленному sha256: любой файл, который я добавляю в форму, успешно загружен, даже если sha256 не соответствует.

Есть предложения, что не так, или как еще я могу обеспечить выполнение условия sha256, одновременно ограничивая длину содержимого?

Обновление: я использую signature v4, и я попробовал политику S3 Deny для этого условия:

 Condition:
  StringEquals:
    s3:x-amz-content-sha256: UNSIGNED-PAYLOAD
  

Соответствующий журнал запросов для отправки файла, содержащего строку «hello world»:

 
----------------------------986452911605138616518063
Content-Disposition: form-data; name="X-Amz-Content-Sha256"

b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
----------------------------986452911605138616518063
Content-Disposition: form-data; name="key"

b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
  

Комментарии:

1. Как вы в конечном итоге справились с этим? В настоящее время пытаюсь сделать то же самое. Также сделал запрос на форуме для этой функции здесь .

Ответ №1:

Насколько мне известно, S3 не предоставляет sha256 по умолчанию. Однако, прослушивая события S3, вы можете реализовать функцию Lambda, которая делает это автоматически для вас. Вот предложение, которое приходит на ум:

  1. Клиент запрашивает подписанный S3 URL на основе предоставленного пользователем sha256
  2. Клиент загружает файл, используя подписанный URL
  3. Функция Lambda настроена для прослушивания s3:ObjectCreated:* событий из корзины загрузки
  4. Когда загрузка завершена, функция Lambda запускается событием сообщения S3. Частью события является ключ объекта S3
  5. Функция Lambda загружает загруженный файл и повторно вычисляет sha256
  6. Функция Lambda удаляет файл, если вычисленное значение sha256 отличается от значения sha256, которое было предоставлено клиентом (либо в качестве ключа объекта, либо доступно из метаданных объектов)

В качестве альтернативы, если основной целью является проверка целостности загруженных файлов, S3 предоставляет другой вариант использования sha256 при вычислении контрольных сумм.

  1. Настройте политику корзины, чтобы принимать только запросы, которые были подписаны
  2. Настройте клиентский AWS S3 sdk для использования AWS signature версии 4, например

     const s3 = new AWS.S3({apiVersion: '2006-03-01', signatureVersion: 'v4'});
      
  3. Функция S3.putObject() подпишет запрос перед загрузкой файла

S3 не сохранит объект, если подпись неправильная, как описано в часто задаваемых вопросах AWS CLI S3

Комментарии:

1. Подход 1: это может сработать, но разве это не соответствует цели подписанных URL-адресов, что файлы не должны проходить через лямбда? В противном случае я мог бы напрямую отправлять файлы в lambda, а проверка выполняется централизованно?

2. Подход 2: мне трудно это понять. Я обновил вопрос (подпись, политика) и упростил здесь