1

S3のカスタムドメインでホストされている多数のビデオ/audo/ mediaがあり、URLに署名して、ストリーミングとダウンロードの両方を可能にするための一連の関数を作成しました。問題は、もちろん署名されたURLが機能しないことです。エラーは次のとおりです。

計算したリクエストの署名が、提供した署名と一致しません。キーと署名方法を確認してください。

もちろん、このページから返されたバイトコードを取得してAmazon S3署名テスターに​​入力し、そこからバイトコードを取得すると、問題なく機能します。関数から署名する文字列と、Signature Testerでデコードされたバイトコードが同一であっても、機能することはありません。

これは、PHPコードの小さなブロックを介して呼び出されます。

$headers = createS3parameters($expiry, $file_type);
$request = preg_replace("/^.*?:\/\/.*\//", "/", $bucketurl);
$signature = signRequest($request, $expiry, $s3secret, $headers, "GET", $file_type);
$signed_request = "$bucketurl?AWSAccessKeyId=$s3key&Expires=$expiry&$headers&Signature=$signature";

これは実際に署名する関数です。

function signRequest($request, $expiration, $s3secret, $headers = '', $type = 'GET', $content_type = 'default')
{
    if ($expiration == 0 || $expiration == null)
    {
        $expiration = time() + 315576000; // 10 years (never)
    }

    if (strcmp($content_type, 'default') == 0)
    {
        $content_type = "";
    }

    // S3 tester spits out this format
    /*$string = "$type\n".
              "\n\n".
              "$expiration\n".
              "/mybucket$request?$headers";*/

    $string = "$type\n".
              "\n".
              "$content_type\n".
              "$expiration\n".
              "$headers\n".
              "$request";


    // must be in UTF8 format
    $string = utf8_encode(trim($string));
    // encode to binary hash using sha1. require S3 bucket secret key
    $hash = hash_hmac("sha1",$string, $s3secret,false);
    // sha1 hash must be base64 encoded
    $hash = base64_encode($hash);
    // base64 encoded sha1 hash must be urlencoded
    $signature = rawurlencode($hash);

    return $signature;
}

次に、次のようなURLを作成します。

http://mybucket.s3.amazonaws.com/My_Boring_Video.wmv?AWSAccessKeyId=AKIAIEXAMPLE6GA3WYQ&Expires=1344160808&response-content-type=application/force-download&response-expires=1344160808&Signature=OTIxOTI0YjNjMTA1NjMyNmJjYTk0MGE2YWJkMmI5OWQ3MGM2ZGY0MQ%3D%3D

残念ながら、これは機能しません。私が正しく理解するにはあまりにも長い間見つめてきた明らかな問題がここにありますか?

4

1 に答える 1

2

更新:仕様は仕様ですが、実際の慣行と一致する場合にのみ役立ちます。

AmazonのS3仕様では、署名は次のように形成する必要があるとされています。

Signature = URL-Encode( Base64( HMAC-SHA1( YourSecretAccessKeyID, UTF-8-Encoding-Of( StringToSign ) ) ) );

StringToSign = HTTP-VERB + "\n" +
    Content-MD5 + "\n" +
    Content-Type + "\n" +
    Expires + "\n" +
    CanonicalizedAmzHeaders +
    CanonicalizedResource;    

ただし、実際に必要なリクエストは次のようになります。

 StringToSign = HTTP-VERB + "\n" +
     "\n" + 
     "\n" +
     Expires + "\n" +
     Bucket + CanonicalizedResource + "?" + CanonicalizedAmzHeaders;

不思議なことに、PHPではこれもできないようです。

$string = "$type\n".
          "\n".
          "\n".
          "$expiration\n".
          "/$bucket$request?$headers";

署名が変更されて拒否されるため、すべて1行にする必要があります。これが特定のバージョンのPHPのバグなのか、それとも一般的なPHPのバグ(または意図された機能??!)なのかを確認するまでには至っていません。mybucket.s3.amazonaws.comやmybucket.mydomain.comなどの利用可能なバニティURLを使用している場合でも、バケットの名前を含める必要があります。ドキュメントには、実行できることと実行できないことは指定されていません。S3ベースのバニティURLを使用しているため、ドメイン名を取得してバケットに変換すると仮定しました。

関数を次のように変更することになりました。

function signRequest($bucket, $request, $expiration, $s3secret, $headers = '', $type = 'GET', $content_type = 'default')
{
    if ($expiration == 0 || $expiration == null)
    {
        $expiration = time() + 315576000; // 10 years (never)
    }

    if (strcmp($content_type, 'default') == 0)
    {
        $content_type = "";
    }

    $headers = trim($headers, '&');

    // This is the spec:
    /*$string = "$type\n".
        "\n".
        "$content_type\n".
        "$expiration\n".
        "$headers\n".
        "$bucket$request";*/
    // but it will only work as this
    $string = "$type\n\n\n$expiration\n/$bucket$request?$headers";

    // this could be a single line of code but left otherwise for readability
    // must be in UTF8 format
    $string = utf8_encode(trim($string));
    // encode to binary hash using sha1. require S3 bucket secret key
    $hash = hash_hmac("sha1",$string, $s3secret,true);
    // sha1 hash must be base64 encoded
    $hash = base64_encode($hash);
    // base64 encoded sha1 hash must be urlencoded
    $signature = urlencode($hash);

    return $signature;
}

うまくいけば、他の誰かがこれも役立つと思うでしょう。

更新(20180109):これを呼び出す関数を追加します(別名、これを非常に単純にしましょう)。

signRequest()関数に何を渡すかを理解するのに役立ちます。

    private function genS3QueryString($bucketurl)
    {
            $file_type = 'application/force-download';
            $expiry = '1831014000'; //Sun, Jan 09 2028 0700 UTC

            $headers = '&response-content-type='.$file_type.'&response-expires='.$expiry;

            $bucket = preg_replace("/^.*?:\/\/(.*)\.s3\.amazonaws\.com\/.*/", "$1", $bucketurl);
            $request = preg_replace("/^.*?:\/\/.*\//", "/", $bucketurl);
            $signature = $this->signRequest($bucket, $request, $expiry, S3_SECRET_KEY, $headers, 'GET', $file_type);

            $signed_request = '?AWSAccessKeyId='.S3_KEY.'&Expires='.$expiry.$headers.'&Signature='.$signature;

            return $signed_request;
    }

ただし、最初の関数は、AWSのGETリクエストにアタッチするために必要な暗号化された署名セクションのみを生成することに注意してください。ドキュメント(上記のリンクを参照)によると、すぐ上の2番目の関数から形成されるように、リクエストにはさらに多くのことが必要です。

有効期限は、必要に応じてローリング有効期限になる可能性がありますが、実際の有効期限よりもずっと前に資産が古くなり、交換されるのに十分な将来である必要があります。Webサイトの現在のバージョンよりも十分に長持ちするように、任意の時間が選択されました。

2番目の関数は、保護されたアセットのバケットURLのみを必要とします。目的の応答タイプを通話に追加するか、アセット(この場合は表示できない)ドキュメントを別のタイプとしてダウンロードするように変更することができます。

次に返されるsigned_requestは、保護されたS3アセットからリクエストするための作業URIのbucketurlに追加し直す必要があります。

于 2012-08-02T15:44:54.120 に答える