doujing3896 2016-06-30 19:11
浏览 57

AWS SQS - 一致的“对资源X的访问被拒绝”

OK, I'm at my wit's end at this point, and really need another set of eyes.

I'm trying to create a really lightweight PHP endpoint to push data into an SQS queue for offline processing. I don't want to leverage the entire AWS SDK if I can help it.

IAM Side

  • I created a Group and assigned it AmazonSQSFullAccess Policy
  • I created a User and assigned it to the aforementioned Group
  • I downloaded the credentials. I've verified them about a dozen times at this point

SQS Side

  • Created an SQS queue
  • Allow access to everyone via the Permissions widget on the queue (seems redundant given the IAM but what do I know). Push a message in using the code below and it works - yay.
  • Restrict access to the single user defined in the IAM via the Permissions widget. Get "Access to the resource $sqs_endpoint denied", where $sqs_endpoint = my endpoint

OK, so I figure it has to be the signature. I download the AWSv4 Test Suite and throw it behind a debug switch. I get valid responses returned. BUT, when I turn debug off - back to the error message.

I've tried base64 encoding the payload in case it was a weird escaping issue, still nothing (plus I'm able to push the data in when I allow access to everyone).

At any rate, here's my code. It's got to be an IAM permission issue, no? I can't seem to square the circle:

