背景

JS クライアントによる直接署名は、OSS の AccessId / AcessKey がフロントエンドに公開され、他のユーザーからアクセスされる可能性があるため、深刻かつ潜在的なセキュリティ上のリスクがあります。 このドキュメントでは、署名を取得する方法、およびバックエンドの PHP コードにポリシーをアップロードする方法を説明します。

バックエンドに署名をアップロードするためのロジックは以下の通りです。

  1. イメージをアップロードする前に、クライアントはアップロードされたポリシーと署名をアプリケーションサーバーから取得します。
  2. クライアントは取得した署名を OSS に直接アップロードします。

バックエンドにアップロードされた署名のサンプル

  • サンプルのダウンロード:
    • PC ブラウザーからテストサンプルをダウンロードするには、こちらをクリックしてください。
    • アップロードが携帯電話で有効かどうかをテストできます。 携帯電話のアプリ (WeChat、QQ、携帯ブラウザーなど) を使用して、QR コードをスキャンします。

      これは広告ではなく、上記 URL の QR コードです。 この操作により、携帯電話でサービスが意図したとおりに機能するかどうかを確認できます。

  • ダウンロードコード:

    コードをダウンロードするにはこちらをクリックしてください。

    この例ではバックエンド署名を採用し、PHP 言語を使用しています。

    • Java 言語を使用したバックエンド署名のサンプルについては、こちらをクリックしてください。
    • Go 言語を使用したバックエンド署名のサンプルについては、こちらをクリックしてください。

    1. 対応する言語のサンプルをダウンロードします。
    2. サンプルコードを修正します。 たとえば、リスニングポートを設定してから、実行を開始します。
    3. oss-h5-upload-js-php.zipupload.js で、変数 serverUrl をステップ 2 で設定したアドレスに変更します。 たとえば、次のようになります。 serverUrl=http://1.2.3.4:8080 または serverUrl=http://abc.com/post/

サーバー側で Post 署名を構築する際の原則

アップロードには OSS PostObject メソッドが使用されます。 Plupload を使用してブラウザーで PostObject リクエストを作成し、そのリクエストを OSS に送信します。 署名は PHP のサーバーに実装されています。 同じ原則で、サーバーは Java、.NET、Ruby、Go、または Python 言語でコンパイルできます。 コアロジックは Post 署名を作成することです。 Java と PHP の例がこちらに提供されています。 以下のステップが必要です。

  1. Web ページはサーバー側から JavaScript を介して署名を要求します。
  2. JavaScript が署名を取得すると、Plupload を介して OSS に署名をアップロードします。
  • 実装
    1. 自分の ID、キー、およびバケットをフィールドに入力します。
      php/get.php を修正します。
      • 変数 $id を AccessKeyId に設定します。
      • $key を AccessKeySecret に設定します。
      • $host を bucket+endpoint に設定します。
        エンドポイントについては、「OSSの概要」をご参照ください。
        $id= 'xxxxxx';
          $key= 'xxxxx';
          $host = 'http://post-test.oss-cn-hangzhou.aliyuncs.com
    2. ブラウザーの安全性を保証するため、バケットに CORS を設定する必要があります。
      バケット属性の CORS 設定が POST メソッドをサポートしていることを確認します。 これは、HTML が直接 OSS にデータをアップロードし、その過程でクロスオリジン要求を生成するためです。 したがって、バケット属性ではクロスオリジナルリクエストを許可する必要があります。

      手順については、「Cross-Origin Resource Sharing (CORS) の 設定」をご参照ください。 設定は次のとおりです。

      旧バージョンの IE ブラウザでは、Plupload は flash で実行されます。 crossdomain.xml を設定する必要があります。

