You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PHP通过REST API上传Azure Blob Storage时认证失败求助

Azure Blob Storage PHP REST API 上传图片 AuthenticationFailed 错误排查与修正

问题描述

使用PHP通过REST API上传图片到Azure Blob Storage时,持续收到AuthenticationFailed错误,服务器提示HTTP请求中的MAC签名与计算出的签名不匹配,并给出了用于签名的字符串。

报错信息

AuthenticationFailedServer failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.

RequestId: <Request ID> Time:2023-04-13T20:31:58.4450928Z

The MAC signature found in the HTTP request '...' is not the same as any computed signature. Server used following string to sign:
'PUT

11746

image/png







x-ms-blob-cache-control:max-age=259200
x-ms-blob-type:BlockBlob
x-ms-date:Thu, 13 Apr 2023 20:31:58 GMT
x-ms-version:2017-04-17
/<storage account>/<container name>/<blob name>'.

原代码

function upload_blob(
    $file_to_upload,
    $blob_name // "image.png"
){
    $access_key = "<Access key>";
    $storage_account = "<Storage account>";
    $container_name = "<Container name>";
    $destination_url = "https://$storage_account.blob.core.windows.net/$container_name/$blob_name";
    $mime_type = mime_content_type($file_to_upload);
    $file_handle = fopen($file_to_upload, "r");
    $current_date = gmdate("D, d M Y H:i:s T", time());
    $file_length = filesize($file_to_upload);

    $header_resource = "x-ms-blob-cache-control:max-age=2592000
x-ms-blob-type:BlockBlob
x-ms-date:$current_date
x-ms-version:2017-04-17";
    $url_resource = "/$storage_account/$container_name/$blob_name";

    $str_to_sign = "PUT


$file_length

$mime_type






$header_resource
$url_resource";

    $signature = base64_encode(hash_hmac("sha256", urldecode(utf8_encode($str_to_sign)), base64_decode($access_key), true));
    $auth_header = "SharedKey $storage_account:$signature";

    $headers = [
        "Authorization: " . $auth_header,
        "x-ms-blob-cache-control: max-age=259200",
        "x-ms-blob-type: BlockBlob",
        "x-ms-date: " . $current_date,
        "x-ms-version: 2017-04-17",
        "Content-Type: $mime_type",
        "Content-Length: " . $file_length
    ];

    $curl_handle = curl_init($destination_url);
    curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, "PUT");
    curl_setopt($curl_handle, CURLOPT_INFILE, $file_handle);
    curl_setopt($curl_handle, CURLOPT_INFILESIZE, $file_length);
    curl_setopt($curl_handle, CURLOPT_UPLOAD, true);
    $result = curl_exec($curl_handle);

    echo "<pre>";
    print_r("Result:");
    print_r($result);
    print_r("============================================================");
    echo "</pre>";

    curl_close($curl_handle);
}

错误点分析

  1. 缓存控制值不一致:构造签名用的x-ms-blob-cache-control值为max-age=2592000,但实际请求头和服务器签名字符串中均为max-age=259200,直接导致签名计算偏差。
  2. 签名字符串空行数量不符:对比服务器给出的签名字符串,原代码在Content-Type(即$mime_type)之后的空行数量不足,Azure要求所有未使用的标准HTTP头字段必须留空行占位。
  3. 多余的字符串编码处理:对签名字符串执行urldecode(utf8_encode(...))是错误操作,Azure要求直接使用原始字符串计算签名,无需额外编码转换。

修正后的代码

function upload_blob(
    $file_to_upload,
    $blob_name // "image.png"
){
    $access_key = "<Access key>";
    $storage_account = "<Storage account>";
    $container_name = "<Container name>";
    $destination_url = "https://$storage_account.blob.core.windows.net/$container_name/$blob_name";
    $mime_type = mime_content_type($file_to_upload);
    $file_handle = fopen($file_to_upload, "r");
    $current_date = gmdate("D, d M Y H:i:s T", time());
    $file_length = filesize($file_to_upload);

    // 修正缓存控制值,与请求头保持一致
    $header_resource = "x-ms-blob-cache-control:max-age=259200
x-ms-blob-type:BlockBlob
x-ms-date:$current_date
x-ms-version:2017-04-17";
    $url_resource = "/$storage_account/$container_name/$blob_name";

    // 严格按照服务器给出的格式构造签名字符串,确保空行数量匹配
    $str_to_sign = "PUT

$file_length

$mime_type







$header_resource
$url_resource";

    // 移除多余的编码处理,直接使用原始字符串计算签名
    $signature = base64_encode(hash_hmac("sha256", $str_to_sign, base64_decode($access_key), true));
    $auth_header = "SharedKey $storage_account:$signature";

    $headers = [
        "Authorization: " . $auth_header,
        "x-ms-blob-cache-control: max-age=259200",
        "x-ms-blob-type: BlockBlob",
        "x-ms-date: " . $current_date,
        "x-ms-version: 2017-04-17",
        "Content-Type: $mime_type",
        "Content-Length: " . $file_length
    ];

    $curl_handle = curl_init($destination_url);
    curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, "PUT");
    curl_setopt($curl_handle, CURLOPT_INFILE, $file_handle);
    curl_setopt($curl_handle, CURLOPT_INFILESIZE, $file_length);
    curl_setopt($curl_handle, CURLOPT_UPLOAD, true);
    $result = curl_exec($curl_handle);

    echo "<pre>";
    print_r("Result:");
    print_r($result);
    print_r("============================================================");
    echo "</pre>";

    curl_close($curl_handle);
}

额外说明

  • 构造签名字符串时,必须严格遵循Azure SharedKey签名规则:所有未使用的标准HTTP头字段(如Content-Encoding、Content-MD5等)需留空行占位,顺序不可错乱。
  • 确保x-ms-date使用GMT时间,且签名计算与请求发送的时间一致,避免因时间差导致签名失效。
  • 生产环境建议开启CURLOPT_SSL_VERIFYPEER和CURLOPT_SSL_VERIFYHOST,关闭该选项会带来安全风险。

内容的提问来源于stack exchange,提问作者Pape

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 17:24:55