Object Storage Service (OSS) では、PostObject 操作を使用してオブジェクトをアップロードする際に、各リクエストに V4 署名を含める必要があります。これにより、セキュリティが確保されます。アプリケーションサーバは、ご利用の AccessKey Secret を使用して、ポリシーとその有効期限を含む一連のリクエストパラメーターを暗号化し、この署名を計算します。クライアントはこの情報を使用してアップロードリクエストを構築します。OSS がアップロードリクエストを受信すると、署名を検証し、署名が有効な場合のみリクエストを処理し、それ以外のリクエストはすべて拒否します。
POST 署名の仕組み
HTTP POST リクエストでは、V4 署名アルゴリズムを使用してセキュリティを強化します。フォームおよびポリシーは、アップロードリクエストの真正性とセキュリティを保証するために重要です。
フォーム要素
フォームとは、POST リクエスト内でファイルと関連メタデータを送信するためのフィールドのコレクションです。次の表は、V4 署名に特有のフォーム要素について説明しています。一般的なフォーム要素については、「PostObject フォーム要素」をご参照ください。
パラメーター | タイプ | 必須 | 説明 |
x-oss-signature-version | String | はい | 署名バージョンおよびアルゴリズム。値は |
x-oss-credential | String | はい | 派生キー用のパラメーターセットを指定します。形式は以下のとおりです。
|
x-oss-date | String | はい | ISO 8601 形式でのリクエスト時刻。例:
|
x-oss-signature | String | はい | 認証に使用される署名。値は、Base64 エンコードされたポリシー文字列を HMAC-SHA256 アルゴリズムでハッシュ化し、結果を 16 進数文字列に変換することで計算されます。 |
ポリシー
ポリシーのフォームフィールドは、HTML フォームを通じて OSS へのファイルアップロードに関する権限と制約を定義します。JSON 形式で記述され、許可されたバケット名、オブジェクトプレフィックス、有効期限、許可された HTTP メソッド、コンテンツサイズ制限、コンテンツタイプ制限などのパラメーターを定義することで、アップロードを制限します。
ポリシーには expiration および conditions フィールドを必ず含める必要があります。以下の例の conditions フィールドには、オプションのパラメーターである x-oss-security-token が含まれています。このパラメーターは、STS からの一時的なアクセス認証情報を使用して POST 署名を生成する場合にのみ必要です。長期的な AccessKey を使用して POST 署名を生成する場合は、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"]]
]
}ポリシーパラメーターの説明は以下のとおりです。
expiration
ポリシーの有効期限を ISO 8601 GMT 形式で指定します。たとえば、
2023-12-03T13:00:00.000Zという値は、POST リクエストを 2023 年 12 月 03 日 13:00 以前に開始する必要があることを意味します。conditions
POST リクエストのフォームフィールドに関する条件を定義します。
パラメーター
タイプ
必須
説明
マッチングタイプ
bucket
String
いいえ
バケット名。
bucket
x-oss-signature-version
String
はい
署名バージョンおよびアルゴリズム。値は
OSS4-HMAC-SHA256に固定されています。x-oss-signature-version
x-oss-credential
String
はい
派生キー用のパラメーターセットを指定します。形式は以下のとおりです。
<AccessKeyId>/<date>/<region>/oss/aliyun_v4_requestAccessKeyId: ご利用の AccessKey ID。date: リクエスト日付。region: Alibaba Cloud の汎用リージョン ID。例:cn-hangzhou。oss: リクエスト対象のサービス名。値はossに固定されています。aliyun_v4_request: リクエストバージョン。値はaliyun_v4_requestに固定されています。
x-oss-credential
x-oss-security-token
String
いいえ
STS からの一時的なアクセス認証情報を使用して POST 署名を生成する場合にのみ必要です。セキュリティトークンは、STS の AssumeRole 操作を呼び出すことで取得できます。
x-oss-security-token
x-oss-date
String
はい
ISO 8601 形式でのリクエスト時刻。例:
20231203T121212Z。最大 15 分の遡及許容があります。つまり、サーバーがリクエストを受信する実際の時刻は、
x-oss-dateヘッダーで指定された時刻から最大 15 分後まで許容されます。これは主にネットワーク伝送遅延およびクライアントとサーバー間の潜在的な時刻同期エラーを考慮したもので、クライアントの時刻がわずかにずれていても一定の範囲内でリクエストが正常に処理されることを保証します。リクエストの有効期間は、
x-oss-dateヘッダーで指定された時刻から最大 7 日間です。x-oss-dateの値がリクエストの有効期限切れ(7 日以上経過)を示している場合、OSS サーバーはそのリクエストを拒否し、エラーメッセージを返します。これにより、リクエストの適時性とセキュリティが確保され、古い署名付きリクエストの悪意ある再送を防止します。x-oss-dateフィールドは、StringToSign内のTimeStampとして使用される時刻を指定します。このフィールドの値は、署名キー作成時に使用したDateと同じ日付であり、かつフォーム内のx-oss-dateフィールドの値と同一である必要があります。
x-oss-date
content-length-range
String
いいえ
アップロードするオブジェクトの許容最小サイズおよび最大サイズ(バイト単位)。
content-length-range
success_action_status
String
いいえ
アップロード成功後に返す HTTP ステータスコード。
eq、eq-ci、starts-with、starts-with-ci、in、in-ci、not-in、not-in-ci
key
String
いいえ
アップロードするオブジェクトの名前。
eq、eq-ci、starts-with、starts-with-ci、in、in-ci、not-in、not-in-ci
content-type
String
いいえ
アップロードのファイルタイプを制限します。
eq、eq-ci、starts-with、starts-with-ci、in、in-ci、not-in、not-in-ci
cache-control
String
いいえ
オブジェクトのキャッシュ動作を指定します。
eq、eq-ci、starts-with、starts-with-ci、in、in-ci、not-in、not-in-ci
署名計算プロセス
UTF-8 エンコードされたポリシーを作成します。
署名対象文字列を構築します。
ポリシーを Base64 エンコードして安全な文字列を生成します。このエンコードされた文字列が署名対象文字列です。
署名キーを計算します。
ご利用の AccessKey Secret を使用して一連の HMAC-SHA256 ハッシュ化操作を実行します。このプロセスにより、日付、リージョン、サービスに固有の最終的な派生キー(署名キー)が作成されます。
署名を計算します。
HMAC-SHA256 アルゴリズムを使用して、署名対象文字列を署名キーで署名します。結果として得られるバイナリハッシュを 16 進数文字列に変換します。この 16 進数文字列が、リクエストの整合性と真正性を検証するための最終的な署名です。
POST 署名計算の例
以下の Java コードは、前述のポリシーに対する POST 署名の計算方法を示す完全な例です。
AccessKey 認証情報
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.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import java.util.Arrays; 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"); 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(), "20241203"); 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 { // HMAC キースペックを初期化し、アルゴリズムとして HmacSHA256 を指定し、提供されたキーを使用します。 SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256"); // Mac インスタンスを取得し、アルゴリズムとして HmacSHA256 を指定します。 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一時的なアクセス認証情報
STS からの一時的なアクセス認証情報を使用して POST 署名を生成する場合は、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 { // STS クライアントを初期化します。 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); // credentials オブジェクトには、後続の操作に必要な AccessKeyId、AccessKeySecret、SecurityToken が含まれています。 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(), "20241203"); 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 { // HMAC キースペックを初期化し、アルゴリズムとして HmacSHA256 を指定し、提供されたキーを使用します。 SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256"); // Mac インスタンスを取得し、アルゴリズムとして HmacSHA256 を指定します。 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以下の Python コードは、POST 署名の計算方法を示す完全な例です。
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 Secret を読み取ります。 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 = f'''{{ "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"}}, {{"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-hangzhou") 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