<?php  
    define( 'DEBUG', false );
    define( 'AMZN_ALGO', 'AWS4-HMAC-SHA256' );
    define( 'HASH_ALGO', 'SHA256' );


    function getSignatureKey( $key, $date, $region, $service ) {
        $kDate = hash_hmac( HASH_ALGO, $date, 'AWS4' . $key, true );
        $kRegion = hash_hmac( HASH_ALGO, $region, $kDate, true );
        $kService = hash_hmac( HASH_ALGO, $service, $kRegion, true );
        $kSigning = hash_hmac( HASH_ALGO, 'aws4_request', $kService, true );
        return $kSigning;
    }

    // these values are the hashes provided in the test suite to validate your approach
    function compareTestSig( $type, $val ) {
        $testSigs = [
            'sts'=>'2e1cf7ed91881a30569e46552437e4156c823447bf1781b921b5d486c568dd1c',
            'creq'=>'9095672bbd1f56dfc5b65f3e153adc8731a4a654192329106275f4c7b24d0b6e',
            'authz'=>'1a72ec8f64bd914b0e42e42607c7fbce7fb2c7465f63e3092b3b0d39fa77a6fe'  
        ];

        if( $testSigs[$type] === $val ) {
            return true;
        }

        return false;
    }



    $headers = getallheaders();

    /**
      * Config
      * DEBUG = true are all the values from the AWS 4 Test Suite
      */    
    if( DEBUG === true ) {
        define( 'ACCESS_KEY', 'AKIDEXAMPLE' );
        define( 'SECRET_ACCESS_KEY', 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY' );
        define( 'ENDPOINT', 'https://example.amazonaws.com/' );

        $amznDate = '20150830T123600Z';
        $nrmlDate = '20150830';
        $service = 'service';
        $version = '2012-11-05';

        $payload = 'Param1=value1';                  
    } else {
        define( 'ACCESS_KEY', 'MY_ACCESS_KEY_I_KNOW_IS_CORRECT' );
        define( 'SECRET_ACCESS_KEY', 'MY_SECRET_ACCESS_KEY_I_CHECKED_100_TIMES' );
        define( 'ENDPOINT', 'https://sqs.us-east-1.amazonaws.com/[ENDPOINT_ID]/[QUEUE_NAME]' );

        $amznDate = gmdate( "Ymd\THis\Z" );
        $nrmlDate = gmdate( 'Ymd' );
        $service = 'sqs';
        $version = '2012-11-05';

        // create the payload
        $pBody = [];
        $pBody['Action'] = 'SendMessage';
        $pBody['MessageBody'] = json_encode( $headers );
        $pBody['Version'] = $version;
        $payload = http_build_query( $pBody);
    }

    $host = parse_url( ENDPOINT, PHP_URL_HOST );
    $path = parse_url( ENDPOINT, PHP_URL_PATH );  
    $region = 'us-east-1';
    $contentType = 'application/x-www-form-urlencoded; charset=utf8';
    $scope = sprintf( '%s/%s/%s/%s', $nrmlDate, $region, $service, 'aws4_request' );

    $payloadHash = hash( HASH_ALGO, $payload );

    if( DEBUG ) {
        if( compareTestSig( 'creq', $payloadHash ) ) {
            var_dump( 'CREQ sig PASS' );
        } else {
            var_dump( 'CREQ sig NO PASS' );
            exit;
        }
    }

    /**
      * Sign, seal, deliver.
      * First, create the header string
      */
    $aggregate = [
        'content-type' => $contentType,
        'host' => $host,
        'x-amz-date' => $amznDate
    ];

    ksort( $aggregate );
    $canonHeaders = [];
    foreach( $aggregate as $k=>$v ) {
        $canonHeaders[] = sprintf( "%s:%s", $k, $v );
    }
    $signedHeadersString = implode( ';', array_keys( $aggregate ) );

    /**
      * next, create the canonical request and hash it
      */
    $canon = sprintf( "%s
%s
%s
%s

%s
%s", 
        'POST', 
        $path,
        '',
        implode( "
", $canonHeaders ),
        $signedHeadersString,
        $payloadHash
    );

    $canonHash = hash( HASH_ALGO, $canon );
    if( DEBUG ) {
        if( compareTestSig( 'sts', $canonHash ) ) {
            var_dump( 'STS sig PASS' );
        } else {
            var_dump( 'STS sig NO PASS' );
            exit;
        }
    }

    $toSign = sprintf( "%s
%s
%s
%s", AMZN_ALGO, $amznDate, $scope, $canonHash );

    // signingKey will be bin, signature will be in hex
    $signingKey = getSignatureKey( SECRET_ACCESS_KEY, $nrmlDate, $region, $service );
    $signature = hash_hmac( HASH_ALGO, $toSign, $signingKey );

    if( DEBUG ) {
        if( compareTestSig( 'authz', $signature ) ) {
            var_dump( 'AUTHZ sig PASS' );
        } else {
            var_dump( 'AUTHZ sig NOPASS' );
            exit;
        }
    }

    /**
    * set up the post headers
    **/
    $postHeaders = [];
    $postHeaders['Content-Type'] = $contentType;    
    $postHeaders['Host'] = $host;
    $postHeaders['X-Amz-Date'] = $amznDate;
    $postHeaders['Authorization'] = sprintf( "%s Credential=%s, SignedHeaders=%s, Signature=%s", 
        AMZN_ALGO,
        ACCESS_KEY . '/' . $scope,
        $signedHeadersString,
        $signature
    );

    //do it
    $ch = curl_init();
    curl_setopt( $ch, CURLOPT_URL, ENDPOINT );
    curl_setopt( $ch, CURLOPT_POST, 1 );
    curl_setopt( $ch, CURLOPT_HTTPHEADER, $postHeaders );
    curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload );
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
    $output = curl_exec( $ch );
    curl_close( $ch );

    $xml = simplexml_load_string( $output );

    if( empty( $xml ) ) {
        var_dump( $output );
        die( 'UnknownOperationException' );
    }

    if(! empty( $xml->Error ) ) {
        printf( "ERROR %s : %s", $xml->Error->Code, $xml->Error->Message );
        exit;
    }


    echo 'OK=1;';
    exit;
?>
  • 写回答

0条回答 默认 最新

    报告相同问题?

    悬赏问题

    • ¥65 永磁型步进电机PID算法
    • ¥15 sqlite 附加(attach database)加密数据库时,返回26是什么原因呢?
    • ¥88 找成都本地经验丰富懂小程序开发的技术大咖
    • ¥15 如何处理复杂数据表格的除法运算
    • ¥15 如何用stc8h1k08的片子做485数据透传的功能?(关键词-串口)
    • ¥15 有兄弟姐妹会用word插图功能制作类似citespace的图片吗?
    • ¥200 uniapp长期运行卡死问题解决
    • ¥15 latex怎么处理论文引理引用参考文献
    • ¥15 请教:如何用postman调用本地虚拟机区块链接上的合约?
    • ¥15 为什么使用javacv转封装rtsp为rtmp时出现如下问题:[h264 @ 000000004faf7500]no frame?