Я создаю веб-приложение, которое включает функцию загрузки файлов. Моя цель - начать загрузку от пользователей непосредственно в корзину S3. Стратегия заключается в том, чтобы предварительно подписать запрос POST, который будет отправлен в виде формы.

Контрольно-пропускной пункт - это ошибка SignatureDoesNotMatch - насколько я могу судить, я соответствовал документации и изучил множество вариантов, но все еще не могу решить. Я могу создавать предварительно загруженные ссылки для скачивания.

Реферирование :

Документация AWS POST

Примере

boto3 generate_presigned_post Справка

Создать подписанный запрос:

def s3_upload_creds(name, user):
    s3 = boto3.client('s3')
    key = '${filename}'
    region = 'us-east-1'
    date_short = datetime.datetime.utcnow().strftime('%Y%m%d')
    date_long = datetime.datetime.utcnow().strftime('%Y%m%dT000000Z')
    fields = { 
        'acl': 'private',
        'date': date_short,
        'region': region,
        'x-amz-algorithm': 'AWS4-HMAC-SHA256',
        'x-amz-date': date_long
    }

    return s3.generate_presigned_post(
        Bucket = 'leasy',
        Fields = fields,
        Key = key,
        Conditions = [
            {'acl': 'private'},
            {'x-amz-algorithm': 'AWS4-HMAC-SHA256'},
            {'x-amz-credential': '/'.join(['AKI--snip--', date_short, region, 's3', 'aws4_request'])},
            {'x-amz-date': date_long}
        ]
    )

Форма загрузки (заполненная fields выше):

<html>
  <head>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

  </head>
  <body>
  {{ creds }}
  <form action="{{ creds.url }}" method="post" enctype="multipart/form-data">
    Key to upload: 
    <input type="input"  name="key" value="${filename}" /><br />
    <input type="input"  name="acl" value="{{ creds.fields.acl }}" />
    <input type="hidden" name="Policy" value="{{ creds.fields.policy }}" />
    <input type="text"   name="X-Amz-Algorithm" value="{{ creds.fields['x-amz-algorithm'] }}" />
    <input type="input"   name="X-Amz-Credential" value="{{ creds.fields.AWSAccessKeyId }}/{{ creds.fields.date }}/us-east-1/s3/aws4_request" />
    <input type="input"   name="X-Amz-Date" value="{{ creds.fields['x-amz-date'] }}" />
    <input type="input" name="X-Amz-Signature" value="{{ creds.fields.signature }}" />
    File: 
    <input type="file"   name="file" /> <br />
    <!-- The elements after this will be ignored -->
    <input type="submit" name="submit" value="Upload to Amazon S3" />
  </form>

</html>

Соответствующая часть ответа:

<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your key and signing method.
</Message>
<AWSAccessKeyId>AKI--snip--</AWSAccessKeyId>
<StringToSign>
eyJjb25kaXRpb25zIjogW3siYWNsIjogInByaXZhdGUifSwgeyJ4LWFtei1hbGdvcml0aG0iOiAiQVdTNC1ITUFDLVNIQTI1NiJ9LCB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQUlDVjRNVlBUUlFHU1lLV1EvMjAxNTEyMTgvdXMtZWFzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LCB7IngtYW16LWRhdGUiOiAiMjAxNTEyMThUMDAwMDAwWiJ9LCB7ImJ1Y2tldCI6ICJsZWFzeSJ9LCBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAiIl1dLCAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTE4VDA1OjEwOjU2WiJ9
</StringToSign>
<SignatureProvided>wDOjsBRc0iIW7JNtz/4GHgfvKaU=</SignatureProvided>

Расшифровка Base64 StringToSign в приведенной выше ошибке:

{u'conditions': [{u'acl': u'private'},
                 {u'x-amz-algorithm': u'AWS4-HMAC-SHA256'},
                 {u'x-amz-credential': u'AKI--snip--/20151218/us-east-1/s3/aws4_request'},
                 {u'x-amz-date': u'20151218T000000Z'},
                 {u'bucket': u'leasy'},
                 [u'starts-with', u'$key', u'']],
 u'expiration': u'2015-12-18T04:59:32Z'}
