API Key & Subscription Key 101 — string ลับฝั่ง server-to-server
อธิบายแบบง่ายที่สุด: API Key กับ Subscription Key คืออะไร ตอบคำถามอะไร (ใครโทรมา) ต่างกันตรงไหน (ตรวจที่ backend vs ตรวจที่ APIM gateway), dual-key rotation แบบไม่มี downtime, best practice — และเหตุผลเดียวที่ทำให้ทั้งคู่ใช้ได้เฉพาะ server-to-server ห้ามโผล่ขา browser
อัปเดต: 2026-08-06
API Key & Subscription Key 101 — string ลับฝั่ง server-to-server
สองตัวนี้หน้าตาเหมือนกันเป๊ะ (string ลับแนบไปกับ request) และคนสับสนกันบ่อยที่สุด — จุดต่างจริงคือตรวจคนละที่ ตอบคนละคำถาม: API Key คือ “บัตรพนักงาน” ที่ backend ตรวจเองที่ประตูห้อง ส่วน Subscription Key คือ “บัตรสมาชิก” ที่ยามตรวจตั้งแต่ประตูรั้ว (APIM gateway) — request ที่ไม่มีบัตรถูกปัดตกก่อนถึงตัวบ้านด้วยซ้ำ
ทั้งคู่เป็นเครื่องมือขา server-to-server เท่านั้น (ภาพระบบข้อ 6 — มติ 18) ห้ามโผล่ขา browser — เหตุผลเดียวจบทุกข้อ อยู่ในข้อ 5
TL;DR — อ่าน 30 วิ
- API Key = string ลับที่ caller แนบมากับ request (
X-API-Key) เพื่อบอกว่า “ฉันคือ service/ระบบตัวนี้” — backend ตรวจเอง (ApiKeyAuthFilterในBackend_Package) - Subscription Key = key ที่ Azure APIM ออกให้ต่อ “ผู้สมัครใช้ API” (header
Ocp-Apim-Subscription-Key) — ตรวจที่ gateway ก่อนถึง backend · จุดแข็งจริงไม่ใช่ความลับ แต่คือการบริหารผู้เรียก: analytics, quota/rate ราย caller, ปิดก๊อกจาก portal ได้ทันที - ทั้งคู่ตอบคำถามเดียวกัน: “ใครโทรมา (ระดับ system) และอนุญาตไหม” — ไม่ใช่ตัวตนผู้ใช้ (นั่นคือ JWT), ไม่กันแก้ข้อมูล (นั่นคือ HMAC/ลายเซ็น), ไม่กันอ่าน (นั่นคือ encryption)
- ใช้ได้เฉพาะ server → server เพราะเป็น shared secret — ฝั่งที่ถือ key ต้องเก็บเป็นความลับได้จริง (Key Vault) ซึ่ง browser ทำไม่ได้
- ในระบบเรา: กลไกมีครบแล้วทั้งคู่ (
ApiKeyAuthFilter+ subscription ที่ APIM) — ไม่ต้องสร้างใหม่
1. API Key — บัตรพนักงานที่ backend ตรวจเอง
sequenceDiagram
autonumber
participant A as Service A (caller)
participant KV as Key Vault
participant B as Service B (มี ApiKeyAuthFilter)
A->>KV: โหลด key ตอน start (ไม่ hardcode)
A->>B: request + header X-API-Key: <key>
B->>B: เทียบกับ key ที่ตัวเองถือ (จาก KV เช่นกัน)
alt ตรง
B-->>A: 200 — รู้แล้วว่า caller คือใคร ปล่อยเข้า
else ไม่ตรง / ไม่ส่งมา
B-->>A: 401 — ไม่รู้จัก ปัดตก
end
ไม่มี crypto ไม่มีการคำนวณ — แค่ “จำรหัสตรงกันไหม” ความง่ายนี้คือทั้งจุดแข็ง (เร็ว, ไม่มีอะไรพัง) และจุดอ่อน (ใครเห็น key = ปลอมเป็น caller นั้นได้เต็มตัว) จึงต้องคุมที่ “ใครเห็น key ได้บ้าง” อย่างเดียวเลย
ตัวอย่างจริงในระบบ: RedisUserInfoMiddleware (lib กลาง) เวลา cache miss จะ HTTP fallback ไปถาม UserService — แนบ X-API-Key เพื่อบอกว่า “ฉันเป็น service ภายในด้วยกัน” ก่อนเข้า endpoint ภายใน
2. Subscription Key — บัตรสมาชิกที่ประตูรั้ว (เครื่องมือ “บริหาร” ไม่ใช่ “ป้องกัน”)
sequenceDiagram
autonumber
participant P as Partner / Server caller
participant APIM as APIM Gateway
participant BE as Backend Service
P->>APIM: request + Ocp-Apim-Subscription-Key
APIM->>APIM: key นี้เป็นของ subscription ไหน?
alt ไม่รู้จัก / ถูก revoke
APIM-->>P: 401 — ไม่ถึง backend เลย
else รู้จัก
APIM->>APIM: เช็ค rate-limit / quota ของ subscription นี้
alt ยิงถี่เกิน rate-limit
APIM-->>P: 429 Too Many Requests
else ใช้เกินโควต้ารวม (quota)
APIM-->>P: 403 Out of call volume quota
else ผ่าน
APIM->>BE: forward (backend ค่อยตรวจชั้นของตัวเอง: JWT / X-API-Key / HMAC)
BE-->>P: response
end
end
สิ่งที่ได้จากการตรวจที่ประตูรั้ว:
| ความสามารถ | ใช้ทำอะไรจริง |
|---|---|
| ระบุ caller ต่อ subscription | รายงาน/analytics ต่อผู้เรียก — “partner X เรียก 2 ล้านครั้ง/เดือน” |
| Quota / Rate limit ราย caller | กัน caller ตัวเดียวถล่มระบบ — จำกัดที่รั้ว ไม่เปลืองแรง backend |
| ปิดก๊อกราย caller ทันที | revoke subscription จาก portal — มีผลทันทีทุก API โดยไม่แตะ backend/ไม่ deploy |
| แยก tier การใช้งาน (Product) | จัดกลุ่ม API เป็น product — ให้สิทธิ์ต่างกันต่อผู้สมัคร (เช่น partner เห็นเฉพาะชุด read-only) |
3. ต่างกันตรงไหนกันแน่ — จุดต่างจริงมี 3 ข้อ
| Subscription Key | API Key (X-API-Key) | |
|---|---|---|
| ใครตรวจ | APIM gateway — ก่อนถึง backend | backend เอง (ApiKeyAuthFilter ใน Backend_Package) |
| ผูกกับอะไร | subscription (ผู้สมัครใช้ API — มักเป็น partner/ระบบภายนอก) | caller service (มักเป็น service ภายในด้วยกัน) |
| เกิดมาเพื่อ | บริหาร: quota, analytics, ปิดก๊อกจาก portal | gate: กัน endpoint ภายในจาก caller แปลกหน้า |
| ทำงานเมื่อ | traffic วิ่งผ่าน APIM เท่านั้น | ทุก hop ที่ backend ตรวจ — รวม service→service ตรงที่ไม่ผ่าน APIM |
ใช้คู่กันได้และมักใช้คู่กันจริง: partner → Subscription Key ที่รั้ว (บริหาร/โควต้า) → API Key/JWT ที่บ้าน (gate จริง) — คนละชั้นคนละหน้าที่ ไม่แทนกัน
4. ทั้งคู่ตอบคำถามไหน — และไม่ตอบคำถามไหน
| คำถาม | Key พวกนี้ตอบไหม | ตัวที่ตอบจริง |
|---|---|---|
| ใครโทรมา (ระดับ system: “นี่คือ NotificationService” / “partner X”) | ✅ | — |
| ใครโทรมา (ระดับ ผู้ใช้: “นี่คือคุณสมชาย”) | ❌ | JWT (ชั้น 1) |
| request ถูกแก้กลางทางไหม | ❌ | HMAC (server↔server) / body signature (browser) |
| คนกลางอ่านเนื้อหาได้ไหม | ❌ | TLS + field encryption |
| ยิงซ้ำ (replay) ได้ไหม | ❌ — key เดิมใช้ซ้ำได้เสมอโดยนิยาม | timestamp + nonce |
จุดที่คนสับสนบ่อย: เห็น request มี key แล้วคิดว่า “ปลอดภัยแล้ว” — จริงๆ มันแค่บอกว่าประตูรู้ว่าใครเข้ามา เนื้อ request ยังปลอม/แก้/อ่านได้หมดถ้าไม่มีชั้นอื่นประกบ · request ที่ผ่าน APIM มาแล้ว backend ยังต้องตรวจชั้นของตัวเองครบเหมือนเดิมทุกตัว ห้ามถือว่า “ผ่าน APIM มา = เชื่อได้” · ขา server-to-server ของเราจึงมี 3 ตัวทำงานร่วมกัน: API Key (ใครโทรมา) + HMAC (ของไม่ถูกแก้) + AES field encryption (คนกลางอ่านไม่ได้)
5. ทำไมใช้กับ browser ไม่ได้ — เหตุผลเดียว จบทุกข้อ
ค่าใดก็ตามที่ browser ต้องส่ง มันต้องอยู่ใน bundle/config ที่ browser โหลดได้ = เปิด DevTools เห็น = กลายเป็นค่า public โดยนิยาม:
- attacker copy key ไปแนบเหมือน app จริง → การป้องกันเป็นศูนย์เป๊ะ (ไม่ใช่ลดลง — ศูนย์)
- แต่ภาระเพิ่มเต็มๆ: ต้อง rotate, ต้อง sync ทุก env, rotate ทีหน้าเว็บพังที
- นี่คือเหตุผลของมติ 7 (ตัด key ทั้งสองจากขา browser) และมติ 18 (นำกลับเข้า architecture เฉพาะขา server) — ขา browser การพิสูจน์ “เครื่องจริง” ทำด้วย body signature (กุญแจคู่ non-extractable) ซึ่งไม่มี secret ให้ขโมย (Body Signature 101) และตัวตนผู้ใช้ผูกผ่าน cookie → resolve-session → JWT (ชั้น 1) ซึ่งละเอียดกว่า key ระดับ app ที่ทุกคนใช้ร่วมกันคนละโลก
- ตรวจโค้ดจริง 03/08: FE ทั้ง 4 repo (รอบสแกนขา browser — คนละชุดกับ “4 repo scope POC” ในแผน Implement) ไม่เคยถือ key พวกนี้อยู่แล้ว และ authentication ไม่เคยพึ่งมัน — การไม่ใช้จึงไม่ทำให้ security ลดแม้แต่นิดเดียว
กติกาตายตัว: เห็น X-API-Key หรือ Ocp-Apim-Subscription-Key ในโค้ด FE / ใน Network tab ของ browser เมื่อไหร่ = bug security ทันที ไม่มีข้อยกเว้น
ถ้าอนาคตมี partner/ระบบภายนอกมาเรียก API เรา → นั่นคือที่ของ Subscription Key: เปิด product ให้ subscribe, ได้ quota + ปิดก๊อกราย partner ฟรีจาก portal — โดยไม่แตะขา browser เลย
6. Best practice เมื่อใช้ (ขา server)
| กติกา | เพราะ |
|---|---|
| key อยู่ Key Vault เท่านั้น — ไม่อยู่ใน appsettings, ไม่อยู่ใน source code, ไม่อยู่ใน pipeline variable แบบ plaintext | จุดรั่วอันดับหนึ่งของ API Key คือ commit ติดลง git |
| แยก key ต่อ caller — ไม่ใช้ key กลางตัวเดียวทั้งระบบ | เพิกถอน/rotate ราย caller ได้โดยไม่กระทบตัวอื่น + log บอกได้ว่าใครเป็นใคร |
| ห้าม log ค่า key — log แค่ “caller ไหน ผ่าน/ไม่ผ่าน” | log อ่านได้กว้างกว่า KV เสมอ |
| เทียบ key แบบ constant-time | กัน timing attack — ⚠️ ของจริงวันนี้ยังไม่ใช่: InMemoryApiKeyValidator ใน Backend_Package ใช้ dictionary lookup ธรรมดา (ตัวที่เทียบแบบ fixed-time มีเฉพาะฝั่ง HMAC) — เป็น gap ที่บันทึกไว้ รอรอบที่แตะ lib ส่วนนี้ |
| rotate มีแผนตั้งแต่แรก — ช่วง overlap รับ 2 key พร้อมกัน | rotate แบบ big-bang = ทุก caller พังพร้อมกัน — ดู dual-key ข้างล่าง |
Dual-key rotation — ของแถมที่ดีที่สุดของ APIM
ทุก subscription มี primary + secondary key และ APIM รับทั้งคู่เสมอ ⇒ rotate โดยไม่มี downtime:
1. caller ใช้ primary อยู่ → ระบบปกติ
2. caller สลับไปใช้ secondary → ยังผ่าน (APIM รับทั้งคู่)
3. regenerate primary (ค่าเก่าตายทันที) → ไม่มีใครใช้อยู่แล้ว ไม่มีใครพัง
4. รอบถัดไปสลับกลับ: ใช้ primary ใหม่ → regen secondary
หลักคิดนี้ (มี key สำรองรับคู่ระหว่าง overlap) คือ pattern เดียวกับที่เราต้องทำเองมือถ้าใช้ API Key ฝั่ง backend — APIM ทำให้ฟรี · จดไว้เป็นตัวอย่างเวลาต้องออกแบบ rotation ของ secret ตัวอื่น (รวมถึง encryption key ของชั้น 3 ที่ใช้ kid — หลักการเดียวกัน)
7. สรุปเทียบเครื่องมือทั้งตระกูล — ใครทำหน้าที่อะไร
| เครื่องมือ | ระบุตัว | ระดับ | ตรวจที่ | ใช้ขาไหน |
|---|---|---|---|---|
| API Key | caller (system) | หยาบ — ต่อ service | backend filter (ApiKeyAuthFilter) | server → server |
| Subscription Key | caller (subscription) | หยาบ — ต่อผู้สมัครใช้ API | APIM gateway (ก่อนถึง backend) | server/partner → APIM |
| JWT | ผู้ใช้ (คน) | ละเอียด — ต่อ user + roles | backend (validate กับ Entra) | ทุกขาที่มี user |
| HMAC | integrity + caller โดยนัย | ต่อ request | backend | server → server |
| Body signature | เครื่องของผู้ใช้ + integrity | ต่อ request ต่อ user | backend filter (ใหม่ — POC) | browser → backend |
8. กติกาสรุป
- API Key = gate ที่ประตูห้อง (backend ตรวจเอง — กัน endpoint ภายในจาก caller แปลกหน้า) · Subscription Key = บริหารที่ประตูรั้ว (quota / analytics / revoke จาก portal) — คนละชั้น ใช้คู่กันได้ ไม่แทนกัน
- ทั้งคู่ไม่ป้องกันเนื้อ request — ผ่านมาแล้ว backend ตรวจชั้นตัวเอง (JWT/HMAC/encryption) ครบเหมือนเดิม ไม่มีข้อยกเว้น
- ขา browser ไม่ใช้ทั้งคู่ — เห็นในโค้ด FE เมื่อไหร่ = bug security
- เมื่อไหร่ใช้ตัวไหน: caller ภายนอก/partner ผ่าน APIM = Subscription Key (+ชั้น backend ตามปกติ) · server-to-server ภายใน cluster ที่ไม่ผ่าน APIM = API Key + HMAC ตามเดิม
อ่านต่อ
- Body Signature 101 — ขา browser พิสูจน์ “เครื่องจริง” ด้วยกุญแจคู่แทน key พวกนี้ยังไง
- Encryption Field 101 — ตัวที่ตอบคำถาม “กันอ่าน” ที่ key พวกนี้ไม่ตอบ
- ภาพระบบ 3 ชั้น (Target Architecture) — ข้อ 6 = ที่อยู่ทางการของขา server-to-server (มติ 18)