Gunakan cookie yang ditandatangani

Halaman ini menyediakan ringkasan cookie yang ditandatangani dan petunjuk untuk menggunakannya dengan Cloud CDN. Cookie bertanda tangan memberikan akses resource dengan waktu terbatas ke sekumpulan file, terlepas dari apakah pengguna memiliki Akun Google atau tidak.

Cookie yang ditandatangani adalah alternatif untuk URL yang ditandatangani. Cookie yang ditandatangani melindungi akses saat menandatangani puluhan atau ratusan URL secara terpisah untuk setiap pengguna tidak memungkinkan di aplikasi Anda.

Cookie bertanda tangan memungkinkan Anda melakukan hal berikut:

  • Beri pengguna otorisasi dan berikan token berbatas waktu untuk mengakses konten yang dilindungi (bukan menandatangani setiap URL).
  • Berikan cakupan akses pengguna ke awalan URL tertentu, seperti https://media.example.com/videos/, dan berikan akses kepada pengguna yang diberi otorisasi ke konten yang dilindungi hanya dalam awalan URL tersebut.
  • Jangan ubah URL dan manifes media, sehingga menyederhanakan pipeline pengemasan dan meningkatkan kemampuan cache.

Jika Anda ingin mencakup akses ke URL tertentu, pertimbangkan untuk menggunakan URL bertanda tangan.

Sebelum memulai

Sebelum Anda menggunakan cookie yang ditandatangani, lakukan hal berikut:

  • Pastikan Cloud CDN diaktifkan. Untuk mengetahui petunjuknya, lihat Menggunakan Cloud CDN. Anda dapat mengonfigurasi cookie yang ditandatangani di backend sebelum mengaktifkan Cloud CDN, tetapi hal apa pun tidak akan berpengaruh hingga Cloud CDN diaktifkan.

  • Jika perlu, update Google Cloud CLI ke versi terbaru:

    gcloud components update
    

Untuk ringkasan, lihat URL yang ditandatangani dan cookie yang ditandatangani.

Mengonfigurasi kunci permintaan yang ditandatangani

Membuat kunci untuk URL yang ditandatangani atau cookie yang ditandatangani memerlukan beberapa langkah, yang dijelaskan di bagian berikut.

Pertimbangan keamanan

Cloud CDN tidak memvalidasi permintaan dalam situasi berikut:

  • Permintaan tidak ditandatangani.
  • Layanan backend atau bucket backend untuk permintaan tidak mengaktifkan Cloud CDN.

Permintaan bertanda tangan harus selalu divalidasi di asal sebelum menampilkan respons. Hal ini karena origin dapat digunakan untuk menayangkan campuran konten yang ditandatangani dan tidak ditandatangani, serta karena klien mungkin mengakses origin secara langsung.

  • Cloud CDN tidak memblokir permintaan tanpa parameter kueri Signature atau cookie HTTP Cloud-CDN-Cookie. API ini menolak permintaan dengan parameter permintaan yang tidak valid (atau salah format).
  • Jika aplikasi Anda mendeteksi tanda tangan yang tidak valid, pastikan aplikasi Anda merespons dengan kode respons HTTP 403 (Unauthorized). Kode respons HTTP 403 tidak dapat di-cache.
  • Respons terhadap permintaan yang ditandatangani dan tidak ditandatangani akan disimpan dalam cache secara terpisah, sehingga respons yang berhasil terhadap permintaan yang ditandatangani dan valid tidak akan digunakan untuk menyalurkan permintaan yang tidak ditandatangani.
  • Jika aplikasi Anda mengirimkan kode respons yang dapat di-cache ke permintaan yang tidak valid, permintaan mendatang yang valid mungkin akan ditolak secara keliru.

Untuk backend Cloud Storage, pastikan Anda menghapus akses publik, sehingga Cloud Storage dapat menolak permintaan yang tidak memiliki tanda tangan yang valid.

