すべてのプロダクト
Search
ドキュメントセンター

Object Storage Service:PostObject リクエストでの V4 署名

最終更新日:Apr 18, 2025

PostObject リクエストの有効性とセキュリティを確保するために、PostObject リクエストに署名を含める必要があります。PostObject リクエストの V4 署名は、AccessKey シークレット、現在の時刻、およびリージョンに基づいて、ポリシーや有効期限を含む一連のリクエストパラメーターを暗号化することで計算されます。アプリケーションサーバーが署名を生成した後、アプリケーションサーバーは署名とアップロードポリシーを含む情報をクライアントに返します。クライアントはその情報を使用してアップロードリクエストを作成します。OSS はアップロードリクエストを受信した後、署名の有効性を検証します。有効な署名を含むリクエストのみが許可されます。署名検証に失敗したリクエストは拒否されます。

PostObject リクエストの署名

データセキュリティを強化するために、PostObject リクエストは V4 署名アルゴリズムを統合しています。フォーム要素とポリシーフォームフィールドは、PostObject リクエストの有効性とセキュリティを確保する上で重要な役割を果たします。

フォーム要素

フォームは、アップロードするオブジェクトとそのメタデータに関する情報を渡すために、PostObject リクエストで実際に運ばれるフィールドの集合です。次の表は、PostObject リクエストの V4 署名に固有のフォーム要素について説明しています。共通のフォーム要素の詳細については、「フォーム要素」をご参照ください。

要素

タイプ

必須

説明

x-oss-signature-version

文字列

はい

署名バージョンと署名の計算に使用されるアルゴリズム。値を OSS4-HMAC-SHA256 に設定します。

x-oss-credential

文字列

はい

署名の計算に使用できる資格情報。フォーマット:

<AccessKeyId>/<date>/<region>/oss/aliyun_v4_request
  • AccessKeyId:AccessKey ペアの AccessKey ID。

  • date:リクエストが開始された日付。フォーマット:YYYYMMDD。例:20231203。

  • region:アクセスするバケットが配置されている Alibaba Cloud リージョンの ID。例:cn-hangzhou。

  • oss:リクエストされたサービスの名前。値を oss に設定します。

  • aliyun_v4_request:リクエストの署名バージョンの説明。値を aliyun_v4_request に設定します。

x-oss-date

文字列

はい

リクエストが開始された時刻。時刻は ISO 8601 標準に従う必要があります。例:20231203T121212Z

  • リクエスト開始後最大 15 分のオフセットが許容されます。したがって、サーバーがリクエストを受信する時刻は、x-oss-date で指定された時刻より最大 15 分遅れる可能性があります。これにより、ネットワーク伝送の遅延やクライアントとサーバー間の時差が存在する場合でも、リクエストが想定どおりに処理されることが保証されます。

  • リクエストの有効期間は最大 7 日間です。x-oss-date で指定された時刻の 7 日後、OSS はリクエストを拒否し、エラーが報告されます。これにより、リクエストの適時性とセキュリティが確保され、期限切れまたは署名済みリクエストの悪意のある送信を防ぎます。

  • x-oss-date で指定された時刻は、署名対象文字列のタイムスタンプとして使用されます。値は、派生キーの date フィールドの値、およびポリシーフォームフィールドの x-oss-date フィールドの値と同じである必要があります。

x-oss-signature

文字列

はい

署名検証に使用される記述。値は、Base64 エンコードされたポリシー文字列を HMAC-SHA256 を使用して暗号化し、HMAC-SHA256 を使用して取得したバイナリハッシュを 16 進数形式に変換することで計算されます。

policy

PostObject リクエストの policy フォームフィールドは、HTML フォームを使用してオブジェクトをアップロードするために開始する PostObject リクエストの有効期限と条件を指定するために使用されます。policy フォームフィールドの値は、オブジェクトをアップロードするバケット名、オブジェクト名のプレフィックス、リクエストの有効期間、許可される HTTP メソッド、オブジェクトのサイズとコンテンツなど、複数のパラメーターを指定することで PostObject リクエストを制限する JSON 文字列です。

重要

policy フォームフィールドには、expiration パラメーターと conditions パラメーターを含める必要があります。Conditions パラメーターの次のフィールドには、x-oss-security-token パラメーターなどのオプションパラメーターが含まれています。これは、セキュリティトークンを使用して PostObject リクエストの署名を作成する場合にのみ必要です。AccessKey ペアを使用して PostObject リクエストの署名を作成する場合、x-oss-security-token パラメーターは不要です。

{
  "expiration": "2023-12-03T13:00:00.000Z",
  "conditions": [
    {"bucket": "examplebucket"},
    {"x-oss-signature-version": "OSS4-HMAC-SHA256"},
    {"x-oss-credential": "AKIDEXAMPLE/20231203/cn-hangzhou/oss/aliyun_v4_request"},
    {"x-oss-security-token": "CAIS******"},
    {"x-oss-date": "20231203T121212Z"},
    ["content-length-range", 1, 10],
    ["eq", "$success_action_status", "201"],
    ["starts-with", "$key", "user/eric/"],
    ["in", "$content-type", ["image/jpg", "image/png"]],
    ["not-in", "$cache-control", ["no-cache"]]
  ]
}

policy フォームフィールドの必須パラメーター

  • expiration

    expiration パラメーターは、リクエストの有効期限を指定します。時刻は ISO 8601 標準に従う必要があり、GMT である必要があります。たとえば、2023-12-03T13:00:00.000Z は、PostObject リクエストを 2023 年 12 月 3 日 13:00 より前に送信する必要があることを指定します。

  • conditions

    conditions パラメーターは、PostObject リクエストのフォームフィールドの有効な値を指定するリストです。

    パラメーター

    タイプ

    必須

    説明

    一致モード

    bucket

    文字列

    いいえ

    バケットの名前。

    bucket

    x-oss-signature-version

    文字列

    はい

    署名バージョンと署名の計算に使用されるアルゴリズム。値を OSS4-HMAC-SHA256 に設定します。

    x-oss-signature-version

    x-oss-credential

    文字列

    はい

    署名の計算に使用できる資格情報。フォーマット:

    <AccessKeyId>/<date>/<region>/oss/aliyun_v4_request
    • AccessKeyId:AccessKey ペアの AccessKey ID。

    • date:リクエストが開始された日付。

    • region:アクセスするバケットが配置されている Alibaba Cloud リージョンの ID。例:cn-hangzhou。

    • oss:リクエストされたサービスの名前。値を oss に設定します。

    • aliyun_v4_request:リクエストの署名バージョンの説明。値を aliyun_v4_request に設定します。

    x-oss-credential

    x-oss-security-token

    文字列

    いいえ

    このパラメーターは、セキュリティトークンを使用して PostObject リクエストの署名を作成する場合にのみ必要です。STS の AssumeRole 操作を呼び出すことで、セキュリティトークンを取得できます。

    x-oss-security-token

    x-oss-date

    文字列

    はい

    リクエストが開始された時刻。時刻は ISO 8601 標準に従う必要があります。例:20231203T121212Z

    • リクエスト開始後最大 15 分のオフセットが許容されます。したがって、サーバーがリクエストを受信する時刻は、x-oss-date で指定された時刻より最大 15 分遅れる可能性があります。これにより、ネットワーク伝送の遅延やクライアントとサーバー間の時差が存在する場合でも、リクエストが想定どおりに処理されることが保証されます。

    • リクエストの有効期間は最大 7 日間です。x-oss-date で指定された時刻の 7 日後、OSS はリクエストを拒否し、エラーが報告されます。これにより、リクエストの適時性とセキュリティが確保され、期限切れまたは署名済みリクエストの悪意のある送信を防ぎます。

    • x-oss-date で指定された時刻は、署名対象文字列のタイムスタンプとして使用されます。値は、派生キーの date フィールドの値、およびポリシーフォームフィールドの x-oss-date フィールドの値と同じである必要があります。

    x-oss-date

    content-length-range

    文字列

    いいえ

    アップロードするオブジェクトの許容される最小サイズと最大サイズ。単位:バイト。

    content-length-range

    success_action_status

    文字列

    いいえ

    オブジェクトのアップロード後に返される HTTP ステータスコード。

    eq、starts-with、in、および not-in

    key

    文字列

    いいえ

    アップロードするオブジェクトの名前。

    eq、starts-with、in、および not-in

    content-type

    文字列

    いいえ

    アップロードするオブジェクトのタイプ。

    eq、starts-with、in、および not-in

    cache-control

    文字列

    いいえ

    オブジェクトのキャッシュ動作。

    eq、starts-with、in、および not-in

条件の一致モード

一致モード

説明

content-length-range

アップロードするオブジェクトのサイズは、サポートされているオブジェクトサイズの範囲内である必要があります。たとえば、サポートされているオブジェクトサイズが 1 ~ 10 バイトの場合、条件は ["content-length-range", 1, 10] である必要があります。

eq

フォームフィールドの値は、conditions で指定された値と完全に同じである必要があります。たとえば、key フォームフィールドの値が a である必要がある場合、条件として ["eq", "$key", "a"] を指定できます。

starts-with

フォームフィールドの値は、特定のプレフィックスで始まる必要があります。たとえば、key フォームフィールドの値が user/user1 で始まる場合、条件として ["starts-with", "$key", "user/user1"] を指定できます。

in

検証に含める要素。要素は条件文字列で指定する必要があります。たとえば、アップロードするオブジェクトのタイプが image であるかどうかを確認し、PostObject リクエストで複数のフォーマットの画像をアップロードできるようにする場合、条件は ["in", "$content-type", ["image/jpg", "image/png"]] である必要があります。

not-in

検証から除外する要素。要素は条件文字列で指定する必要があります。たとえば、オブジェクトのキャッシュ動作を指定し、no-cache 要素を除外する場合、条件は ["not-in", "$cache-control", ["no-cache"]] である必要があります。

policy フォームフィールドのエスケープ文字

PostObject リクエストの policy フォームフィールドでは、ドル記号 ($) は変数を指定します。ドル記号 ($) を記述するには、次のエスケープ文字を使用する必要があります:\$. 次の表は、PostObject リクエストの policy フォームフィールドの JSON 文字列で使用されるエスケープ文字について説明しています。

エスケープ文字

説明

\/

スラッシュ

\\

バックスラッシュ

\"

二重引用符

\$

ドル記号

\b

スペース

\f

フォームフィード

\n

改行

\r

キャリッジリターン

\t

水平タブ

\uxxxx

Unicode 文字

署名計算プロセス

  1. UTF-8 エンコードされたポリシーを作成します。

  2. 署名対象文字列を作成します。

    ポリシーを Base64 エンコードして、署名対象文字列として安全に送信できる文字列を生成します。

  3. 署名キーを計算します。

    HMAC-SHA256 を使用して署名対象文字列を暗号化します。アカウントの派生キーを HMAC-SHA256 を使用するキーとして使用します。

  4. 署名を計算します。

    HMAC-SHA256 を使用して計算されたバイナリハッシュを 16 進数文字列に変換します。取得した 16 進数文字列は、リクエストの整合性と有効性を検証するために使用される署名です。

  1. 次のサンプルコードは、OSS SDK for Java を使用して、上記のポリシーを使用して PostObject リクエストの V4 署名を計算する方法の例を示しています。

    アクセスキー ( AK ) またはシークレットキー ( SK ) を使用する

    import com.aliyun.oss.common.utils.BinaryUtil;
    import org.apache.commons.codec.binary.Base64;
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    
    public class Demo {
        public static void main(String[] args) throws Exception {
            // サンプルコードを実行する前に、OSS_ACCESS_KEY_ID 環境変数と OSS_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
            String accesskeyid =  System.getenv().get("OSS_ACCESS_KEY_ID");
            String accesskeysecret =  System.getenv().get("OSS_ACCESS_KEY_SECRET");
    
            // 手順 1:ポリシーを作成する。
            ObjectMapper mapper = new ObjectMapper();
    
            Map<String, Object> policy = new HashMap<>();
            policy.put("expiration", "2024-12-03T13:00:00.000Z");
    
            List<Object> conditions = new ArrayList<>();
    
            Map<String, String> bucketCondition = new HashMap<>();
            bucketCondition.put("bucket", "examplebucket");
            conditions.add(bucketCondition);
    
            Map<String, String> signatureVersionCondition = new HashMap<>();
            signatureVersionCondition.put("x-oss-signature-version", "OSS4-HMAC-SHA256");
            conditions.add(signatureVersionCondition);
    
            Map<String, String> credentialCondition = new HashMap<>();
            credentialCondition.put("x-oss-credential", "accesskeyid/20241203/cn-hangzhou/oss/aliyun_v4_request"); // この変数を実際の AccessKey ID に置き換えてください。
            conditions.add(credentialCondition);
    
            Map<String, String> dateCondition = new HashMap<>();
            dateCondition.put("x-oss-date", "20241203T121212Z");
            conditions.add(dateCondition);
    
            conditions.add(Arrays.asList("content-length-range", 1, 10));
            conditions.add(Arrays.asList("eq", "$success_action_status", "201"));
            conditions.add(Arrays.asList("starts-with", "$key", "user/eric/"));
            conditions.add(Arrays.asList("in", "$content-type", Arrays.asList("image/jpg", "image/png")));
            conditions.add(Arrays.asList("not-in", "$cache-control", Arrays.asList("no-cache")));
    
            policy.put("conditions", conditions);
    
            String jsonPolicy = mapper.writeValueAsString(policy);
            // 手順 2:署名対象文字列を作成する。
            String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes()));
            System.out.println(stringToSign)
            
            // 手順 3:署名キーを計算する。
            byte[] dateKey = hmacsha256(("aliyun_v4" + accesskeysecret).getBytes(), "20231203");
            byte[] dateRegionKey = hmacsha256(dateKey, "cn-hangzhou");
            byte[] dateRegionServiceKey = hmacsha256(dateRegionKey, "oss");
            byte[] signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request");
    
            // 手順 4:署名を計算する。
            byte[] result = hmacsha256(signingKey, stringToSign);
            String signature = BinaryUtil.toHex(result);
            System.out.println("signature:" + signature);
    
        }
    
        public static byte[] hmacsha256(byte[] key, String data) {
            try {
                // SecretKeySpec を初期化し、アルゴリズムを HMAC-SHA256 に設定し、指定されたキーを使用します。
                SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");
    
                // Mac インスタンスを取得し、getInstance メソッドを使用してアルゴリズムを HMAC-SHA256 に設定します。
                Mac mac = Mac.getInstance("HmacSHA256");
                // キーを使用して Mac インスタンスを初期化します。
                mac.init(secretKeySpec);
    
                // HMAC 値を計算します。doFinal メソッドを使用して計算対象のデータを受信し、計算結果を配列として返します。
                byte[] hmacBytes = mac.doFinal(data.getBytes());
    
                return hmacBytes;
            } catch (Exception e) {
                throw new RuntimeException("HMAC-SHA256 の計算に失敗しました", e);
            }
        }
    }

    サンプルレスポンス:

    signature:3908473f7dbfb79a102eaaa44ca1edec8d7058ce3bd1c624d59eb437463bd5d6

    一時的なアクセス認証情報を使用する

    一時的なアクセス認証情報を使用して PostObject リクエストの署名を計算する場合、STS の AssumeRole 操作を呼び出すことによって、セキュリティトークンを取得する必要もあります。

    import com.aliyun.oss.common.utils.BinaryUtil;
    import org.apache.commons.codec.binary.Base64;
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import com.aliyun.sts20150401.models.AssumeRoleResponse;
    import com.aliyun.sts20150401.models.AssumeRoleResponseBody;
    import com.aliyun.tea.TeaException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import java.util.*;
    public class Demo {
    
        // STSClient インスタンスを初期化する。
        public static com.aliyun.sts20150401.Client createStsClient() throws Exception {
            com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                    // OSS_ACCESS_KEY_ID 環境変数が設定されていることを確認してください。
                    .setAccessKeyId(System.getenv("OSS_ACCESS_KEY_ID"))
                    // OSS_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
                    .setAccessKeySecret(System.getenv("OSS_ACCESS_KEY_SECRET"));
            // エンドポイント
            config.endpoint = "sts.cn-hangzhou.aliyuncs.com";
            return new com.aliyun.sts20150401.Client(config);
        }
    
        // STS トークンを取得する。
        public static AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials getCredential() throws Exception {
            com.aliyun.sts20150401.Client client = Demo.createStsClient();
            com.aliyun.sts20150401.models.AssumeRoleRequest assumeRoleRequest = new com.aliyun.sts20150401.models.AssumeRoleRequest()
                    // OSS_STS_ROLE_ARN 環境変数が設定されていることを確認してください。
                    .setRoleArn(System.getenv("OSS_STS_ROLE_ARN"))
                    .setRoleSessionName("role_session_name");// セッション名を指定します。
            com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
            try {
                // 必要に応じて、API 操作のレスポンスを表示するための独自のコードを記述してください。
                AssumeRoleResponse response = client.assumeRoleWithOptions(assumeRoleRequest, runtime);
                // 後続の操作に必要な AccessKey ID、AccessKey シークレット、および STS トークンは、credentials に含まれています。
                return response.body.credentials;
            } catch (TeaException error) {
                // 実際のビジネスシナリオでは例外を慎重に処理し、プロジェクトで例外を無視しないでください。この例に表示されるエラーメッセージは参考用です。
                // エラーメッセージ。
                System.out.println(error.getMessage());
                // トラブルシューティング用の URL。
                System.out.println(error.getData().get("Recommend"));
                com.aliyun.teautil.Common.assertAsString(error.message);
            } catch (Exception _error) {
                TeaException error = new TeaException(_error.getMessage(), _error);
                // 実際のビジネスシナリオでは例外を慎重に処理し、プロジェクトで例外を無視しないでください。この例に表示されるエラーメッセージは参考用です。
                // エラーメッセージ。
                System.out.println(error.getMessage());
                // トラブルシューティング用の URL。
                System.out.println(error.getData().get("Recommend"));
                com.aliyun.teautil.Common.assertAsString(error.message);
            }
            return null;
        }
    
        public static void main(String[] args) throws Exception {
    
            AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials sts_data = getCredential();
            String accesskeyid = sts_data.accessKeyId;
            String accesskeysecret = sts_data.accessKeySecret;
            String securitytoken = sts_data.securityToken;
            // 手順 1:ポリシーを作成する。
            ObjectMapper mapper = new ObjectMapper();
    
            Map<String, Object> policy = new HashMap<>();
            policy.put("expiration", "2024-12-03T13:00:00.000Z");
    
            List<Object> conditions = new ArrayList<>();
    
            Map<String, String> bucketCondition = new HashMap<>();
            bucketCondition.put("bucket", "examplebucket");
            conditions.add(bucketCondition);
    
            Map<String, String> signatureVersionCondition = new HashMap<>();
            signatureVersionCondition.put("x-oss-signature-version", "OSS4-HMAC-SHA256");
            conditions.add(signatureVersionCondition);
            
            Map<String, String> securityTokenCondition = new HashMap<>();
            securityTokenCondition.put("x-oss-security-token", securitytoken);
            conditions.add(securityTokenCondition);
            
            Map<String, String> credentialCondition = new HashMap<>();
            credentialCondition.put("x-oss-credential", "accesskeyid/20241203/cn-hangzhou/oss/aliyun_v4_request"); 
            conditions.add(credentialCondition);
    
            Map<String, String> dateCondition = new HashMap<>();
            dateCondition.put("x-oss-date", "20241203T121212Z");
            conditions.add(dateCondition);
    
            conditions.add(Arrays.asList("content-length-range", 1, 10));
            conditions.add(Arrays.asList("eq", "$success_action_status", "201"));
            conditions.add(Arrays.asList("starts-with", "$key", "user/eric/"));
            conditions.add(Arrays.asList("in", "$content-type", Arrays.asList("image/jpg", "image/png")));
            conditions.add(Arrays.asList("not-in", "$cache-control", Arrays.asList("no-cache")));
    
            policy.put("conditions", conditions);
    
            String jsonPolicy = mapper.writeValueAsString(policy);
            // 手順 2:署名対象文字列を作成する。
            String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes()));
    
            // 手順 3:署名キーを計算する。
            byte[] dateKey = hmacsha256(("aliyun_v4" + accesskeysecret).getBytes(), "20231203");
            byte[] dateRegionKey = hmacsha256(dateKey, "cn-hangzhou");
            byte[] dateRegionServiceKey = hmacsha256(dateRegionKey, "oss");
            byte[] signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request");
    
            // 手順 4:署名を計算する。
            byte[] result = hmacsha256(signingKey, stringToSign);
            String signature = BinaryUtil.toHex(result);
            System.out.println("signature:" + signature);
    
        }
    
        public static byte[] hmacsha256(byte[] key, String data) {
            try {
                // SecretKeySpec を初期化し、アルゴリズムを HMAC-SHA256 に設定し、指定されたキーを使用します。
                SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");
    
                // Mac インスタンスを取得し、getInstance メソッドを使用してアルゴリズムを HMAC-SHA256 に設定します。
                Mac mac = Mac.getInstance("HmacSHA256");
                // キーを使用して Mac インスタンスを初期化します。
                mac.init(secretKeySpec);
    
                // HMAC 値を計算します。doFinal メソッドを使用して計算対象のデータを受信し、計算結果を配列として返します。
                byte[] hmacBytes = mac.doFinal(data.getBytes());
    
                return hmacBytes;
            } catch (Exception e) {
                throw new RuntimeException("HMAC-SHA256 の計算に失敗しました", e);
            }
        }
    }

    サンプルレスポンス:

    signature:1e09438f7ad01af6b3e144b42c98929c68f8d090ce07f4c277b18d8b62d0aa02
  2. 次のサンプルコードは、OSS SDK for Python を使用して PostObject リクエストの V4 署名を計算する方法の例を示しています。

    import base64
    import hmac
    import hashlib
    import os
    
    def hmac_sha256(key, data):
        return hmac.new(key, data.encode('utf-8'), hashlib.sha256).digest()
    
    # 環境変数から AccessKey ID と AccessKey シークレットを取得します。
    accesskeyid = os.getenv('OSS_ACCESS_KEY_ID')
    accesskeysecret = os.getenv('OSS_ACCESS_KEY_SECRET')
    
    # AccessKey ID を表示します。
    print(accesskeyid)
    
    # 環境変数が取得されているかどうかを確認します。
    if not accesskeyid or not accesskeysecret:
        raise ValueError("必要な環境変数が見つかりません:OSS_ACCESS_KEY_ID または OSS_ACCESS_KEY_SECRET")
    
    # ポリシーを作成します。
    policy = '''{
      "expiration": "2025-01-01T00:00:00.000Z",
      "conditions": [
        {"x-oss-signature-version": "OSS4-HMAC-SHA256"},
        {"x-oss-credential": "accesskeyid/20241105/cn-hangzhou/oss/aliyun_v4_request"}, // accesskeyid を実際のアクセスキー ID に置き換えてください。
        {"x-oss-date": "20241105T065000Z"}
      ]
    }'''
    
    # ポリシーを出力します。
    print(policy)
    
    # 署名対象文字列を計算します。
    string_to_sign = base64.b64encode(policy.encode('utf-8')).decode('utf-8')
    print(string_to_sign)
    
    # 署名キーを計算します。
    date_key = hmac_sha256(f"aliyun_v4{accesskeysecret}".encode('utf-8'), "20241105")
    date_region_key = hmac_sha256(date_key, "cn-beijing")
    date_region_service_key = hmac_sha256(date_region_key, "oss")
    signing_key = hmac_sha256(date_region_service_key, "aliyun_v4_request")
    
    # 署名を計算します。
    result = hmac_sha256(signing_key, string_to_sign)
    signature = result.hex()
    print("signature:", signature)

    サンプルレスポンス:

    signature:9e85d56429245283b1aca5bc2dc31e0020b95ac2de9e9b81b496994db602ba1e