10
A--- 18 Дек 2015 в 07:40

3 ответа

Лучший ответ

Нашел решение: пришлось явно настроить клиент s3 для использования новой подписи Amazon v4. Ошибка возникает, поскольку по умолчанию используется более старая версия, что приводит к несоответствию. Немного подтрунивания лица - в то время это не было написано в boto3 документах, хотя люди в Amazon говорят, что это должно быть скоро.

Метод упрощен, поскольку теперь он возвращает именно те поля, которые необходимы:

def s3_upload_creds(name):
    BUCKET = 'mybucket'
    REGION = 'us-west-1'
    s3 = boto3.client('s3', region_name=REGION, config=Config(signature_version='s3v4'))
    key = '${filename}'
    return s3.generate_presigned_post(
        Bucket = BUCKET,
        Key = key
    )

Это означает, что форму можно легко сгенерировать:

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  </head>
  <body>
    {{ creds }}
      <form action="https://mybucket.s3.amazonaws.com" method="post" enctype="multipart/form-data">
        {% for key, value in creds.fields.items() %}
          <input type="hidden" name="{{ key }}" value="{{ value }}" />
        {% endfor %}
      File:
      <input type="file"   name="file" /> <br />
    <input type="submit" name="submit" value="Upload to Amazon S3" />
  </form>
</html>

Ура

5
A--- 28 Дек 2015 в 10:25

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

Я получаю сообщение об ошибке: "403: Указанный вами идентификатор ключа доступа AWS не существует в наших записях" при попытке загрузки в корзину s3 через мой предварительно назначенный URL-адрес.

Мне удалось сгенерировать предварительно назначенный URL-адрес, как описано выше, используя код на стороне сервера :

signed_url_dict = self.s3_client.generate_presigned_post(
                self.bucket_name,
                object_name,
                ExpiresIn=300

Это вернул словарь со структурой:

{
    url: "https://___",
    fields: {
        key: "___",
        AWSAccesKeyId: "___",
        x-amz-security-token: "___",
        policy: "___",
        signature: "___"
    }
}

Это привело к тому, что в 2019 году с использованием браузера на стороне браузера все немного изменилось, и требуемые входные данные для форм изменились. Вместо того, чтобы настраивать форму как OP, я должен был создать свою форму, как показано ниже:

<form action="https://pipeline-poc-ed.s3.amazonaws.com/" method="post" enctype="multipart/form-data" name="upload_form">
            <!-- Copy ALL of the 'fields' key:values returned by S3Client.generate_presigned_post() -->
            <input type="hidden" name="key" value="___" />
            <input type="hidden" name="AWSAccessKeyId" value="___" />
            <input type="hidden" name="policy" value="___"/>
            <input type="hidden" name="signature" value="___" />
            <input type="hidden" name="x-amz-security-token" value="___" />
        File:
            <input type="file"   name="file" /> <br />
            <input type="submit" name="submit" value="Upload to Amazon S3" />
        </form>

Моя ошибка заключалась в том, что я последовал примеру в boto3 1.9.138 docs и пропустил в форме «x-amz-security-token», что оказалось совершенно необходимым. Бездумная оплошность может расстаться, но, надеюсь, это поможет кому-то еще.

РЕДАКТИРОВАТЬ . Мои результаты, приведенные выше, основаны на лямбда-функции Н. Вирджинии. Когда я бежал generate_presigned_post(...) в Огайо (регион, в котором находится мое ведро), я получил результаты, похожие на OP:

{
  "url": "https://__",
  "fields": {
    "key": "___",
    "x-amz-algorithm": "___",
    "x-amz-credential": "___",
    "x-amz-date": "___",
    "x-amz-security-token": "___",
    "policy": "___",
    "x-amz-signature": "___"
  }
}

Возможно, результаты функции зависят от региона?

1
KC54 2 Май 2019 в 03:17

В моем случае я генерировал форму с кодировкой Base64.

Проблема была из-за того, что Firefox по своей природе кодирует значения токенов политики и безопасности в Base64, закодированный поверх него.

Таким образом, было двойное кодирование, и поэтому подпись не соответствовала требованиям.

0
Pranav Nandan 7 Июн 2019 в 09:25