Tabel berikut merangkum perilaku tersebut.

Permintaan memiliki tanda tangan Cache ditemukan Perilaku
Tidak Tidak Teruskan ke asal backend.
Tidak Ya Menayangkan dari cache.
Ya Tidak Validasi tanda tangan. Jika valid, teruskan ke asal backend.
Ya Ya Validasi tanda tangan. Jika valid, tayangkan dari cache.

Membuat kunci permintaan yang ditandatangani

Anda mengaktifkan dukungan untuk URL yang ditandatangani dan cookie yang ditandatangani Cloud CDN dengan membuat satu atau beberapa kunci di layanan backend, bucket backend, atau keduanya yang mendukung Cloud CDN.

Untuk setiap layanan backend atau bucket backend, Anda dapat membuat dan menghapus kunci sesuai kebutuhan keamanan Anda. Setiap backend dapat memiliki hingga tiga kunci yang dikonfigurasi sekaligus. Sebaiknya rotasikan kunci Anda secara berkala dengan menghapus yang terlama, menambahkan kunci baru, dan menggunakan kunci baru saat menandatangani URL atau cookie.

Anda dapat menggunakan nama kunci yang sama di beberapa layanan backend dan bucket backend karena setiap kumpulan kunci tidak bergantung pada kumpulan lainnya. Nama kunci dapat berisi hingga 63 karakter. Untuk memberi nama kunci, gunakan karakter A-Z, a-z, 0-9, _ (garis bawah), dan - (tanda hubung).

Saat Anda membuat kunci, pastikan untuk menyimpannya dengan aman karena siapa pun yang memiliki salah satu kunci Anda dapat membuat URL bertanda tangan atau cookie bertanda tangan yang diterima Cloud CDN hingga kunci tersebut dihapus dari Cloud CDN. Kunci disimpan di komputer tempat Anda membuat URL yang ditandatangani atau cookie yang ditandatangani. Cloud CDN juga menyimpan kunci untuk memverifikasi tanda tangan permintaan.

Untuk menjaga kerahasiaan kunci, nilai kunci tidak disertakan dalam respons atas permintaan API apa pun. Jika kehilangan kunci, Anda harus membuat kunci baru.

Untuk membuat kunci permintaan yang ditandatangani, ikuti langkah-langkah berikut.

Konsol

  1. Di konsol Google Cloud, buka halaman Cloud CDN.

    Buka Cloud CDN

  2. Klik nama origin yang ingin Anda tambahi kunci tersebut.
  3. Di halaman Detail asal, klik tombol Edit.
  4. Di bagian Origin Fundamentals, klik Next untuk membuka bagian Host and path rules.
  5. Di bagian Host and path rules, klik Next untuk membuka bagian Cache performance.
  6. Di bagian Konten yang dibatasi, pilih Batasi akses menggunakan URL yang ditandatangani dan cookie yang ditandatangani.
  7. Klik Tambahkan kunci penandatanganan.

    1. Tentukan nama unik untuk kunci penandatanganan baru.
    2. Di bagian Key creation method, pilih Automatically generate. Atau, klik Let me enter, lalu tentukan nilai kunci penandatanganan.

      Untuk opsi pertama, salin nilai kunci penandatanganan yang dihasilkan secara otomatis ke file pribadi, yang dapat Anda gunakan untuk membuat URL yang ditandatangani.

    3. Klik Done.

    4. Di bagian Cache entry maximum age, masukkan nilai, lalu pilih satuan waktu.

  8. Klik Done.

gcloud

Alat command line gcloud membaca kunci dari file lokal yang Anda tentukan. File kunci harus dibuat dengan menghasilkan 128 bit yang sangat acak, mengenkodenya dengan base64, lalu mengganti karakter + dengan - dan mengganti karakter / dengan _. Untuk informasi selengkapnya, lihat RFC 4648. Kuncinya harus benar-benar acak. Pada sistem yang mirip dengan UNIX, Anda dapat membuat kunci yang sangat acak dan menyimpannya dalam file kunci dengan perintah berikut:

head -c 16 /dev/urandom | base64 | tr +/ -_ > KEY_FILE_NAME

Untuk menambahkan kunci ke layanan backend:

gcloud compute backend-services \
   add-signed-url-key BACKEND_NAME \
   --key-name KEY_NAME \
   --key-file KEY_FILE_NAME

Untuk menambahkan kunci ke bucket backend:

gcloud compute backend-buckets \
   add-signed-url-key BACKEND_NAME \
   --key-name KEY_NAME \
   --key-file KEY_FILE_NAME

Mengonfigurasi izin Cloud Storage

Jika Anda menggunakan Cloud Storage dan telah membatasi siapa yang dapat membaca objek, Anda harus memberikan izin kepada Cloud CDN untuk membaca objek dengan menambahkan akun layanan Cloud CDN ke ACL Cloud Storage.

Anda tidak perlu membuat akun layanan. Akun layanan dibuat secara otomatis saat Anda pertama kali menambahkan kunci ke bucket backend di sebuah project.

Sebelum menjalankan perintah berikut, tambahkan minimal satu kunci ke bucket backend di project Anda. Jika tidak, perintah akan gagal dengan menampilkan error karena akun layanan pengisian cache Cloud CDN tidak dibuat hingga Anda menambahkan satu atau beberapa kunci untuk project tersebut. Ganti PROJECT_NUM dengan nomor project Anda dan BUCKET dengan bucket penyimpanan Anda.

gsutil iam ch \
  serviceAccount:service-PROJECT_NUM@cloud-cdn-fill.iam.gserviceaccount.com:objectViewer \
  gs://BUCKET

Akun layanan Cloud CDN service-PROJECT_NUM@cloud-cdn-fill.iam.gserviceaccount.com tidak muncul dalam daftar akun layanan di project Anda. Hal ini karena akun layanan Cloud CDN dimiliki oleh Cloud CDN, bukan project Anda.

Untuk mengetahui informasi selengkapnya tentang nomor project, baca artikel Menemukan project ID dan nomor project di dokumentasi Bantuan Konsol Google Cloud.

Menyesuaikan waktu cache maksimum

Cloud CDN menyimpan respons untuk permintaan yang ditandatangani ke dalam cache, terlepas dari header Cache-Control backend. Waktu maksimum respons dapat disimpan dalam cache tanpa validasi ulang ditetapkan oleh flag signed-url-cache-max-age, yang ditetapkan secara default ke satu jam dan dapat diubah seperti yang ditunjukkan di sini.

Untuk menyetel waktu cache maksimum bagi layanan backend atau bucket backend, jalankan salah satu perintah berikut:

gcloud compute backend-services update BACKEND_NAME
  --signed-url-cache-max-age MAX_AGE
gcloud compute backend-buckets update BACKEND_NAME
  --signed-url-cache-max-age MAX_AGE

Mencantumkan nama kunci permintaan yang ditandatangani

Untuk menampilkan daftar kunci pada layanan backend atau bucket backend, jalankan salah satu perintah berikut:

gcloud compute backend-services describe BACKEND_NAME
gcloud compute backend-buckets describe BACKEND_NAME

Hapus kunci permintaan yang ditandatangani

Jika URL yang ditandatangani oleh kunci tertentu tidak lagi dipatuhi, jalankan salah satu perintah berikut untuk menghapus kunci tersebut dari layanan backend atau bucket backend:

gcloud compute backend-services \
   delete-signed-url-key BACKEND_NAME --key-name KEY_NAME
gcloud compute backend-buckets \
   delete-signed-url-key BACKEND_NAME --key-name KEY_NAME

Membuat kebijakan

