一尘不染

如何使用 PHP 签署 AWS API 请求?

php

我正在尝试签署 AWS API 请求,然后使用 cURL。

目的是向服务提供商的 API 提交跟踪号,并使用响应。

我完全是 AWS API 的菜鸟,经过多次测试后找不到我的错。

我尝试了很多方法,但都导致{"message":"Forbidden"}.

这是我当前的脚本:

<?php

    $accessKeyId = "AKIA55D**********";   
    $secretAccessKey = "NQ0xcl**********";  
    $method ='GET';
    $uri = '/tracking/shipments';
    $secretKey = $secretAccessKey;
    $access_key = $accessKeyId;
    $region = 'af-south-1';
    $service = 'execute-api';
    $host = "https://api.shiplogic.com";
    $alg = 'sha256';
    $date = new DateTime('Africa/Johannesburg');
    $dd = $date->format( 'Ymd\THis\Z' );
    $amzdate2 = new DateTime( 'Africa/Johannesburg' );
    $amzdate2 = $amzdate2->format( 'Ymd' );
    $amzdate = $dd;
    $algorithm = 'AWS4-HMAC-SHA256';
    $canonical_uri = $uri;
    $canonical_querystring = '';
    $canonical_headers = "host:".$host."\n"."x-amz-date:".$amzdate."\n";
    $signed_headers = 'host;x-amz-date';
    $canonical_request = "".$method."\n".$canonical_uri."\n".$canonical_querystring."\n".$canonical_headers."\n".$signed_headers;
    $credential_scope = $amzdate2 . '/' . $region . '/' . $service . '/' . 'aws4_request';
    $string_to_sign  = "".$algorithm."\n".$amzdate ."\n".$credential_scope."\n".hash('sha256', $canonical_request)."";
   //string_to_sign is the answer..hash('sha256', $canonical_request)//

    $kSecret = 'AWS4' . $secretKey;
    $kDate = hash_hmac( $alg, $amzdate2, $kSecret, true );
    $kRegion = hash_hmac( $alg, $region, $kDate, true );
    $kService = hash_hmac( $alg, $service, $kRegion, true );
    $kSigning = hash_hmac( $alg, 'aws4_request', $kService, true );     
    $signature = hash_hmac( $alg, $string_to_sign, $kSigning ); 
    $authorization_header = $algorithm . ' ' . 'Credential=' . $access_key . '/' . $credential_scope . ', ' .  'SignedHeaders=' . $signed_headers . ', ' . 'Signature=' . $signature;

    $headers = [
                'content-type'=>'application/json', 
                'x-amz-date'=>$amzdate, 
                'Authorization'=>$authorization_header];

$curl = curl_init();

curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.shiplogic.com/tracking/shipments?tracking_reference=M3RPH',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => array(
'X-Amz-Date: '.$amzdate.'',
'Authorization: ' . $authorization_header . ''
 ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

我收到的回复是{"message":"Forbidden"}

我究竟做错了什么?


阅读 117

收藏
2022-07-25

共1个答案

一尘不染

如评论中已经指出的那样,安装亚马逊的 PHP SDK 。

composer require aws/aws-sdk-php

您尝试调用的 API 文档有1 个 PHP 代码示例,它使用该 SDK。您需要做的就是插入您的凭据。

use Aws\Credentials\Credentials;
use Aws\Signature\SignatureV4;
use Psr\Http\Message\RequestInterface;

function sign(
    RequestInterface $request,
    string $accessKeyId,
    string $secretAccessKey
): RequestInterface {
    $signature = new SignatureV4('execute-api', 'af-south-1');
    $credentials = new Credentials($accessKeyId, $secretAccessKey);

    return $signature->signRequest($request, $credentials);
}

您可以看到输入$request和返回值都是相同的RequestInterface类型。因此,它为您提供了一个具有适当签名的修改请求对象(如果您的密钥和参数有效)。

如果你正在使用curl,你可能还没有这个请求对象。Amazon 的 API 使用PSR-7 标准请求接口,大多数框架如果有请求对象就会实现这一点。

如果你手头没有请求对象,你可以使用Guzzle 的实现。所需的包已经安装为Amazon 的 SDK 的依赖项。您可以从 Github 永久链接安装两个突出显示的软件包。

 $request = new GuzzleHttp\Psr7\Request(
     'GET',
     'https://api.shiplogic.com/tracking/shipments',
     [
         // Maybe you still need headers.
     ]
 );
 $signed_request = sign($request);

 $client = new \GuzzleHttp\Client();
 $response = $client->send($signed_request);

正如评论中提到的,应尽可能避免创建自己的安全组件实现。重写代码以使用此请求对象要容易得多,尤其是对于将来的维护。

另请记住,您的凭据可能只是无效,或者无法访问您请求的资源。乍一看,您的大部分实现似乎都遵循了签名协议的预期(尽管您只需要一个小错误并且它不会起作用),所以如果您确定您检查了所有内容,它可能就这么简单。

2022-07-25