AWS S3 - отправлять подписанные запросы не удается - PullRequest
0 голосов
/ 03 мая 2020

Пояснения:

Использование метода заголовка работает как для обычных (постоянных) учетных данных, так и для временных. Используя строку запроса (AKS вставляет все заголовки как часть URI), работает с постоянными учетными данными, но не работает с временными учетными данными со следующей ошибкой:

The request signature we calculated does not match the signature you provided. Check your key and signing method.

Это не связано с какой-либо разностью часов, и Я знаю, что временные учетные данные работают с этой корзиной S3 для факта.

формат временных учетных данных выглядит следующим образом:

[default]
aws_access_key_id = A********
aws_secret_access_key = U*******
aws_session_token = F******

Конец разъяснений


Я пишу фрагмент кода, который должен обрабатывать запросы на получение от частного сегмента S3. У меня есть рабочий код, который использует заголовки как для постоянных, так и для временных учетных данных

Теперь моя цель - добиться того же, используя строку запроса, но я не могу заставить ее работать с временными учетными данными, она отлично работает с постоянный, так что я почти уверен, что проблема где-то передается в canonical_querystring, но, похоже, ничего из того, что я пробовал, не работает

Я не могу использовать boto3, потому что он должен быть независимым от любых внешних пакет (пакет запросов используется только для отладки, он не будет частью окончательного кода), если кто-нибудь может сказать мне, что я делаю неправильно, он был бы очень признателен

Ее попытка использовать строку запроса

import datetime
import hashlib
import hmac
import re

try:
    import httplib
except ImportError:
    import http.client as httplib
import requests
import urllib


def get_region(url, host):
    conn = httplib.HTTPConnection(url)
    headers = {'Host': host}
    conn.request('HEAD', '/', headers=headers)
    res = conn.getresponse()
    status = res.status
    if 400 <= status:
        return None
    return res.getheader('x-amz-bucket-region')


def _sign(key, msg):
    return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()


def _get_signature_key(key, date_stamp, region_name, service_name):
    k_date = _sign(('AWS4' + key).encode('utf-8'), date_stamp)
    k_region = _sign(k_date, region_name)
    k_service = _sign(k_region, service_name)
    k_signing = _sign(k_service, 'aws4_request')
    return k_signing


