All Products
Search
Document Center

Simple Message Queue (formerly MNS):Verifikasi signature pesan push HTTP

Last Updated:Apr 25, 2026

Saat Simple Message Queue (formerly MNS) mendorong pesan topik ke titik akhir HTTP Anda, permintaan tersebut menyertakan signature RSA-SHA1 dalam header Authorization. Verifikasi signature ini untuk memastikan bahwa pesan berasal dari SMQ dan tidak dimodifikasi.

Praktik keamanan terbaik

Tinjau persyaratan berikut sebelum mengimplementasikan verifikasi signature:

  • Validasi URL sertifikat. URL sertifikat penandatangan harus diawali dengan https://mnstest.oss-cn-hangzhou.aliyuncs.com/ atau https://mns-cert.oss-cn-{region}.aliyuncs.com/. Tolak sertifikat apa pun dari awalan yang berbeda. Untuk detailnya, lihat Bagaimana cara memeriksa apakah URL sertifikat kunci publik disediakan oleh Alibaba Cloud?

  • Periksa stempel waktu. Header Date memiliki jendela validitas 15 menit. SMQ mengembalikan kode kesalahan 400 untuk permintaan yang kedaluwarsa. Tolak pesan dengan stempel waktu lebih dari 15 menit lalu untuk melindungi titik akhir Anda dari serangan replay. Untuk informasi selengkapnya, lihat Kode kesalahan.

Cara kerja verifikasi signature

Verifikasi signature terdiri dari empat langkah:

  1. Ambil sertifikat penandatangan X.509 dan ekstrak kunci publiknya.

  2. Buat string-to-sign dari permintaan HTTP.

  3. Decode Base64 header Authorization dan dekripsi nilainya menggunakan kunci publik.

  4. Bandingkan nilai hasil dekripsi dengan string-to-sign. Jika sesuai, permintaan tersebut autentik.

Contoh permintaan masuk

SMQ mengirim permintaan HTTP POST ke titik akhir Anda dengan header seperti berikut:

POST /notifications HTTP/1.1
Content-Type: text/xml;charset=utf-8
Content-MD5: ZDgxNjY5ZjFlMDQ5MGM0YWMwMWE5ODlmZDVlYmQxYjI=
Date: Wed, 25 May 2016 10:46:14 GMT
Authorization: Mko2Azg9fhCw8qR6G7AeAFMyzjO9qn7LDA5/t9E+6X5XURXTqBUuhpK+K55UNhrnlE2UdDkRrwDxsaDP5ajQ****
x-mns-request-id: 57458276F0E3D56D7C00****
x-mns-signing-cert-url: aHR0cDovL21uc3Rlc3Qub3NzLWNuLWhhbmd6aG91LmFsaXl1bmNzLmNvbS94NTA5X3B1YmxpY19jZXJ0aWZpY2F0ZS5w****
x-mns-version: 2015-06-06

Nilai x-mns-signing-cert-url dikodekan dalam Base64. Setelah didekodekan, nilai tersebut menghasilkan URL unduhan sertifikat.

Pengumuman perubahan

Penting

Mulai 7 April 2026, sertifikat penandatangan ditingkatkan menjadi penerapan berbasis wilayah, memungkinkan distribusi sertifikat yang lebih dekat. Detail peningkatan adalah sebagai berikut:

  • Format URL sertifikat baru: https://mns-cert.oss-cn-{region}.aliyuncs.com/x509_public_certificate.pem, dengan {region} sebagai pengenal wilayah Alibaba Cloud, seperti cn-hangzhou, cn-beijing, atau cn-shanghai.

  • Algoritma signature, konten kunci publik sertifikat, dan format signature tetap tidak berubah. Hanya URL unduhan sertifikat yang berubah.

  • Jika kode verifikasi signature Anda memvalidasi awalan URL sertifikat, tingkatkan logika validasi agar mendukung baik awalan lama maupun baru. Untuk informasi selengkapnya, lihat instruksi pemeriksaan validitas sertifikat di Langkah 1: Ambil sertifikat X.509.

  • URL sertifikat asli akan dihentikan. Segera lakukan adaptasi.

Langkah 1: Ambil sertifikat X.509

Ekstrak header x-mns-signing-cert-url dari permintaan masuk dan decode Base64 nilainya untuk mendapatkan URL sertifikat. Unduh sertifikat dari URL tersebut dan ekstrak kunci publiknya.

Penting

Pemeriksaan validitas sertifikat

Setelah menerima permintaan push, verifikasi bahwa URL sertifikat diawali dengan awalan yang valid untuk mencegah permintaan palsu. URL sertifikat dianggap valid jika memenuhi salah satu kondisi berikut:

  • URL diawali dengan https://mnstest.oss-cn-hangzhou.aliyuncs.com/ (akan dihentikan; segera beralih ke URL berbasis wilayah).

  • URL diawali dengan https://mns-cert.oss-cn-{region}.aliyuncs.com/, dengan {region} sebagai pengenal wilayah Alibaba Cloud, seperti cn-hangzhou, cn-beijing, cn-shanghai, atau cn-shenzhen.

Tolak permintaan apa pun yang URL sertifikatnya tidak memenuhi salah satu kondisi tersebut. Untuk informasi selengkapnya, lihat Bagaimana cara memeriksa apakah URL sertifikat kunci publik disediakan oleh Alibaba Cloud?

Langkah 2: Buat string-to-sign

Buat string-to-sign dengan menggabungkan komponen permintaan dalam format berikut:

StringToSign = HttpMethod + "\n"
             + CONTENT-MD5 + "\n"
             + CONTENT-TYPE + "\n"
             + DATE + "\n"
             + CanonicalizedMNSHeaders
             + CanonicalizedResource;

Parameter

Parameter Deskripsi
HttpMethod Metode HTTP dalam huruf kapital, seperti POST, GET, PUT, atau DELETE.
CONTENT-MD5 Hash MD5 dari badan permintaan. Biarkan kosong jika header Content-MD5 tidak ada.
CONTENT-TYPE Jenis konten dari badan permintaan. Biarkan kosong jika header Content-Type tidak ada.
DATE Stempel waktu permintaan dalam format GMT, seperti Thu, 07 Mar 2012 18:49:58 GMT. Jika permintaan menggunakan x-mns-date alih-alih Date, gunakan nilai x-mns-date. Parameter ini wajib diisi. SMQ harus menerima permintaan dalam waktu 15 menit setelah dikirim. Jika tidak, SMQ mengembalikan kode kesalahan 400. Untuk informasi selengkapnya, lihat Kode kesalahan.
CanonicalizedMNSHeaders Semua header x-mns-*, diurutkan secara alfabetis berdasarkan nama huruf kecil, digabungkan dalam format key:value\n. Lihat Buat CanonicalizedMNSHeaders.
CanonicalizedResource Path URI dan string kueri, tanpa domain dan port. Contohnya, http://123.123.XX.XX:8080/api/test?code=200 menghasilkan /api/test?code=200.
Penting

Jika gerbang atau gerbang transit lain dikonfigurasi dalam sistem Anda, gunakan URI asli dari permintaan HTTP, bukan URI yang telah dimodifikasi oleh gerbang.

Buat CanonicalizedMNSHeaders

Urutkan semua header x-mns-* secara alfabetis berdasarkan nama huruf kecil dan gabungkan:

// Ambil header permintaan
Map<String, String> httpHeaders = request.getHeaders();
// Urutkan header dan ubah nama menjadi huruf kecil
sortHeadersKeyAndToLowerCase(httpHeaders);
// Gabungkan header x-mns-*
Set<String> keySet = httpHeaders.keySet();
for (String key : keySet) {
    if (key.startsWith("x-mns-")) {
        CanonicalizedMNSHeaders.append(key).append(":")
            .append(httpHeaders.get(key)).append("\n");
    }
}

Contoh string-to-sign

Berdasarkan contoh permintaan di atas, string-to-sign yang dihasilkan adalah:

POST
ZDgxNjY5ZjFlMDQ5MGM0YWMwMWE5ODlmZDVlYmQxYjI=
text/xml;charset=utf-8
Wed, 25 May 2016 10:46:14 GMT
x-mns-request-id:57458276F0E3D56D7C00****
x-mns-signing-cert-url:aHR0cDovL21uc3Rlc3Qub3NzLWNuLWhhbmd6aG91LmFsaXl1bmNzLmNvbS94NTA5X3B1YmxpY19jZXJ0aWZpY2F0ZS5w****
x-mns-version:2015-06-06
/notifications

Langkah 3: Dekripsi header Authorization

Decode Base64 nilai header Authorization, lalu dekripsi byte hasil decode menggunakan kunci publik RSA dari Langkah 1.

Langkah 4: Bandingkan dan verifikasi

Bandingkan string-to-sign dari Langkah 2 dengan nilai hasil dekripsi dari Langkah 3:

  • Sesuai: Permintaan autentik. Proses pesannya.

  • Tidak sesuai: Permintaan mungkin palsu. Tolak permintaan tersebut.

Kode contoh Java

Contoh berikut menggabungkan keempat langkah tersebut. Metode authenticate mengambil sertifikat penandatangan, membuat string-to-sign, dan memverifikasi signature RSA-SHA1.

import org.apache.commons.codec.binary.Base64;

import java.io.DataInputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class SignDemo {
    private Boolean authenticate(String method, String uri, Map<String, String> headers) {
        try {
            // Langkah 1: Ambil URL sertifikat penandatangan dari header permintaan
            if (!headers.containsKey("x-mns-signing-cert-url")) {
                System.out.println("Header x-mns-signing-cert-url tidak ditemukan");
                return false;
            }
            String cert = headers.get("x-mns-signing-cert-url");
            if (cert.isEmpty()) {
                System.out.println("x-mns-signing-cert-url kosong");
                return false;
            }
            // Decode Base64 nilai header untuk mendapatkan URL sertifikat sebenarnya
            cert = new String(Base64.decodeBase64(cert));
            System.out.println("x-mns-signing-cert-url:\t" + cert);

            // Unduh sertifikat dan ekstrak kunci publik
            URL url = new URL(cert);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            DataInputStream in = new DataInputStream(conn.getInputStream());
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            Certificate c = cf.generateCertificate(in);
            PublicKey pk = c.getPublicKey();

            // Langkah 2: Buat string-to-sign
            String str2sign = getSignStr(method, uri, headers);
            System.out.println("String2Sign:\t" + str2sign);

            // Langkah 3: Decode Base64 header Authorization
            String signature = headers.get("Authorization");
            byte[] decodedSign = Base64.decodeBase64(signature);

            // Langkah 4: Verifikasi signature RSA-SHA1
            java.security.Signature signetcheck = java.security.Signature.getInstance("SHA1withRSA");
            signetcheck.initVerify(pk);
            signetcheck.update(str2sign.getBytes());
            Boolean res = signetcheck.verify(decodedSign);
            return res;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private String getSignStr(String method, String uri, Map<String, String> headers) {
        StringBuilder sb = new StringBuilder();
        sb.append(method);
        sb.append("\n");
        sb.append(safeGetHeader(headers, "Content-md5"));
        sb.append("\n");
        sb.append(safeGetHeader(headers, "Content-Type"));
        sb.append("\n");
        sb.append(safeGetHeader(headers, "Date"));
        sb.append("\n");

        List<String> tmp = new ArrayList<String>();
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            if (entry.getKey().startsWith("x-mns-")) {
                tmp.add(entry.getKey() + ":" + entry.getValue());
            }
        }
        Collections.sort(tmp);

        for (String kv : tmp) {
            sb.append(kv);
            sb.append("\n");
        }

        sb.append(uri);
        return sb.toString();
    }

    private String safeGetHeader(Map<String, String> headers, String name) {
        if (headers.containsKey(name)) {
            return headers.get(name);
        } else {
            return "";
        }
    }

    public static void main(String[] args) {
        SignDemo sd = new SignDemo();
        Map<String, String> headers = new HashMap<String, String>();
        headers.put("Authorization", "Mko2Azg9fhCw8qR6G7AeAFMyzjO9qn7LDA5/t9E+6X5XURXTqBUuhpK+K55UNhrnlE2UdDkRrwDxsaDP5ajQ****");
        headers.put("Content-md5", "M2ViOTE2ZDEyOTlkODBjMjVkNzM4YjNhNWI3ZWQ1****");
        headers.put("Content-Type", "text/xml;charset=utf-8");
        headers.put("Date", "Tue, 23 Feb 2016 09:41:06 GMT");
        headers.put("x-mns-request-id", "56CC2932F0E3D5BD5306****");
        headers.put("x-mns-signing-cert-url", "aHR0cDovL21uc3Rlc3Qub3NzLWNuLWhhbmd6aG91LmFsaXl1bmNzLmNvbS94NTA5X3B1YmxpY19jZXJ0aWZpY2F0ZS5w****");
        headers.put("x-mns-version", "2015-06-06");
        Boolean res = sd.authenticate("POST", "/notifications", headers);
        System.out.println("Hasil autentikasi:" + res);
    }
}