Azure Blob Storage:块Blob上传MAC签名不匹配问题排查
使用REST API向Azure Blob Storage上传块Blob时,按文档构造待签名字符串后持续收到403错误:
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
现有核心代码如下:
待签名字符串构造
$str2sign = "PUT $blockSize $blockMimeType x-ms-blob-type:BlockBlob x-ms-date:$Date x-ms-version:2019-12-12 $headerResource $urlResource";
其中$headerResource为:
x-ms-blob-type:BlockBlob x-ms-date:$Date x-ms-version:2019-12-12
$urlResource为:
/$storageAccountname/$containerName/$blobName blockid:" . urlencode($blockId) . " comp:block
签名生成
$sig = base64_encode(hash_hmac('sha256', urldecode(utf8_encode($str2sign)), base64_decode($accesskey), true));
授权头与请求头
$authHeader = "SharedKey $storageAccountname:$sig"; $headers = [ 'Authorization: ' . $authHeader, 'x-ms-date: ' . $Date, 'x-ms-version: 2019-12-12', 'Content-Length: ' . strlen($blockList), 'Content-Type: application/xml', ];
CURL请求
curl_setopt($ch, CURLOPT_URL, $URL); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_POSTFIELDS, $blockList); $result = curl_exec($ch);
注:其他使用相同认证方式的Blob上传/展示功能可正常运行,例如下载功能的签名构造片段:
// Azure Blob Storage download $Date = gmdate('D, d M Y H:i:s \G\M\T'); $headerResource = "x-ms-date:$Date x-ms-version:2019-12-12"; $urlResource = "/$storageAccountname/$containerName/$blobName"; $arraysign = array(); $arraysign[] = 'GET'; /*HTTP Verb*/ $arraysign[] = ''; /*Content-Encoding*/ $arraysign[] = ''; /*Content-Language*/ $arraysign[] = ''; /*Content-Length (include value when zero)*/ $arraysign[] = ''; /*Content-MD5*/ $arraysign[] = ''; /*Content-Type*/ $arraysign[] = ''; /*Date*/ $arraysign[] = ''; /*If-Modified-Since */ $arraysign[] = ''; /*If-Match*/ $arraysign[] = ''; /*If-None-Match*/
移除待签名字符串中的重复头部字段
当前$str2sign中已手动写入x-ms-blob-type、x-ms-date、x-ms-version,又追加了$headerResource,导致这些字段被重复计入签名,直接引发验证失败。修正后的$str2sign应只保留一次Canonicalized Headers:$str2sign = "PUT $blockSize $blockMimeType x-ms-blob-type:BlockBlob x-ms-date:$Date x-ms-version:2019-12-12 $urlResource";对齐HTTP请求方法与签名中的方法
签名构造用的是PUT,但CURL默认使用POST(因设置了CURLOPT_POSTFIELDS),需显式指定PUT方法:curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');修正CanonicalizedResource的格式
查询参数需合并到同一行,用&分隔,而非换行。正确的$urlResource构造:$urlResource = "/$storageAccountname/$containerName/$blobName?comp=block&blockid=" . urlencode($blockId);简化签名生成的编码操作
urldecode(utf8_encode($str2sign))属于冗余操作,待签名字符串无需URL解码,直接使用原始字符串即可:$sig = base64_encode(hash_hmac('sha256', $str2sign, base64_decode($accesskey), true));确保Content-Length的准确性
若$blockList包含多字节字符,strlen()会计算字符数而非字节数,导致长度不匹配。改用mb_strlen()计算字节数:'Content-Length: ' . mb_strlen($blockList, '8bit'),
内容的提问来源于stack exchange,提问作者LucaSpeedStack

