Private Docs

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 KeyAPI Key (X-API-Key)
ใครตรวจAPIM gateway — ก่อนถึง backendbackend เอง (ApiKeyAuthFilter ใน Backend_Package)
ผูกกับอะไรsubscription (ผู้สมัครใช้ API — มักเป็น partner/ระบบภายนอก)caller service (มักเป็น service ภายในด้วยกัน)
เกิดมาเพื่อบริหาร: quota, analytics, ปิดก๊อกจาก portalgate: กัน 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 Keycaller (system)หยาบ — ต่อ servicebackend filter (ApiKeyAuthFilter)server → server
Subscription Keycaller (subscription)หยาบ — ต่อผู้สมัครใช้ APIAPIM gateway (ก่อนถึง backend)server/partner → APIM
JWTผู้ใช้ (คน)ละเอียด — ต่อ user + rolesbackend (validate กับ Entra)ทุกขาที่มี user
HMACintegrity + caller โดยนัยต่อ requestbackendserver → server
Body signatureเครื่องของผู้ใช้ + integrityต่อ request ต่อ userbackend 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)