求助:PHP/cURL实现AWS S3对象DELETE请求(Signature V4签名错误)
解决AWS S3 DELETE请求签名不匹配问题
核心问题排查与修复
以下是导致The request signature we calculated does not match the signature you provided错误的关键原因及修复方案:
未定义变量引发Canonical URI错误
代码中定义了$object_key = '/filename.jpg',但构建Canonical Request时误用了未定义的$new_object_key,直接导致Canonical URI无效,这是签名不匹配的常见诱因。请求URL未指向目标对象
删除S3对象的DELETE请求必须将URL指向具体对象,而非仅桶地址。正确的URL格式应为https://examplebucket.s3.amazonaws.com/filename.jpg,而非仅https://examplebucket.s3.amazonaws.com。Canonical URI编码不规范
对象键需严格按照AWS SigV4规范进行URL编码:仅编码特殊字符,保留路径斜杠,且需单独编码路径中的每个分段(如folder/filename.jpg需分别编码folder和filename.jpg后再拼接)。可使用rawurlencode()实现规范编码。请求头顺序一致性问题
Canonical Headers必须按字母顺序排序,虽然原代码中数组顺序恰好符合要求,但显式使用ksort()排序能避免因数组顺序变化导致的签名错误。
修复后的完整代码
/* * AWS S3 SigV4认证实现 */ // 替换为你的实际配置参数 $access_key = '你的Access Key'; $secret_key = '你的Secret Key'; $region = 'us-east-1'; // 桶所在区域,需与实际一致 $bucket_host = 'examplebucket.s3.amazonaws.com'; $object_key = 'filename.jpg'; // 目标对象键,无需开头斜杠 $date_long = gmdate('Ymd\THis\Z'); $date_short = gmdate('Ymd'); $credential = $access_key . '/' . $date_short . '/' . $region . '/s3/aws4_request'; // 规范生成Canonical URI:编码对象键,处理路径分段 $encoded_key_parts = array_map('rawurlencode', explode('/', $object_key)); $canonical_uri = '/' . implode('/', $encoded_key_parts); // DELETE请求无payload,使用空字符串的SHA256哈希 $hashed_payload = strtolower(hash("sha256", "")); // 按字母顺序排序请求头,确保Canonical Headers顺序正确 $request_headers = array( 'host' => $bucket_host, 'x-amz-content-sha256' => $hashed_payload, 'x-amz-date' => $date_long, ); ksort($request_headers); $signed_headers_string = strtolower(implode(';', array_keys($request_headers))); // 构建Canonical Request $canonical_request = "DELETE\n"; $canonical_request .= $canonical_uri . "\n"; $canonical_request .= "\n"; // 无查询参数,留空行 foreach ($request_headers as $header => $value) { $canonical_request .= strtolower($header) . ':' . trim($value) . "\n"; } $canonical_request .= $signed_headers_string . "\n"; $canonical_request .= $hashed_payload; // 构建String to Sign $string_to_sign = "AWS4-HMAC-SHA256\n"; $string_to_sign .= $date_long . "\n"; $string_to_sign .= $date_short . '/' . $region. "/s3/aws4_request\n"; $string_to_sign .= hash('sha256', $canonical_request); // 生成签名密钥(二进制哈希) $sig_key = hash_hmac('sha256', $date_short, 'AWS4' . $secret_key, true); $sig_key = hash_hmac('sha256', $region, $sig_key, true); $sig_key = hash_hmac('sha256', 's3', $sig_key, true); $sig_key = hash_hmac('sha256', 'aws4_request', $sig_key, true); $signature = hash_hmac('sha256', $string_to_sign, $sig_key); // 构建cURL请求头 $curl_headers = array( "Authorization: AWS4-HMAC-SHA256 Credential={$credential},SignedHeaders={$signed_headers_string},Signature={$signature}", ); foreach($request_headers as $header => $value) { $curl_headers[] = "{$header}: {$value}"; } // 初始化cURL请求 $curl = curl_init(); $request_url = "https://{$bucket_host}{$canonical_uri}"; curl_setopt_array($curl, array( CURLOPT_URL => $request_url, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLINFO_HEADER_OUT => true, CURLOPT_HEADER => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'DELETE', CURLOPT_HTTPHEADER => $curl_headers )); $response = curl_exec($curl); curl_close($curl); // 调试用:输出响应内容 var_dump($response);
额外调试建议
- 开启S3服务器访问日志,对比AWS计算的Canonical Request与本地生成的内容,找出差异点
- 打印
$canonical_request和$string_to_sign,与AWS官方示例的格式、内容逐一核对 - 确认
$region与桶实际所在区域完全一致(如us-east-1不可简写为us-east) - 验证Access Key拥有目标对象的
s3:DeleteObject权限
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