def get_sign_headers(access_key, secret_key, url, session_token=None, method='GET', request_parameters=''):
    regex = r"^(https://)(([a-zA-z0-9\-]+)\.)((\w+.*)\.amazonaws\.com)([^:^/]*)?(.*)$"
    matches = re.match(regex, url, re.MULTILINE)
    service = 's3'
    host = matches.group(2) + matches.group(4)
    canonical_uri = matches.group(7)
    region = get_region('s3.us-east-2.amazonaws.com', host)
    if matches.group(3) == 's3':
        if not region:
            region = matches.group(5)
        host = service + '.' + region + '.amazonaws.com'
    endpoint = 'https://' + host + matches.group(7)

    t = datetime.datetime.utcnow()
    amz_date = t.strftime('%Y%m%dT%H%M%SZ')  # Format date as YYYYMMDD'T'HHMMSS'Z'
    datestamp = t.strftime('%Y%m%d')  # Date w/o time, used in credential scope
    canonical_headers = 'host:' + host + '\n'
    signed_headers = 'host'
    if session_token:
        canonical_headers += 'x-amz-security-token:' + session_token + '\n'
        signed_headers += ';x-amz-security-token'
    algorithm = 'AWS4-HMAC-SHA256'
    credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
    canonical_querystring = 'X-Amz-Algorithm=AWS4-HMAC-SHA256'
    try:
        credential = urllib.quote_plus(access_key + '/' + credential_scope)
    except AttributeError:
        credential = urllib.parse.quote_plus(access_key + '/' + credential_scope)
    canonical_querystring += '&X-Amz-Credential=' + credential
    canonical_querystring += '&X-Amz-Date=' + amz_date
    canonical_querystring += '&X-Amz-Expires=30'
    canonical_querystring += '&X-Amz-SignedHeaders=' + signed_headers
    payload_hash = 'UNSIGNED-PAYLOAD'
    canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + \
                                 '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
    string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(
        canonical_request.encode('utf-8')).hexdigest()
    signing_key = _get_signature_key(secret_key, datestamp, region, service)
    signature = hmac.new(signing_key, string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest()
    if session_token:
        try:
            session_token = urllib.quote_plus(session_token)
        except AttributeError:
            session_token = urllib.parse.quote_plus(session_token)
        canonical_querystring += '&X-Amz-Security-Token={TOKEN}'.format(TOKEN=session_token)
    canonical_querystring += '&X-Amz-Signature=' + signature
    request_url = endpoint + "?" + canonical_querystring

    print('\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++')
    print('Request URL = ' + request_url)
    r = requests.get(request_url)

    print('\nRESPONSE++++++++++++++++++++++++++++++++++++')
    print('Response code: %d\n' % r.status_code)
    print(r.text)

и это код, который использует подход заголовков (который работает для обоих):

import datetime
import hashlib
import hmac
import re
import requests
try:
    import httplib
except ImportError:
    import http.client as httplib


def get_region(url, host):
    conn = httplib.HTTPConnection(url)
    headers = {'Host': host}
    conn.request('HEAD', '/', headers=headers)
    res = conn.getresponse()
    status = res.status
    if 400 <= status:
        return None
    return res.getheader('x-amz-bucket-region')


def _sign(key, msg):
    return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()


def _get_signature_key(key, date_stamp, region_name, service_name):
    k_date = _sign(('AWS4' + key).encode('utf-8'), date_stamp)
    k_region = _sign(k_date, region_name)
    k_service = _sign(k_region, service_name)
    k_signing = _sign(k_service, 'aws4_request')
    return k_signing


def get_sign_headers(access_key, secret_key, url, session_token=None, method='GET', request_parameters=''):
    regex = r"^(https://)(([a-zA-z0-9\-]+)\.)((\w+.*)\.amazonaws\.com)([^:^/]*)?(.*)$"
    matches = re.match(regex, url, re.MULTILINE)
    service = 's3'
    host = matches.group(2) + matches.group(4)
    canonical_uri = matches.group(7)
    region = get_region('s3.us-east-2.amazonaws.com', host)
    if matches.group(3) == 's3':
        if not region:
            region = matches.group(5)
        host = service + '.' + region + '.amazonaws.com'
    endpoint = 'https://' + host
    t = datetime.datetime.utcnow()
    amz_date = t.strftime('%Y%m%dT%H%M%SZ')
    datestamp = t.strftime('%Y%m%d')  # Date w/o time, used in credential scope

    canonical_querystring = request_parameters
    canonical_headers = 'host:' + host + '\n' + 'x-amz-content-sha256:UNSIGNED-PAYLOAD' + \
                        '\n' + 'x-amz-date:' + amz_date + '\n'
    signed_headers = 'host;x-amz-content-sha256;x-amz-date'
    if session_token:
        signed_headers += ';x-amz-security-token'
        canonical_headers += 'x-amz-security-token:' + session_token + '\n'
    payload_hash = 'UNSIGNED-PAYLOAD'
    canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + \
                                 '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash

    algorithm = 'AWS4-HMAC-SHA256'
    credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
    string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(
        canonical_request.encode('utf-8')).hexdigest()
    signing_key = _get_signature_key(secret_key, datestamp, region, service)
    signature = hmac.new(signing_key, string_to_sign.encode('utf-8'), hashlib.sha256).hexdigest()
    authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + \
                                       'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature

    headers = {'x-amz-date': amz_date, 'x-amz-content-sha256': 'UNSIGNED-PAYLOAD',
               'Authorization': authorization_header}
    if session_token:
        headers['x-amz-security-token'] = session_token
    request_url = endpoint + canonical_uri
​
    print('\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++')
    print('Request URL = ' + request_url)
    print('Headers = {}'.format(headers))
    r = requests.get(request_url, headers=headers)
​
    print('\nRESPONSE++++++++++++++++++++++++++++++++++++')
    print('Response code: %d\n' % r.status_code)


1 Ответ

0 голосов
/ 03 мая 2020

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...