0

Web フォームから直接 Amazon S3 に非同期でファイルをアップロードしようとしています。これを行うには、サーバーにファイルをアップロードするクライアント リクエストを認証する必要があります。

AWS シークレット キーを使用してアップロード リクエストにデジタル署名することで、クライアントがファイルを S3 バケットにアップロードするために使用できる一時的な認証済み URL を作成できます。

Amazon S3 ドキュメントでは、署名は次の方法で生成する必要があると指定されています。

Signature = URL-Encode( Base64( HMAC-SHA1( YourSecretAccessKeyID, 
                                         UTF-8-Encoding-Of( StringToSign ) ) ) );

サーバーでHaskellを使用しているため、実装は次のようになります。

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString.Base64.Lazy as B64
import qualified Data.Digest.Pure.SHA as SHA
import qualified Data.ByteString.Lazy.Char8 as BL8

sign :: BL8.ByteString -> BL8.ByteString
sign = B64.encode . SHA.bytestringDigest . SHA.hmacSha1 secret
  where secret = "aws-secret-key"

Amazon ドキュメントの形式では、StringToSign が次のようになっている必要があります。

StringToSign = HTTP-VERB + "\n" +
    Content-MD5 + "\n" +
    Content-Type + "\n" +
    Expires + "\n" +
    CanonicalizedAmzHeaders +
    CanonicalizedResource; 

Amazon からの別の例:

GET\n
\n
\n
1175139620\n

/johnsmith/photos/puppy.jpg

したがって、私の文字列は次のようになります。

"PUT\n\n\n1384330538\n/バケット名/オブジェクト名"

上記の文字列に (sign 関数を使用して) 署名し、次のような URL を作成します。

https://s3.amazonaws.com/bucketname/objname?AWSAccessKeyId=accessskey&Signature=signature=&Expires=1384330979

これは、アップロードの前に AJAX リクエストを介してクライアントに送信されます。PUT リクエストを許可するために、バケットの CORS ポリシーも更新しました。

問題は、上記の署名付き URL で何かをアップロードしようとするたびに、このメッセージが (XML ドキュメントで) 表示されることです。

計算したリクエストの署名が、提供された署名と一致しません。キーと署名方法を確認してください。

だから私はどこで間違ったのか分かりません。注: パブリック URL ( https://s3.amazonaws.com/bucketname/objname )を使用すればアップロードできます(ただし、これはすべきではありません。ユーザーに BLOB のアップロードのみを許可し、読み取りや削除などは許可しないでください)。

4

2 に答える 2

1

このダンスを何度も行ってきた人として、このような HTTP ダイジェスト認証済み要求に正しく署名するソフトウェアを構築することは非常に困難です。特に、サーバーの応答のみに頼ってガイドすると、時間がかかります。セキュリティ上の理由から、サーバーはユーザーを拒否するときに意図的に暗号化されています。

私の最善のヒントは、(a) 動作することがわかっている代替の実装を取得し、(b) Haskell インターフェースを純粋に構築して、他のフレームワークからの要求を正確に複製するのが簡単になるようにし、(c) できることを確認することです。代替フレームワークと独自のコードの両方から、正確な要求テキストと正確な署名文字列の両方を取得します。特に、正確なタイムスタンプとノンスを推定し、パーセントエンコーディングに細心の注意を払う必要があることがよくあります。

これら 2 つのツールを使用すると、代替実装からさまざまな成功した要求を作成し、独自のフレームワークを使用して正確な署名文字列と正確な要求テキストを複製できるかどうかを確認できます。

ほとんどの場合、私自身のエラーには、不適切なエンコード、引用符の欠落、適切なパラメーター (または間違ったパラメーター) のすべてが含まれていない、または hmac 関数の不適切な使用が含まれていました。

于 2013-11-13T15:03:18.357 に答える
0

これが私のアップロード URL コードです。ディープから引き出したので、いくつかのインポートを見逃した可能性があります。

{-# LANGUAGE OverloadedStrings, FlexibleContexts, TypeFamilies, DeriveDataTypeable, TemplateHaskell, QuasiQuotes #-}

import qualified Aws
import qualified Aws.Core          as Aws
import qualified Aws.S3            as S3
import qualified Data.Text         as T
import qualified Codec.Binary.Base64         as B64
import qualified Data.ByteString             as BS
import Text.Shakespeare.Text(st)
import qualified Codec.Binary.Url            as Url
import System.Posix.Time(epochTime)
import Crypto.MAC.HMAC(hmac)
import Crypto.Hash.SHA1(hash)

data Cfg = Cfg { baseCfg :: Aws.Configuration
               , s3Cfg :: S3.S3Configuration Aws.NormalQuery
               , s3Bucket :: S3.Bucket
               }

uploadUrl :: Cfg -> T.Text -> T.Text -> IO T.Text
uploadUrl cfg mime filename = do
   time <- epochTime
   let expires = show $ time + 600
       msg = E.encodeUtf8 $ [st|PUT

#{mime}
#{expires}
x-amz-acl:public-read
/#{s3Bucket cfg}/#{filename}|] --the gap is necessary
       key = Aws.secretAccessKey $ Aws.credentials $ baseCfg cfg
       accessid = T.pack $ Url.encode $ BS.unpack $ Aws.accessKeyID $ Aws.credentials $ baseCfg cfg
       signature = encode . T.pack $ B64.encode $ BS.unpack $ hmac hash 64 key msg
       encode = T.pack . Url.encode .  BS.unpack . E.encodeUtf8
   return $ [st|http://#{s3Bucket cfg}.s3.amazonaws.com/#{filename}?AWSAccessKeyId=#{accessid}&Expires=#{expires}&Signature=#{signature}|]
于 2014-01-11T08:16:17.943 に答える