Kebijakan cookie bertanda tangan adalah serangkaian pasangan key-value (yang dipisahkan oleh karakter :), mirip dengan parameter kueri yang digunakan di URL yang ditandatangani. Misalnya, lihat Menerbitkan cookie kepada pengguna.

Kebijakan menunjukkan parameter yang membuat permintaan valid. Kebijakan ditandatangani menggunakan kode autentikasi pesan berbasis hash (HMAC) yang divalidasi Cloud CDN pada setiap permintaan.

Menentukan format dan kolom kebijakan

Ada empat kolom wajib diisi yang harus Anda tentukan dengan urutan berikut:

  • URLPrefix
  • Expires
  • KeyName
  • Signature

Pasangan key-value dalam kebijakan cookie yang ditandatangani bersifat peka huruf besar/kecil.

URLPrefix

URLPrefix menunjukkan awalan URL berenkode base64 yang aman untuk URL yang mencakup semua jalur yang valid untuk tanda tangan tersebut.

URLPrefix mengenkode skema (http:// atau https://), FQDN, dan jalur opsional. Mengakhiri jalur dengan / bersifat opsional, tetapi direkomendasikan. Awalan tidak boleh menyertakan fragmen atau parameter kueri seperti ? atau #.

Misalnya, https://media.example.com/videos mencocokkan permintaan dengan kedua hal berikut:

  • https://media.example.com/videos?video_id=138183&user_id=138138
  • https://media.example.com/videos/137138595?quality=low

Jalur awalan digunakan sebagai substring teks, tidak hanya jalur direktori. Misalnya, awalan https://example.com/data memberikan akses ke kedua hal berikut:

  • /data/file1
  • /database

Untuk menghindari kesalahan ini, sebaiknya akhiri semua awalan dengan /, kecuali jika Anda sengaja memilih untuk mengakhiri awalan dengan nama file parsial, seperti https://media.example.com/videos/123 untuk memberikan akses ke hal berikut:

  • /videos/123_chunk1
  • /videos/123_chunk2
  • /videos/123_chunkN

Jika URL yang diminta tidak cocok dengan URLPrefix, Cloud CDN akan menolak permintaan dan menampilkan error HTTP 403 ke klien.

Expires

Expires harus berupa stempel waktu Unix (jumlah detik sejak 1 Januari 1970).

KeyName

KeyName adalah nama kunci untuk kunci yang dibuat terhadap bucket backend atau layanan backend. Nama kunci peka huruf besar/kecil.

Tanda Tangan

Signature adalah tanda tangan HMAC-SHA-1 dengan enkode base64 yang aman URL dari kolom yang membentuk kebijakan cookie. Otorisasi ini divalidasi pada setiap permintaan; permintaan dengan tanda tangan yang tidak valid ditolak dengan error HTTP 403.

Membuat cookie yang ditandatangani secara terprogram

Contoh kode berikut menunjukkan cara membuat cookie yang ditandatangani secara terprogram.

Go

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"time"
)

// signCookie creates a signed cookie for an endpoint served by Cloud CDN.
//
// - urlPrefix must start with "https://" and should include the path prefix
// for which the cookie will authorize access to.
// - key should be in raw form (not base64url-encoded) which is
// 16-bytes long.
// - keyName must match a key added to the backend service or bucket.
func signCookie(urlPrefix, keyName string, key []byte, expiration time.Time) (string, error) {
	encodedURLPrefix := base64.URLEncoding.EncodeToString([]byte(urlPrefix))
	input := fmt.Sprintf("URLPrefix=%s:Expires=%d:KeyName=%s",
		encodedURLPrefix, expiration.Unix(), keyName)

	mac := hmac.New(sha1.New, key)
	mac.Write([]byte(input))
	sig := base64.URLEncoding.EncodeToString(mac.Sum(nil))

	signedValue := fmt.Sprintf("%s:Signature=%s",
		input,
		sig,
	)

	return signedValue, nil
}

Java

import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.time.ZonedDateTime;
import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class SignedCookies {

  public static void main(String[] args) throws Exception {
    // TODO(developer): Replace these variables before running the sample.

    // The name of the signing key must match a key added to the back end bucket or service.
    String keyName = "YOUR-KEY-NAME";
    // Path to the URL signing key uploaded to the backend service/bucket.
    String keyPath = "/path/to/key";
    // The Unix timestamp that the signed URL expires.
    long expirationTime = ZonedDateTime.now().plusDays(1).toEpochSecond();
    // URL prefix to sign as a string. URL prefix must start with either "http://" or "https://"
    // and must not include query parameters.
    String urlPrefix = "https://media.example.com/videos/";

    // Read the key as a base64 url-safe encoded string, then convert to byte array.
    // Key used in signing must be in raw form (not base64url-encoded).
    String base64String = new String(Files.readAllBytes(Paths.get(keyPath)),
        StandardCharsets.UTF_8);
    byte[] keyBytes = Base64.getUrlDecoder().decode(base64String);

    // Create signed cookie from policy.
    String signedCookie = signCookie(urlPrefix, keyBytes, keyName, expirationTime);
    System.out.println(signedCookie);
  }

  // Creates a signed cookie for the specified policy.
  public static String signCookie(String urlPrefix, byte[] key, String keyName,
      long expirationTime)
      throws InvalidKeyException, NoSuchAlgorithmException {

    // Validate input URL prefix.
    try {
      URL validatedUrlPrefix = new URL(urlPrefix);
      if (!validatedUrlPrefix.getProtocol().startsWith("http")) {
        throw new IllegalArgumentException(
            "urlPrefix must start with either http:// or https://: " + urlPrefix);
      }
      if (validatedUrlPrefix.getQuery() != null) {
        throw new IllegalArgumentException("urlPrefix must not include query params: " + urlPrefix);
      }
    } catch (MalformedURLException e) {
      throw new IllegalArgumentException(
          "urlPrefix malformed: " + urlPrefix);
    }

    String encodedUrlPrefix = Base64.getUrlEncoder().encodeToString(urlPrefix.getBytes(
        StandardCharsets.UTF_8));
    String policyToSign = String.format("URLPrefix=%s:Expires=%d:KeyName=%s", encodedUrlPrefix,
        expirationTime, keyName);

    String signature = getSignatureForUrl(key, policyToSign);
    return String.format("Cloud-CDN-Cookie=%s:Signature=%s", policyToSign, signature);
  }

  // Creates signature for input string with private key.
  private static String getSignatureForUrl(byte[] privateKey, String input)
      throws InvalidKeyException, NoSuchAlgorithmException {

    final String algorithm = "HmacSHA1";
    final int offset = 0;
    Key key = new SecretKeySpec(privateKey, offset, privateKey.length, algorithm);
    Mac mac = Mac.getInstance(algorithm);
    mac.init(key);
    return Base64.getUrlEncoder()
        .encodeToString(mac.doFinal(input.getBytes(StandardCharsets.UTF_8)));
  }
}

Python

def sign_cookie(
    url_prefix: str,
    key_name: str,
    base64_key: str,
    expiration_time: datetime,
) -> str:
    """Gets the Signed cookie value for the specified URL prefix and configuration.

    Args:
        url_prefix: URL prefix to sign.
        key_name: name of the signing key.
        base64_key: signing key as a base64 encoded string.
        expiration_time: expiration time.

    Returns:
        Returns the Cloud-CDN-Cookie value based on the specified configuration.
    """
    encoded_url_prefix = base64.urlsafe_b64encode(
        url_prefix.strip().encode("utf-8")
    ).decode("utf-8")
    epoch = datetime.utcfromtimestamp(0)
    expiration_timestamp = int((expiration_time - epoch).total_seconds())
    decoded_key = base64.urlsafe_b64decode(base64_key)

    policy = f"URLPrefix={encoded_url_prefix}:Expires={expiration_timestamp}:KeyName={key_name}"

    digest = hmac.new(decoded_key, policy.encode("utf-8"), hashlib.sha1).digest()
    signature = base64.urlsafe_b64encode(digest).decode("utf-8")

    signed_policy = f"Cloud-CDN-Cookie={policy}:Signature={signature}"

    return signed_policy

Memvalidasi cookie yang ditandatangani

Proses validasi cookie bertanda tangan pada dasarnya sama dengan membuat cookie bertanda tangan. Misalnya, Anda ingin memvalidasi header cookie yang ditandatangani berikut:

Cookie: Cloud-CDN-Cookie=URLPrefix=URL_PREFIX:Expires=EXPIRATION:KeyName=KEY_NAME:Signature=SIGNATURE; Domain=media.example.com; Path=/; Expires=Tue, 20 Aug 2019 02:26:49 GMT; HttpOnly

Anda dapat menggunakan kunci rahasia yang diberi nama oleh KEY_NAME untuk membuat tanda tangan secara independen, lalu memvalidasi bahwa kunci tersebut cocok dengan SIGNATURE.

Menerbitkan cookie kepada pengguna

Aplikasi Anda harus membuat dan memberikan satu cookie HTTP kepada setiap pengguna (klien) yang berisi kebijakan yang ditandatangani dengan benar:

  1. Buat penanda tangan HMAC-SHA-1 di kode aplikasi Anda.

  2. Tanda tangani kebijakan menggunakan kunci yang dipilih, dengan memperhatikan nama kunci yang Anda tambahkan ke backend, seperti mySigningKey.

  3. Buat kebijakan cookie dengan format berikut, dengan memperhatikan bahwa nama dan nilainya peka huruf besar/kecil:

    Name: Cloud-CDN-Cookie
    Value: URLPrefix=$BASE64URLECNODEDURLORPREFIX:Expires=$TIMESTAMP:KeyName=$KEYNAME:Signature=$BASE64URLENCODEDHMAC
    

    Contoh header Set-Cookie:

    Set-Cookie: Cloud-CDN-Cookie=URLPrefix=aHR0cHM6Ly9tZWRpYS5leGFtcGxlLmNvbS92aWRlb3Mv:Expires=1566268009:KeyName=mySigningKey:Signature=0W2xlMlQykL2TG59UZnnHzkxoaw=; Domain=media.example.com; Path=/; Expires=Tue, 20 Aug 2019 02:26:49 GMT; HttpOnly
    

    Atribut Domain dan Path dalam cookie menentukan apakah klien mengirim cookie ke Cloud CDN.

Rekomendasi dan persyaratan

  • Tetapkan atribut Domain dan Path secara eksplisit agar cocok dengan domain dan awalan jalur tempat Anda ingin menayangkan konten yang dilindungi, yang mungkin berbeda dari domain dan jalur tempat cookie diterbitkan (example.com dibandingkan media.example.com atau /browse versus /videos).

  • Pastikan Anda hanya memiliki satu cookie dengan nama tertentu untuk Domain dan Path yang sama.

  • Pastikan Anda tidak mengeluarkan cookie yang bertentangan karena hal ini dapat mencegah akses ke konten di sesi browser lain (jendela atau tab).

  • Tetapkan tanda Secure dan HttpOnly jika berlaku. Secure memastikan bahwa cookie dikirim melalui koneksi HTTPS saja. HttpOnly mencegah pembuatan cookie tersedia untuk JavaScript.

  • Atribut cookie Expires dan Max-Age bersifat opsional. Jika Anda menghilangkannya, cookie ada saat sesi browser (tab, jendela) ada.

  • Pada saat cache terpenuhi atau cache tidak ditemukan, cookie yang ditandatangani akan diteruskan ke origin yang ditentukan di layanan backend. Pastikan Anda memvalidasi nilai cookie yang ditandatangani pada setiap permintaan sebelum menayangkan konten.