簡體   English   中英

使用 Perl、HTTP、LWP 了解我的授權標頭有什么問題

[英]Understanding what's wrong with my authorization header using Perl, HTTP, LWP

我有一段 PHP 代碼,用於創建對 Azure 存儲帳戶中的容器的 Blob 存儲 PUT 請求。 經過大量的修修補補,我終於得到了正確的標題,一切都很好。 不幸的是,我想在其中使用它的應用程序是用 Perl 編寫的。 所以我認為移植它是一項相對容易的任務。 事實證明,這比我預期的要困難得多。

我已經比較了 PHP 代碼和 Perl 代碼之間的所有內容(好吧,顯然不是所有內容,否則它會很糟糕),但繼續收到與標頭相關的身份驗證錯誤。

PHP 腳本使用 Curl 發出用戶代理請求。 在我的 Perl 安裝中,我沒有它作為直接替代品的可訪問性。 如果沒有本地安裝和 Net::Curl 的 C 編譯器,我不確定我能做多少。 (也許我在那里遺漏了什么?)因為兩個版本(PHP 和 Perl)之間的所有內容似乎都匹配,即消息、密鑰、字符串的編碼/解碼版本、散列簽名(我硬編碼了兩個實現之間的驗證日期) ,我不知道還有什么可以嘗試的。 這是第 3 天,我覺得我可能正在解決這個小組已經解決的問題。

工作正常的 PHP 代碼:

<?php

date_default_timezone_set ( 'GMT' );
$date = date ( "D, d M Y H:i:s T" );

$version = "2009-09-19";

$account_name = 'emiliolizardo';
$account_key = "uXwt+WJ14kkV6zDALOuiDCsJtqrGDMK7W5xtNhuXXUcsfP1HIC1s7IJ+PZS7dgyXPBufad46ncBSQQK5rNs6Qw==";
$container_name = 'containertest';

$blobname = "foobar.txt";
$fdata    = file_get_contents('testfile.txt');

$utfStr = "PUT"
        . "\n\n\n"
        . strlen($fdata)
        . "\n\n"
        . "text/plain; charset=UTF-8"
        . "\n\n\n\n\n\n\n"
        . "x-ms-blob-type:BlockBlob"
        . "\n"
        . "x-ms-date:$date"
        . "\n"
        . "x-ms-version:$version"
        . "\n"
        . "/$account_name/$container_name/$blobname";

$utf8_encode_str = utf8_encode ( $utfStr );

echo "utfStr : " . $utfStr . "\n";
echo "utf8_encode_str:" . $utf8_encode_str . "\n";

$signature_str = base64_encode(hash_hmac('sha256',    $utf8_encode_str, base64_decode($account_key), true));

echo "signature_str:" . $signature_str . "\n";

  $header = array (
   "x-ms-blob-type: BlockBlob",
   "x-ms-date: " . $date,
   "x-ms-version: " . $version,
   "Authorization: SharedKey " . $account_name . ":" . $signature_str,
   "Content-Type: text/plain; charset=UTF-8",
   "Content-Length: " . strlen($fdata),
   );

print_r($header);

$url="https://$account_name.blob.core.windows.net/$container_name/$blobname";
echo "url:" . $url . "\n";

# Check our variables
#echo "account_name: " . $account_name . "\n";
#echo "account_key : " . $account_key . "\n";
#echo "signature   : " . $signature_str . "\n";
#echo "url         : " . $url . "\n";
#var_dump($header);

# Execute curl commands to create container
$ch = curl_init ();

curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt ($ch, CURLOPT_CUSTOMREQUEST, 'PUT' );
curl_setopt ($ch, CURLOPT_URL, $url );
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt ($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt ($ch, CURLOPT_POSTFIELDS, $fdata);
curl_setopt ($ch, CURLOPT_HEADER, True );

$result = curl_exec ( $ch );

和 Perl 代碼,它很接近,但缺少一些東西:

#!/usr/bin/perl

use strict;
use DateTime;
use DateTime::TimeZone;
use Data::Dumper;
use Encode qw(decode encode);
use MIME::Base64 qw( encode_base64 decode_base64 );
use Digest::SHA qw(hmac_sha256 hmac_sha256_base64);
use HTTP::Request;
use LWP::UserAgent;

my $account_name = "emiliolizardo";
my $account_key  = "uXwt+WJ14kkV6zDALOuiDCsJtqrGDMK7W5xtNhuXXUcsfP1HIC1s7IJ+PZS7dgyXPBufad46ncBSQQK5rNs6Qw==";
my $container    = 'containertest';

#my $file = 'YhJCUjrcEi0q.mp3';
my $file = 'testfile.txt';

# -----------------------------------------------------------
# --
# -----------------------------------------------------------
sub uploadblob {
        my ($fname, $accname, $acckey, $cont) = @_;

        my $date = `/bin/date -u +"%a, %d %b %Y %T GMT"`; chomp $date;
#       my $date = 'Mon, 01 Jul 2019 13:14:43 GMT';     # -- JUST FOR TESTING

#       my $version = "2018-03-28";
        my $version = "2009-09-19";                     # -- JUST FOR TESTING TO MIMIC PHP CODE

        my ($blobname, $ctype);
        for ($fname) {
                /\.mp3$/i and do { $ctype = 'audio/mpeg'; last; };
                /\.wav$/i and do { $ctype = 'audio/wav'; last; };
                /\.txt$/i and do { $ctype = 'text/plain'; last; };
                die "Failed to match an acceptable extension";
        }
        my $blobname = $fname;

        open FILE, "< $fname" or die "Can't open file $fname for read: $!";
        my $fdata = <FILE>;
        close FILE;

        my $fsize = -s $fname;

        my $str = qq{PUT\n\n\n$fsize\n\n$ctype; charset=UTF-8\n\n\n\n\n\n\nx-ms-blob-type:BlockBlob\nx-ms-date:$date\nx-ms-version:$version\n/$accname/$cont/$blobname};
print "utfStr : $str\n";

        my $message = encode("UTF-8", $str);
print "utf8_encode_str:$message\n";

        my $secret  = decode_base64($acckey);

        my $signature_str = encode_base64( hmac_sha256($message, $secret) );
        chomp $signature_str;
print "signature_str:$signature_str\n";

#       while(length($digest) %4) { $digest .= '='; }   # -- Is this necessary for the hmac_sha256 digest?

        my $header = [
                'x-ms-blob-type' => "BlockBlob",
                'x-ms-date' => $date,
                'x-ms-version' => $version,
                'Authorization' => "SharedKey $accname:$signature_str",
                'Content-Type' => "$ctype; charset=UTF-8",
                'Content-Length' => $fsize
        ];
        my $url = "https://$accname.blob.core.windows.net/$cont/$blobname";
print "url:$url\n";

        sendPut($header,$url,$fdata);
}


# -----------------------------------------------------------
# --
# -----------------------------------------------------------
sub sendPut {
        my ($header,$url,$data) = @_;
print "\n\nIn sendPut()\n\n\n==============================================\n\n\n";

        my $r = HTTP::Request->new('POST', $url, $header, $data);

        my $ua = LWP::UserAgent->new();
        my $res = $ua->request($r);

        print "res: ", Dumper $res, "\n";
}

uploadblob($file, $account_name, $account_key, $container);

錯誤消息提示了我可能是什么問題,但我不確定如何糾正它:內容長度標頭錯誤,已修復。 這似乎是 LWP 存在的問題(或者是在 2006 年,這是我找到的參考資料)。

在我用 LWP 發送它之前,使用 Data::Dumper 查看 HTTP::Request 對象,對我來說看起來沒問題。 就像 PHP 請求對象一樣。 在某些時候,我會用 PHP 或 Node.js 或最新的東西重寫老式 Perl 代碼,但目前,我真的很想在 Perl 中使用它。

在此先感謝您的任何建議。 如果我違反了任何 SO 禮節,我深表歉意 - 在這里仍然很新。

謝謝 - 安迪

以下是來自 UserAgent->request 的完整響應:

Content-Length header value was wrong, fixed at /usr/share/perl5/vendor_perl/LWP/Protocol/http.pm line 189.
res: $VAR1 = bless( {
                 '_protocol' => 'HTTP/1.1',
                 '_content' => '<?xml version="1.0" encoding="utf-8"?><Error><Code>AuthenticationFailed</Code><Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:62589eac-301e-00bd-3e1e-30c15e000000
Time:2019-07-01T15:04:08.0485043Z</Message><AuthenticationErrorDetail>The MAC signature found in the HTTP request \'PUUgk2meSoiB9o+inlYomIq96Bf13IdAQoIZ4BSu4sE=\' is not the same as any computed signature. Server used following string to sign: \'POST


26

text/plain; charset=UTF-8






x-ms-blob-type:BlockBlob
x-ms-date:Mon, 01 Jul 2019 15:04:07 GMT
x-ms-version:2009-09-19
/emiliolizardo/containertest/testfile.txt\'.</AuthenticationErrorDetail></Error>',
                 '_rc' => '403',
                 '_headers' => bless( {
                                        'client-response-num' => 1,
                                        'date' => 'Mon, 01 Jul 2019 15:04:07 GMT',
                                        'client-ssl-cert-issuer' => '/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/OU=Microsoft IT/CN=Microsoft IT TLS CA 4',
                                        'client-ssl-cipher' => 'ECDHE-RSA-AES256-GCM-SHA384',
                                        'client-peer' => '52.239.177.68:443',
                                        'content-length' => '723',
                                        'client-date' => 'Mon, 01 Jul 2019 15:04:08 GMT',
                                        'client-ssl-warning' => 'Peer certificate not verified',
                                        'content-type' => 'application/xml',
                                        'x-ms-request-id' => '62589eac-301e-00bd-3e1e-30c15e000000',
                                        'client-ssl-cert-subject' => '/CN=*.blob.core.windows.net',
                                        'server' => 'Microsoft-HTTPAPI/2.0',
                                        'client-ssl-socket-class' => 'IO::Socket::SSL'
                                      }, 'HTTP::Headers' ),
                 '_msg' => 'Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.',
                 '_request' => bless( {
                                        '_content' => 'Test file for blob upload
',
                                        '_uri' => bless( do{\(my $o = 'https://emiliolizardo.blob.core.windows.net/containertest/testfile.txt')}, 'URI::https' ),
                                        '_headers' => bless( {
                                                               'user-agent' => 'libwww-perl/5.833',
                                                               'x-ms-date' => 'Mon, 01 Jul 2019 15:04:07 GMT',
                                                               'content-type' => 'text/plain; charset=UTF-8',
                                                               'x-ms-version' => '2009-09-19',
                                                               'x-ms-blob-type' => 'BlockBlob',
                                                               'content-length' => 28,
                                                               'authorization' => 'SharedKey emiliolizardo:PUUgk2meSoiB9o+inlYomIq96Bf13IdAQoIZ4BSu4sE='
                                                             }, 'HTTP::Headers' ),
                                        '_method' => 'POST',
                                        '_uri_canonical' => $VAR1->{'_request'}{'_uri'}
                                      }, 'HTTP::Request' )
               }, 'HTTP::Response' );
$VAR2 = '
';

在評論中解決。 @Grinnz 和 @Guarav Mantri - 你們做對了。

  1. 我的測試文件中有第二行(我沒有看到的點)。 所以從文件中讀取一行,但使用 -s 計算整個文件大小將提供不匹配。 現在正在正確計算內容長度。

  2. 當在簽名哈希中使用 PUT 時,我在 HTTP::Request-new() 調用中盲目地鍵入“POST”。 哎呀。

謝謝你們。 不知道如何對兩個回復進行投票,因為兩者都有部分答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM