Protokol Web Bot Auth (WBA) memungkinkan Anda membuktikan identitas agen atau bot secara kriptografis menggunakan Tanda Tangan Pesan HTTP (RFC 9421).
Saat Anda menandatangani permintaan keluar, layanan seperti Google Cloud Fraud Defense (evolusi reCAPTCHA) dapat memverifikasi agen Anda secara langsung tanpa hanya mengandalkan alamat IP atau string User-Agent.
Untuk mengautentikasi agen Anda dengan WBA, selesaikan tugas berikut:
- Buat pasangan kunci Ed25519 dan format kunci publik sebagai Set Kunci Web JSON (JWK).
- Hosting direktori kunci publik Anda di endpoint HTTPS yang dikenal.
- Tanda tangani setiap permintaan HTTP keluar dengan kunci pribadi Anda.
- Opsional: Tandatangani header
Agent-Contexteksperimental untuk mengisolasi reputasi di seluruh tenant yang menggunakan kunci penandatanganan yang sama.
Membuat pasangan kunci Ed25519
WBA memerlukan pasangan kunci asimetris Ed25519 (RFC 8032). Anda merahasiakan kunci pribadi untuk menandatangani permintaan HTTP keluar, dan Anda memublikasikan kunci publik dalam JSON Web Key (JWK) Set (RFC 7517).
Membuat pasangan kunci dengan OpenSSL dan jwker
Anda dapat membuat pasangan kunci Ed25519 dengan OpenSSL dan mengonversi kunci publik menjadi
JWK menggunakan alat jwker open source:
Buat kunci pribadi Ed25519 dalam format PEM:
openssl genpkey -algorithm ed25519 -out private-key.pemEkstrak kunci publik dalam format PEM:
openssl pkey -in private-key.pem -pubout -out public-key.pemJika Anda belum menginstal
jwker, instal dengan mengikuti petunjuk penginstalanjwker.Konversikan kunci publik PEM ke file JWK menggunakan
jwker:jwker public-key.pem public-key.jwkPerintah ini menulis satu objek JWK ke
public-key.jwk:{ "kty": "OKP", "crv": "Ed25519", "x": "16JuFxlu7PYOFULk_W6t3sZO4NF8Yj-2QpBxCAiXQfs" }Gabungkan objek JWK di dalam array
keysSet JWK (jwks.json) dan tambahkan kolomalg,use, dankidyang diperlukan:{ "keys": [ { "kty": "OKP", "crv": "Ed25519", "alg": "EdDSA", "use": "sig", "kid": "OE_x89FAZvHnzOd9gMU0zFW-AhHTESAL5cV5aL0dqyw", "x": "16JuFxlu7PYOFULk_W6t3sZO4NF8Yj-2QpBxCAiXQfs" } ] }
Referensi kolom Set JWK Publik
Setiap objek kunci publik dalam array keys JWK Set Anda mendukung kolom berikut:
| Kolom | Nilai | Deskripsi |
|---|---|---|
kty |
"OKP" |
Jenis kunci. Harus berupa "OKP" (Octet Key Pair) seperti yang ditentukan dalam RFC 8037. |
crv |
"Ed25519" |
Kurva kriptografis. Harus berupa "Ed25519". |
alg |
"EdDSA" |
Algoritma yang dimaksudkan untuk digunakan dengan kunci. Harus berupa "EdDSA". |
use |
"sig" |
Tujuan penggunaan kunci publik. Tetapkan ke "sig" (tanda tangan). |
x |
"BASE64URL_PUBLIC_KEY" |
Kunci publik Ed25519 mentah 32 byte, yang dienkode menggunakan base64url tanpa padding (RFC 4648 Bagian 5). |
kid |
"KEY_ID" |
ID kunci unik yang cocok dengan parameter keyid dalam permintaan bertanda tangan Anda. Gunakan thumbprint JWK SHA-256 RFC 7638 (base64url tanpa padding) untuk kompatibilitas lintas vendor. |
nbf |
UNIX_SECONDS |
Opsional. Stempel waktu Unix dalam detik yang menunjukkan waktu sebelum kunci tidak valid. |
exp |
UNIX_SECONDS |
Opsional. Stempel waktu Unix dalam detik setelah masa berlaku kunci berakhir. Pemverifikasi menolak kunci yang sudah tidak berlaku. |
Menghosting direktori kunci publik Anda
Pemverifikasi mengambil kunci publik Anda dari endpoint HTTPS yang dikenal di domain agen Anda.
Persyaratan endpoint dan header
Untuk menghosting direktori kunci, konfigurasikan server web Anda agar memenuhi persyaratan berikut:
- Jalur URL: Tayangkan JSON Set JWK di
https://YOUR_AGENT_DOMAIN/.well-known/http-message-signatures-directory. - Keamanan transport: Tayangkan endpoint melalui HTTPS di port default
443. Jangan mengalihkan permintaan ke host atau skema yang berbeda. - Jenis konten: Tetapkan header respons
Content-Typekeapplication/http-message-signatures-directory+json(atauapplication/json). - Caching: Tetapkan header respons
Cache-Controldengan perintahmax-age(misalnya,Cache-Control: public, max-age=3600). - Rotasi kunci: Sebelum Anda menandatangani permintaan dengan kunci pribadi baru, tambahkan JWK publiknya ke array
keysdan tunggu hingga TTLCache-ControlAnda berakhir. Hapus kunci yang dicabut atau tidak berlaku dari arraykeys.
Contoh berikut menunjukkan respons HTTP dari
GET /.well-known/http-message-signatures-directory:
HTTP/1.1 200 OK
Content-Type: application/http-message-signatures-directory+json
Cache-Control: public, max-age=3600
{
"keys": [
{
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"use": "sig",
"kid": "OE_x89FAZvHnzOd9gMU0zFW-AhHTESAL5cV5aL0dqyw",
"x": "16JuFxlu7PYOFULk_W6t3sZO4NF8Yj-2QpBxCAiXQfs"
}
]
}
Menandatangani permintaan HTTP keluar
Untuk mengautentikasi permintaan HTTPS keluar dengan WBA, lampirkan tiga header yang diperlukan: Signature-Agent, Signature-Input, dan Signature.
Header HTTP
Signature-Agent: Menentukan origin HTTPS direktori kunci Anda sebagai string yang dikutip:Signature-Agent: "https://agent.example.com"Signature-Input: Memetakan label tanda tangan (sepertisig1) ke daftar dalam komponen HTTP yang tercakup dan parameter metadata tanda tangan:Signature-Input: sig1=("@authority" "signature-agent");created=1772400000;expires=1772400300;keyid="OE_x89FAZvHnzOd9gMU0zFW-AhHTESAL5cV5aL0dqyw";tag="web-bot-auth"Sertakan komponen dan parameter yang tercakup berikut:
- Komponen yang tercakup:
"@authority": Wajib diisi. Mengikat tanda tangan ke host tujuan (misalnya,www.example.com)."signature-agent": Wajib diisi. Mengikat tanda tangan ke headerSignature-AgentAnda.
- Parameter metadata:
created: Stempel waktu Unix saat ini dalam detik (bilangan bulat).expires: Stempel waktu Unix masa berlaku dalam detik (bilangan bulat). Tetapkanexpireske maksimal 300 detik (5 menit) setelahcreated.keyid: Stringkidkunci publik Anda dari JWK Set Anda.tag: Harus"web-bot-auth".
- Komponen yang tercakup:
Signature: Memetakan label tanda tangan yang sama (sig1) ke tanda tangan Ed25519 berenkode base64 yang diformat sebagai urutan byte RFC 8941 (digabungkan dalam titik dua:):Signature: sig1=:BASE64_ENCODED_SIGNATURE:
Membuat dasar tanda tangan
Sebelum menandatangani, buat string dasar tanda tangan kanonis yang ditentukan dalam
RFC 9421 Bagian 2.5. Setiap komponen yang tercakup muncul di barisnya sendiri dalam huruf kecil, diikuti dengan baris @signature-params tanpa newline di akhir:
"@authority": www.example.com
"signature-agent": "https://agent.example.com"
"@signature-params": ("@authority" "signature-agent");created=1772400000;expires=1772400300;keyid="OE_x89FAZvHnzOd9gMU0zFW-AhHTESAL5cV5aL0dqyw";tag="web-bot-auth"
Tanda tangani byte UTF-8 dari string dasar tanda tangan ini menggunakan kunci pribadi Ed25519 Anda.
Contoh Go
Contoh berikut memuat file private-key.pem yang dihasilkan oleh OpenSSL dan
menandatangani *http.Request keluar menggunakan paket crypto/ed25519 standar Go
dan github.com/dunglas/httpsfv:
package main
import (
"crypto/ed25519"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"net/http"
"strings"
"time"
"github.com/dunglas/httpsfv"
)
// loadPrivateKey parses a PKCS#8 PEM-encoded Ed25519 private key
// generated by OpenSSL.
func loadPrivateKey(pemBytes []byte) (ed25519.PrivateKey, error) {
// Decode the PEM armor to get the raw DER bytes.
block, _ := pem.Decode(pemBytes)
if block == nil {
return nil, fmt.Errorf("failed to decode PEM block")
}
// OpenSSL generates private keys in PKCS#8 format by default.
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
// Confirm that the parsed key is an Ed25519 private key.
privKey, ok := key.(ed25519.PrivateKey)
if !ok {
return nil, fmt.Errorf("not an Ed25519 private key")
}
return privKey, nil
}
// signRequestEd25519 signs req with WBA headers.
func signRequestEd25519(
req *http.Request,
privKey ed25519.PrivateKey,
keyID string,
) error {
const (
// sigLabel links the Signature-Input and Signature entries.
sigLabel = "sig1"
// agentURL is the HTTPS origin where your JWK Set is hosted.
agentURL = "https://agent.example.com"
)
// 1. Set the Signature-Agent header as a quoted string.
sigAgentHeader := fmt.Sprintf("%q", agentURL)
req.Header.Set("Signature-Agent", sigAgentHeader)
// 2. List the HTTP components to cover in the signature.
// Always cover "@authority" (the target host) and "signature-agent".
authority := strings.ToLower(req.URL.Host)
items := []httpsfv.Item{
httpsfv.NewItem("@authority"),
httpsfv.NewItem("signature-agent"),
}
baseLines := []string{
fmt.Sprintf(`"@authority": %s`, authority),
fmt.Sprintf(`"signature-agent": %s`, sigAgentHeader),
}
// If the optional Agent-Context header is set, sign it too.
if agentCtx := req.Header.Get("Agent-Context"); agentCtx != "" {
items = append(items, httpsfv.NewItem("agent-context"))
line := fmt.Sprintf(`"agent-context": %s`, agentCtx)
baseLines = append(baseLines, line)
}
// 3. Set the signature metadata parameters.
// Set expires to 300 seconds (5 minutes) after created.
now := time.Now().Unix()
params := httpsfv.NewParams()
params.Add("created", now)
params.Add("expires", now+300)
params.Add("keyid", keyID)
params.Add("tag", "web-bot-auth")
// Serialize the covered components and parameters for Signature-Input.
innerList := httpsfv.InnerList{
Items: items,
Params: params,
}
sigParamsStr, err := httpsfv.Marshal(innerList)
if err != nil {
return err
}
// 4. Build the RFC 9421 signature base string.
// Join each covered component line and @signature-params with "\n".
// Don't add a trailing newline after the @signature-params line.
paramLine := fmt.Sprintf(`"@signature-params": %s`, sigParamsStr)
baseLines = append(baseLines, paramLine)
sigBase := strings.Join(baseLines, "\n")
// 5. Sign the UTF-8 bytes of the signature base with your private key.
sigBytes := ed25519.Sign(privKey, []byte(sigBase))
sigB64 := base64.StdEncoding.EncodeToString(sigBytes)
// 6. Attach the Signature-Input and Signature headers to the request.
req.Header.Set("Signature-Input", fmt.Sprintf("%s=%s", sigLabel, sigParamsStr))
req.Header.Set("Signature", fmt.Sprintf("%s=:%s:", sigLabel, sigB64))
return nil
}
Menyertakan header Agent-Context eksperimental
Platform multi-tenant (seperti penyedia browser yang dihosting atau runtime agen cloud) sering kali menandatangani permintaan keluar dari beberapa agen atau tenant pelanggan menggunakan satu kunci penandatanganan bersama. Secara default, WBA hanya mengidentifikasi domain
penandatanganan (Signature-Agent), sehingga semua traffic yang ditandatangani oleh kunci tersebut memiliki reputasi yang sama.
Saat Anda menyertakan dan menandatangani header Agent-Context, reCAPTCHA
dapat mengevaluasi reputasi secara terpisah di berbagai produk, project, pengguna, atau
sesi yang menggunakan kunci penandatanganan yang sama di platform Anda.
Memformat header Agent-Context
Format Agent-Context sebagai
RFC 8941 Structured Field Dictionary
dengan setiap kunci berupa token huruf kecil dan setiap nilai berupa string ASCII yang dikutip:
Agent-Context: product="ResearchBot", project="prj_89a0f", user="usr_29df3", session="sess_88b1"
Semua kolom bersifat opsional. Sertakan hanya kolom yang berlaku untuk arsitektur Anda:
| Kolom | Deskripsi | Contoh |
|---|---|---|
product |
Nama aplikasi atau nama komersial agen yang membuat permintaan. | product="ResearchBot" |
project |
ID akun, ruang kerja, atau project tenant dengan nama samaran di platform host. | project="prj_89a0f" |
user |
ID pseudonim (seperti UUID buram atau hash dengan salt) untuk pelanggan atau pengguna akhir. Jaga agar nilai ini tetap stabil di seluruh permintaan dari pengguna yang sama untuk membangun reputasi, dan jangan pernah menyertakan PII yang sebenarnya. | user="usr_29df3" |
session |
ID sementara dan buram untuk satu tugas, eksekusi alur kerja, atau sesi penjelajahan. | session="sess_88b1" |
Menandatangani header Agent-Context
reCAPTCHA hanya menggunakan Agent-Context untuk isolasi reputasi saat
Anda menyertakan "agent-context" dalam tanda tangan WBA Anda. Header Agent-Context yang tidak ditandatangani akan diabaikan.
Untuk menandatangani header Agent-Context, selesaikan langkah-langkah berikut:
Tetapkan header
Agent-Contextpada*http.Requestkeluar Anda sebelum memanggilsignRequestEd25519:req.Header.Set("Agent-Context", `product="ResearchBot", project="prj_89a0f", user="usr_29df3", session="sess_88b1"`)Sertakan
"agent-context"dalam daftar komponenSignature-Inputdan string dasar tanda tangan (ditangani secara otomatis oleh contoh Go):"@authority": www.example.com "signature-agent": "https://agent.example.com" "agent-context": product="ResearchBot", project="prj_89a0f", user="usr_29df3", session="sess_88b1" "@signature-params": ("@authority" "signature-agent" "agent-context");created=1772400000;expires=1772400300;keyid="OE_x89FAZvHnzOd9gMU0zFW-AhHTESAL5cV5aL0dqyw";tag="web-bot-auth"
Contoh berikut menunjukkan permintaan HTTP lengkap yang ditandatangani dengan Agent-Context:
GET /api/data HTTP/1.1
Host: www.example.com
Signature-Agent: "https://agent.example.com"
Agent-Context: product="ResearchBot", project="prj_89a0f", user="usr_29df3", session="sess_88b1"
Signature-Input: sig1=("@authority" "signature-agent" "agent-context");created=1772400000;expires=1772400300;keyid="OE_x89FAZvHnzOd9gMU0zFW-AhHTESAL5cV5aL0dqyw";tag="web-bot-auth"
Signature: sig1=:BASE64_ENCODED_SIGNATURE:
Langkah berikutnya
Menandatangani permintaan Anda dengan WBA membuktikan kontrol kriptografis atas domain agen Anda, tetapi situs juga memerlukan metadata tentang siapa yang mengoperasikan agen Anda dan apa tujuannya.
- Pelajari cara pemilik situs menggunakan sinyal Agen Terverifikasi:
- Pantau traffic agen di dasbor Ringkasan agen.
- Konfigurasikan kebijakan verifikasi menggunakan
variabel kondisi
verified_bots.
- Baca spesifikasi protokol: