Untuk mengunggah objek menggunakan operasi PostObject, Object Storage Service (OSS) memerlukan tanda tangan V4 dalam setiap permintaan guna menjamin keamanan. Server aplikasi Anda menghitung tanda tangan ini dengan mengenkripsi serangkaian parameter permintaan—termasuk policy dan waktu kedaluwarsanya—menggunakan AccessKey Secret Anda. Klien kemudian menggunakan informasi tersebut untuk menyusun permintaan unggah. Saat OSS menerima permintaan unggah, OSS memverifikasi tanda tangan tersebut dan hanya memproses permintaan jika tanda tangan valid; permintaan lainnya ditolak.
Cara kerja tanda tangan POST
Permintaan HTTP POST menggunakan algoritma tanda tangan V4 untuk meningkatkan keamanan. Formulir dan policy sangat penting untuk menjamin keaslian dan keamanan permintaan unggah.
Elemen formulir
Formulir adalah kumpulan bidang dalam permintaan POST yang mentransmisikan file beserta metadata terkaitnya. Tabel berikut menjelaskan elemen formulir yang spesifik untuk tanda tangan V4. Untuk informasi mengenai elemen formulir umum, lihat Elemen formulir PostObject.
Parameter | Tipe | Wajib | Deskripsi |
x-oss-signature-version | String | Ya | Versi dan algoritma tanda tangan. Nilainya tetap |
x-oss-credential | String | Ya | Menentukan kumpulan parameter untuk kunci turunan. Formatnya sebagai berikut:
|
x-oss-date | String | Ya | Waktu permintaan dalam format ISO 8601. Contoh:
|
x-oss-signature | String | Ya | Tanda tangan yang digunakan untuk otentikasi. Nilainya dihitung dengan melakukan hashing terhadap string policy yang telah diencode Base64 menggunakan algoritma HMAC-SHA256, lalu mengonversi hasilnya menjadi string heksadesimal. |
Policy
Bidang formulir policy mendefinisikan izin dan batasan untuk unggahan file ke OSS melalui formulir HTML. Ditulis dalam format JSON, policy membatasi unggahan dengan menentukan parameter seperti nama bucket yang diizinkan, awalan objek, waktu kedaluwarsa, metode HTTP yang diizinkan, batas ukuran konten, dan batas tipe konten.
Policy harus mencakup bidang expiration dan conditions. Bidang conditions pada contoh berikut mencakup parameter opsional x-oss-security-token. Parameter ini hanya diperlukan saat Anda menggunakan kredensial akses temporary dari STS untuk menghasilkan tanda tangan POST. Jika Anda menggunakan AccessKey jangka panjang untuk menghasilkan tanda tangan POST, jangan sertakan bidang 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"]]
]
}Parameter policy dijelaskan sebagai berikut:
expiration
Menentukan waktu kedaluwarsa policy dalam format ISO 8601 GMT. Misalnya, nilai
2023-12-03T13:00:00.000Zberarti permintaan POST harus diajukan sebelum pukul 13.00 pada 3 Desember 2023.conditions
Menentukan kondisi untuk bidang formulir dalam permintaan POST.
Parameter
Jenis
Wajib
Deskripsi
Jenis pencocokan
bucket
String
Tidak
Nama bucket.
bucket
x-oss-signature-version
String
Ya
Versi dan algoritma tanda tangan. Nilainya tetap
OSS4-HMAC-SHA256.x-oss-signature-version
x-oss-credential
String
Ya
Menentukan kumpulan parameter untuk kunci turunan. Formatnya sebagai berikut:
<AccessKeyId>/<date>/<region>/oss/aliyun_v4_requestAccessKeyId: ID AccessKey Anda.date: Tanggal permintaan.region: ID wilayah Alibaba Cloud umum. Contoh:cn-hangzhou.oss: Nama layanan yang diminta. Nilainya tetaposs.aliyun_v4_request: Versi permintaan. Nilainya tetapaliyun_v4_request.
x-oss-credential
x-oss-security-token
String
Tidak
Hanya diperlukan saat Anda menggunakan kredensial akses temporary dari STS untuk menghasilkan tanda tangan POST. Anda dapat memperoleh token keamanan dengan memanggil operasi AssumeRole dari STS.
x-oss-security-token
x-oss-date
String
Ya
Waktu permintaan dalam format ISO 8601. Contoh:
20231203T121212Z.Diperbolehkan offset mundur hingga 15 menit. Artinya, waktu aktual saat server menerima permintaan dapat hingga 15 menit lebih lambat daripada waktu yang ditentukan dalam header
x-oss-date. Desain ini terutama mengakomodasi penundaan transmisi jaringan dan potensi kesalahan sinkronisasi waktu antara klien dan server, sehingga permintaan tetap dapat diproses secara normal dalam rentang tertentu meskipun waktu klien sedikit tidak akurat.Permintaan berlaku maksimal selama 7 hari sejak waktu yang ditentukan dalam header
x-oss-date. Jika nilaix-oss-datedalam permintaan menunjukkan bahwa permintaan telah kedaluwarsa (lebih dari 7 hari), server OSS akan menolak permintaan tersebut dan mengembalikan pesan error. Desain ini menjamin ketepatan waktu dan keamanan permintaan serta mencegah pengiriman permintaan lama yang telah ditandatangani secara jahat.Bidang
x-oss-datemenentukan waktu yang digunakan sebagaiTimeStampdalamStringToSign. Nilai bidang ini harus berada pada hari yang sama denganDateyang digunakan untuk membuat signing key dan harus sama dengan nilai bidangx-oss-datedalam formulir.
x-oss-date
content-length-range
String
Tidak
Ukuran minimum dan maksimum objek yang diizinkan untuk diunggah, dalam byte.
content-length-range
success_action_status
String
Tidak
Kode status HTTP yang dikembalikan setelah unggahan berhasil.
eq, eq-ci, starts-with, starts-with-ci, in, in-ci, not-in, not-in-ci
key
String
Tidak
Nama objek yang akan diunggah.
eq, eq-ci, starts-with, starts-with-ci, in, in-ci, not-in, not-in-ci
content-type
String
Tidak
Membatasi tipe file unggahan.
eq, eq-ci, starts-with, starts-with-ci, in, in-ci, not-in, not-in-ci
cache-control
String
Tidak
Menentukan perilaku caching objek.
eq, eq-ci, starts-with, starts-with-ci, in, in-ci, not-in, not-in-ci
Proses perhitungan tanda tangan
Buat policy yang diencode UTF-8.
Buat string yang akan ditandatangani.
Encode policy dengan Base64 untuk menghasilkan string aman. String yang diencode ini merupakan string yang akan ditandatangani.
Hitung signing key.
Gunakan AccessKey Secret Anda untuk melakukan serangkaian operasi penghashan HMAC-SHA256. Proses ini menghasilkan kunci turunan akhir, yang dikenal sebagai signing key, yang spesifik untuk tanggal, wilayah, dan layanan.
Hitung tanda tangan.
Tandatangani string yang akan ditandatangani dengan signing key menggunakan algoritma HMAC-SHA256. Konversi hash biner yang dihasilkan menjadi string heksadesimal. String heksadesimal ini merupakan tanda tangan akhir yang memverifikasi integritas dan keaslian permintaan.
Contoh perhitungan tanda tangan POST
Kode Java berikut memberikan contoh lengkap cara menghitung tanda tangan POST untuk policy yang dijelaskan sebelumnya.
Kredensial 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 { // Sebelum menjalankan kode ini, pastikan variabel lingkungan OSS_ACCESS_KEY_ID dan OSS_ACCESS_KEY_SECRET telah disetel. String accesskeyid = System.getenv().get("OSS_ACCESS_KEY_ID"); String accesskeysecret = System.getenv().get("OSS_ACCESS_KEY_SECRET"); // Langkah 1: Buat policy. 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); // Langkah 2: Buat string yang akan ditandatangani. String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes())); System.out.println(stringToSign); // Langkah 3: Hitung signing key. 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"); // Langkah 4: Hitung tanda tangan. byte[] result = hmacsha256(signingKey, stringToSign); String signature = BinaryUtil.toHex(result); System.out.println("signature:" + signature); } public static byte[] hmacsha256(byte[] key, String data) { try { // Inisialisasi spesifikasi kunci HMAC, tentukan HmacSHA256 sebagai algoritma dan gunakan kunci yang diberikan. SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256"); // Dapatkan instance Mac, tentukan HmacSHA256 sebagai algoritma. Mac mac = Mac.getInstance("HmacSHA256"); // Inisialisasi objek Mac dengan kunci. mac.init(secretKeySpec); // Lakukan perhitungan HMAC. Metode doFinal mengambil data dan mengembalikan hash hasilnya sebagai array byte. byte[] hmacBytes = mac.doFinal(data.getBytes()); return hmacBytes; } catch (Exception e) { throw new RuntimeException("Gagal menghitung HMAC-SHA256", e); } } }Hasil berikut dikembalikan:
signature:3908473f7dbfb79a102eaaa44ca1edec8d7058ce3bd1c624d59eb437463bd5d6Kredensial akses temporary
Saat Anda menggunakan kredensial akses temporary dari STS untuk menghasilkan tanda tangan POST, Anda juga harus memperoleh token keamanan dengan memanggil operasi AssumeRole dari STS.
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 { // Inisialisasi klien STS. public static com.aliyun.sts20150401.Client createStsClient() throws Exception { com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config() // Wajib. Pastikan variabel lingkungan OSS_ACCESS_KEY_ID telah disetel. .setAccessKeyId(System.getenv("OSS_ACCESS_KEY_ID")) // Wajib. Pastikan variabel lingkungan OSS_ACCESS_KEY_SECRET telah disetel. .setAccessKeySecret(System.getenv("OSS_ACCESS_KEY_SECRET")); // Titik akhir config.endpoint = "sts.cn-hangzhou.aliyuncs.com"; return new com.aliyun.sts20150401.Client(config); } // Peroleh kredensial akses temporary dari 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() // Wajib. Pastikan variabel lingkungan OSS_STS_ROLE_ARN telah disetel. .setRoleArn(System.getenv("OSS_STS_ROLE_ARN")) .setRoleSessionName("role_session_name");// Nama sesi kustom. com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions(); try { // Jika Anda menyalin kode ini, cetak respons API sendiri. AssumeRoleResponse response = client.assumeRoleWithOptions(assumeRoleRequest, runtime); // Objek credentials berisi AccessKeyId, AccessKeySecret, dan SecurityToken untuk operasi selanjutnya. return response.body.credentials; } catch (TeaException error) { // Ini hanya untuk demonstrasi. Di lingkungan produksi, tangani exception dengan hati-hati dan jangan abaikan. // Pesan error System.out.println(error.getMessage()); // URL pemecahan masalah System.out.println(error.getData().get("Recommend")); com.aliyun.teautil.Common.assertAsString(error.message); } catch (Exception _error) { TeaException error = new TeaException(_error.getMessage(), _error); // Ini hanya untuk demonstrasi. Di lingkungan produksi, tangani exception dengan hati-hati dan jangan abaikan. // Pesan error System.out.println(error.getMessage()); // URL pemecahan masalah 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; // Langkah 1: Buat policy. 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); // Langkah 2: Buat string yang akan ditandatangani. String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes())); // Langkah 3: Hitung signing key. 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"); // Langkah 4: Hitung tanda tangan. byte[] result = hmacsha256(signingKey, stringToSign); String signature = BinaryUtil.toHex(result); System.out.println("signature:" + signature); } public static byte[] hmacsha256(byte[] key, String data) { try { // Inisialisasi spesifikasi kunci HMAC, tentukan HmacSHA256 sebagai algoritma dan gunakan kunci yang diberikan. SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256"); // Dapatkan instance Mac, tentukan HmacSHA256 sebagai algoritma. Mac mac = Mac.getInstance("HmacSHA256"); // Inisialisasi objek Mac dengan kunci. mac.init(secretKeySpec); // Lakukan perhitungan HMAC. Metode doFinal mengambil data dan mengembalikan hash hasilnya sebagai array byte. byte[] hmacBytes = mac.doFinal(data.getBytes()); return hmacBytes; } catch (Exception e) { throw new RuntimeException("Gagal menghitung HMAC-SHA256", e); } } }Hasil berikut dikembalikan:
signature:1e09438f7ad01af6b3e144b42c98929c68f8d090ce07f4c277b18d8b62d0aa02Kode Python berikut memberikan contoh lengkap cara menghitung tanda tangan 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() # Baca ID AccessKey dan AccessKey Secret dari variabel lingkungan. accesskeyid = os.getenv('OSS_ACCESS_KEY_ID') accesskeysecret = os.getenv('OSS_ACCESS_KEY_SECRET') # Cetak ID AccessKey. print(accesskeyid) # Periksa apakah variabel lingkungan berhasil diambil. if not accesskeyid or not accesskeysecret: raise ValueError("Variabel lingkungan yang diperlukan tidak ditemukan: OSS_ACCESS_KEY_ID atau OSS_ACCESS_KEY_SECRET") # Buat policy. 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"}} ] }}''' # Cetak policy. print(policy) # Hitung string yang akan ditandatangani. string_to_sign = base64.b64encode(policy.encode('utf-8')).decode('utf-8') print(string_to_sign) # Hitung signing key. 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") # Hitung tanda tangan. result = hmac_sha256(signing_key, string_to_sign) signature = result.hex() print("signature:", signature)Hasil berikut dikembalikan:
signature:9e85d56429245283b1aca5bc2dc31e0020b95ac2de9e9b81b496994db602ba1e