コアロジックの詳細

  • ランダムなオブジェクト名の設定

    クライアント上のオブジェクトと同じサフィックスが付いている場合は、アップロードされたオブジェクトにランダムに名前を付ける必要があります。 この例では、2 つのラジオボタンが区別に使用されています。 アップロードされたオブジェクトにランダムな名前を適用するように設定を修正したい場合は、機能を次のように変更します。

    function check_object_radio() {
        g_object_name_type = 'random_name';
    }

    ユーザーのオブジェクトへのアップロードを設定したい場合は、機能を次のように変更します。

    function check_object_radio() {
        g_object_name_type = 'local_name';
    }
  • アップロードディレクトリの設定

    アップロードディレクトリはサーバー側で指定されているため (PHP では) 、セキュリティが強化されています。 各クライアントは、オブジェクトを指定のディレクトリにアップロードすることだけが許可されています。 これはオブジェクトを他と分離することにより、セキュリティを保証しています。 次のコードは、アップロードディレクトリのアドレスを abc/ に変更します (アドレスは / で終わる必要があります)。

    $dir = 'abc/';
  • アップロードしたオブジェクトのフィルタリング条件を設定

    アップロードのため、フィルタリング条件を設定する必要があることがあります。 イメージのアップロードのみを許可し、アップロードされるオブジェクトのサイズを設定し、繰り返しアップロードを許可しないなどです。 この設定には filters パラメーターを使用します。

    var uploader = new plupload.Uploader({
        ……
        filters: {
            mime_types : [ //Only images and zip objects are allowed to be uploaded
            { title : "Image files", extensions : "jpg,gif,png,bmp" },
    
            ], 
            max_file_size : '400kb', //Only objects with a maximum size of 400 KB are allowed to be uploaded
            prevent_duplicates : true //Repeated objects are not allowed to be selected
        },

    フィルタリング条件を設定するには、Plupload 属性フィルターを使用します。

    上記設定値の説明:

    • mime_types: アップロードされるオブジェクトの拡張子を制限します。

    • アップロードされるオブジェクトのサイズを制限します。

    • 繰り返しアップロードを制限します。

      フィルター条件は必須ではありません。 不要な場合は、フィルタリング条件をコメントアウトします。
  • アップロードされたオブジェクト名の取得

    アップロードされたオブジェクトの名前を知りたい場合、次のように、Plupload を使用して FileUploaded イベントを呼び出すことができます。

    FileUploaded: function(up, file, info) {
                if (info.status == 200)
                {
                    document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = 'upload to oss success, object name:' + get_uploaded_object_name(file.name);
                }
                else
                {
                    document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = info.response;
                }
        }

    以下の関数を使用して、OSS にアップロードされたオブジェクトの名前を取得することができます。 file.name プロパティは、アップロードされたローカルオブジェクトの名前を記録します。

    get_uploaded_object_name(file.name)
  • 署名のアップロード

    JavaScript はバックエンドから policyBase64、accessid、および signature 変数を取得できます。 以下は、3 つの変数を取得するためのコアコードです。

    phpUrl = './php/get.php'
            xmlhttp.open( "GET", phpUrl, false );
            xmlhttp.send( null );
            var obj = eval ("(" + xmlhttp.responseText+ ")");
            host = obj['host']
            policyBase64 = obj['policy']
            accessid = obj['accessid']
            signature = obj['signature']
            expire = parseInt(obj['expire'])
            key = obj['dir']

    xmlhttp.responseText を解析します (以下はあくまで例として提供しています。実際の形式は異なる場合があります。また、signature、accessid、および policy の値が存在している必要があります)。

    {"accessid":"6MKOqxGiGU4AUk44",
    "host":"http://post-test.oss-cn-hangzhou.aliyuncs.com",
    "policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDoyMzoyM1oiLCJjxb25kaXRpb25zIjpbWyJjcb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8iXV19",
    "signature":"I2u57FWjTKqX/AE6doIdyff151E=",
    "expire":1446726203,"dir":"user-dir/"}
    • accessid: ユーザー要求の Accessid です。 なお、Accessid を公開してもデータセキュリティに影響はありません。
    • host: ユーザーがアップロード要求を送信するドメイン名です。
    • policy: ユーザーフォームをアップロードするためのポリシーです。 これは Base64 でエンコードされた文字列です。
    • signature: ポリシー変数の署名文字列です。
    • expire: 現在のアップロードポリシーの有効期限です。 この変数はすでにポリシーに示されているため、OSS には送信されません。

    ポリシーを解析します。 デコードされたポリシーの内容は次のとおりです。

    {"expiration":"2015-11-05T20:23:23Z",
    "conditions":[["content-length-range",0,1048576000],
    ["starts-with","$key","user-dir/"]]

    ポリシーについての詳細は、「ポリシーの基本要素」をご参照ください。

    PolicyText の主な内容は、このポリシーの最終の有効期限を指定しています。 有効期限が切れまでの間、このポリシーを使用してオブジェクトをアップロードすることができます。 そのため、アップロードごとにバックエンドから署名を取得する必要はありません。

    ここでは、以下の設定を使用します。

    • 初めてアップロードをする場合、各オブジェクトのアップロードの署名が取得されます。
    • それ以降のアップロードでは、現在の時刻と署名の時刻が比較され、署名の有効期限が切れていないかどうかが確認されます。
      • 署名が期限切れになると、新しい署名が取得されます。
      • 署名の有効期限が切れていない場合は、同じ署名が使用されます。 ここでは有効期限が切れた変数が使用されています。

    コアコードは次のとおりです。

    now = timestamp = Date.parse(new Date()) / 1000;
    [color=#000000]//This determines whether the time specified by the expire variable is earlier than the current time. If so, a new signature is obtained. 3s is the buffer duration.[/color]
        if (expire < now + 3)
    {  
         ..... 
       phpUrl = './php/get.php'
       xmlhttp.open( "GET", phpUrl, false );
       xmlhttp.send( null );
         ......
    }
    return .

    ポリシーコンテンツに starts-with が追加されたことが確認できます。 これは、アップロードされるオブジェクトの名前が user-dir で始まっている必要があることを示しています (このストリングはカスタマイズ可能です)。

    多くのシナリオでは、1 つのアプリに 1 つのバケットが使用され、さまざまなユーザーのデータが含まれるため、この設定が追加されています。 データが上書きされないようにするため、指定のユーザーによって OSS にアップロードされたオブジェクトには、指定のプレフィックスが追加されます。

    ただし、問題が発生します。 あるユーザーがこのポリシーを取得すると、有効期限が切れる前にアップロードプレフィックスを変更してオブジェクトを別のユーザーのディレクトリにアップロードできてしまいます。 この問題を解決するには、アップロード時に指定のユーザーがアップロードしたオブジェクトのプレフィックスを指定するように、アプリケーションサーバーを設定します。 この場合、ポリシーを取得した後でも、他のユーザーのプレフィックスを持つオブジェクトをアップロードすることはできません。 これによりデータセキュリティが保証されます。

まとめ

このドキュメントで使用されているサンプルでは、Web ページ側からのアップロード中にサーバー側が終了し、その後、サーバー側で圧縮されることなく、オブジェクトが直接アップロードされます。 このアプローチは安全で信頼性があります。

ただし、このサンプルでは、バックエンドプログラムはアップロードされたオブジェクトの数や ID をすぐには認識しません。 アップロードコールバックを使用し、どのオブジェクトがアップロードされたのかを確認できます。 このサンプルはマルチパートとブレークポイントを実装できません。

関連ドキュメント