Private Docs

Body Signature 101 — เข้าใจกุญแจคู่แบบมือใหม่

อธิบายแบบง่ายที่สุด: กุญแจคู่คืออะไร เซ็นยังไง ตรวจยังไง ทำไมหลังบ้านตรวจได้ทั้งที่ไม่มี private key และทำไมคน copy curl ถึงปลอมไม่ได้ — พร้อม sequence diagram ทุกขั้น

อัปเดต: 2026-08-03

Body Signature 101 — เข้าใจกุญแจคู่แบบมือใหม่

อ่านเล่มนี้ก่อน ถ้ายังใหม่กับเรื่องกุญแจคู่ — เล่มนี้ตอบ 4 คำถามที่ทุกคนสงสัย ด้วยภาษาบ้านๆ ไม่มีศัพท์ crypto เกินจำเป็น

พออ่านจบแล้วค่อยไปต่อ: ภาพระบบ 3 ชั้น (ระบบเต็มเป็นยังไง) → แผน Implement (จะสร้างยังไง)

คำถามที่ 1: กุญแจคู่คืออะไร ต่างจากรหัสผ่านยังไง

ของที่เราคุ้นเคย (รหัสผ่าน, API key, secret) เป็น “ความลับก้อนเดียว” — ใครรู้ก้อนนี้ = ทำได้ทุกอย่าง ทั้งตรวจและปลอม ดังนั้นมันอยู่ใน browser ไม่ได้เด็ดขาด เพราะทุกอย่างใน browser เปิด DevTools ก็เห็น

กุญแจคู่ต่างออกไป — มันคือกุญแจ 2 ดอกที่เกิดมาคู่กันทางคณิตศาสตร์ และแบ่งหน้าที่กันเด็ดขาด:

อยู่ที่ไหนทำอะไรได้ถ้าคนอื่นได้ไป
private key (ตราประทับ)ในตู้เซฟที่ฝังอยู่ในเครื่องผู้ใช้ — สั่งให้ประทับได้ แต่หยิบตราออกมาดูไม่ได้ แม้แต่เจ้าของเองสร้างลายเซ็นเป็นไปไม่ได้ — browser ไม่มีปุ่มให้ดูดค่าออกมา (extractable: false)
public key (ตัวอย่างลายประทับ)ฝากไว้ที่ serverตรวจลายเซ็น — อย่างเดียวเท่านั้นไม่เป็นไรเลย มันไม่ใช่ความลับ เห็นแล้วก็ปลอมตราไม่ได้

นึกภาพ: ตราประทับอยู่ในตู้เซฟที่มีช่องสอดกระดาษ — ยื่นเอกสารเข้าไป เอกสารออกมาพร้อมตราประทับ แต่ไม่มีทางล้วงเอาตัวตราออกมา · ส่วนตัวอย่างลายประทับที่ฝากธนาคารไว้ ใช้เทียบได้อย่างเดียว จะเอาไปแกะสลักตราปลอมไม่ได้

ขั้นแรก: สร้างกุญแจแล้วฝาก public key (ทำครั้งเดียวต่อเครื่อง)

sequenceDiagram
    participant B as เครื่องผู้ใช้ (browser)
    participant S as Server

    Note over B: หลัง login สำเร็จครั้งแรก
    B->>B: สร้างกุญแจคู่ — private ถูกล็อกในตู้เซฟทันที<br/>หยิบออกมาไม่ได้ตลอดชีวิตของมัน
    B->>S: ส่งขึ้นไปฝากเฉพาะ public key (ไม่ใช่ความลับ หลุดก็ไม่เป็นไร)
    S->>S: ผูก public key เข้ากับบัญชีผู้ใช้นี้<br/>กติกา: คนแรกเท่านั้น — ใครมาขอทับทีหลังโดนปฏิเสธ + แจ้งเตือน
    S-->>B: เรียบร้อย
    Note over B,S: จากนี้ server มี "ตัวอย่างลายประทับ" ของเครื่องนี้แล้ว<br/>และความลับไม่เคยเดินทางออกจากเครื่องเลย

คำถามที่ 2: หนึ่ง request เซ็นยังไง ตรวจยังไง

ก่อนส่งทุก request, browser จะรวมข้อมูลสำคัญทั้งหมดเป็น “ข้อความ” ก้อนเดียว — เวลา + วิธีเรียก (GET/POST) + ที่อยู่ปลายทาง + เนื้อหา — แล้วยื่นให้ตู้เซฟประทับตรา ได้ลายเซ็นแนบไปกับ request

ฝั่ง server ไม่ได้ “เชื่อ” อะไรที่ client ส่งมาเลย — มันประกอบข้อความเดียวกันขึ้นมาเองจากของที่ได้รับจริง แล้วเช็คว่าลายเซ็นลงตัวกับข้อความนั้นไหม

sequenceDiagram
    participant B as Browser
    participant S as Server

    Note over B: ก่อนส่งทุก request
    B->>B: ① รวมข้อมูลเป็นข้อความเดียว<br/>เวลา + GET/POST + ที่อยู่ + เนื้อหา
    B->>B: ② ยื่นให้ตู้เซฟประทับตรา → ได้ลายเซ็น
    B->>S: ③ ส่ง request + ลายเซ็น + เวลา

    Note over S: ฝั่งตรวจ — ไม่เชื่ออะไรที่ส่งมา ประกอบเองหมด
    S->>S: ④ ประกอบข้อความเดียวกันจากของที่ได้รับจริง
    S->>S: ⑤ หยิบ public key ของผู้ใช้คนนี้ที่ฝากไว้
    S->>S: ⑥ เช็ค: ลายเซ็น + ข้อความ + public key ลงตัวไหม?<br/>(คำตอบมีแค่ จริง/เท็จ)

    alt ลงตัว
        S-->>B: ✅ ทำงานต่อตามปกติ
    else ไม่ลงตัว
        S-->>B: ❌ 401 — มีอะไรถูกแก้ หรือไม่ใช่เครื่องที่ลงทะเบียน
    end

จุดสำคัญที่ทำให้ระบบนี้แน่น: ทุกอย่างถูกมัดรวมในข้อความที่เซ็น — แก้เนื้อหา 1 ตัวอักษร, เปลี่ยนที่อยู่ปลายทาง, หรือเปลี่ยนเวลา = ข้อความเปลี่ยน = ลายเซ็นเดิมไม่ลงตัวทันที

คำถามที่ 3: server ไม่มี private key แล้ว “ตรวจ” ได้ยังไง? 🔑

นี่คือจุดที่มือใหม่งงที่สุด เพราะเราเคยชินกับการตรวจแบบรหัสผ่าน: “เอาความลับมาคำนวณซ้ำ แล้วเทียบว่าตรงกัน” — แบบนั้นคนตรวจต้องมีความลับด้วย

กุญแจคู่ไม่ได้ตรวจแบบนั้น — การตรวจคือ “เช็คคำตอบ” ไม่ใช่ “ทำโจทย์ซ้ำ”:

แบบรหัสผ่าน/HMAC (กุญแจร่วม)แบบกุญแจคู่ (ECDSA)
วิธีตรวจทำโจทย์ซ้ำด้วยความลับเดิม แล้วเทียบแทนค่าลงสมการ ด้วยของเปิดเผยล้วนๆ
คนตรวจต้องมีความลับตัวเดียวกับคนเซ็นแค่ public key
ผลข้างเคียงตรวจได้ = ปลอมได้ด้วยตรวจได้ แต่ปลอมไม่ได้

ดูตัวอย่างเลขจริงให้เห็นภาพ (แบบ RSA ซึ่งหลักเดียวกันแต่เลขดูง่ายกว่า):

กุญแจคู่ถูกสร้างมาให้:  ยกกำลัง d แล้วตามด้วยยกกำลัง e = กลับมาที่เดิมเสมอ

เซ็น (เครื่องผู้ใช้):   ลายเซ็น = ข้อความ ^ d     ← ใช้เลขลับ d
ตรวจ (server):        ลายเซ็น ^ e == ข้อความ ?   ← ใช้เลขเปิดเผย e เท่านั้น

สังเกตว่าบรรทัดตรวจไม่มี d โผล่มาเลย — server แค่เอาลายเซ็นยกกำลัง e (ที่ใครก็รู้) แล้วดูว่าได้ข้อความเดิมกลับมาไหม ถ้าได้ = ลายเซ็นนี้ต้องเกิดจาก d ตัวจริงเท่านั้น เพราะไม่มีทางอื่นที่จะทำให้สมการลงตัว

แล้วทำไม attacker ไม่คำนวณหา d จาก e ล่ะ? — เพราะมันคือโจทย์คณิตศาสตร์ที่โลกยังแก้ไม่ได้ ต้องลองประมาณ 2¹²⁸ ครั้ง (เอาคอมทั้งโลกช่วยกันก็ไม่เสร็จก่อนดวงอาทิตย์ดับ) · กำแพงจริงของระบบนี้คือคณิตศาสตร์ ไม่ใช่การซ่อนไฟล์

ECDSA ที่เราใช้จริงเปลี่ยนจาก “ยกกำลัง” เป็น “คูณจุดบนเส้นโค้ง” แต่หลักเหมือนกันทุกประการ: สมการตรวจถูกออกแบบให้ public key ทำหน้าที่แทน private key ได้ โดยไม่ต้องเห็นมันเลย

ไล่ดูว่าเขาได้อะไรไปบ้าง: cookie ✓ ลายเซ็น ✓ เนื้อหา ✓ เวลา ✓ — และไม่ได้อะไร: ตราประทับ (private key) ✗ เพราะมันถ่ายเอกสารไม่ติด อยู่ในตู้เซฟของเครื่องเหยื่อ

sequenceDiagram
    participant A as Attacker (copy curl มาครบมือ)
    participant S as Server

    Note over A: มี: cookie, ลายเซ็น, เนื้อหา, เวลา<br/>ไม่มี: private key (สิ่งเดียวที่ copy ไม่ได้)

    A->>S: ทางที่ 1 — ยิงซ้ำก้อนเดิมเป๊ะทุก byte ภายใน 5 นาที
    S-->>A: ⚠️ ผ่าน — ลายเซ็นเดิมยังลงตัวกับข้อความเดิม<br/>(ช่องเดียวที่เหลือ — มีแผนปิดด้วย replay-guard)

    A->>A: ทางที่ 2 — แก้เนื้อหา เช่น เปลี่ยนเลขบัญชีปลายทาง
    A->>S: ส่งด้วยลายเซ็นเดิม
    S->>S: ประกอบข้อความจากเนื้อหาใหม่ → เช็คสมการ → ไม่ลงตัว
    S-->>A: ❌ 401

    A->>A: ทางที่ 3 — รอเกิน 5 นาทีค่อยยิง
    S-->>A: ❌ 401 เวลาเก่าเกิน · จะแก้เวลา = ข้อความเปลี่ยน = ต้องเซ็นใหม่อีก

    A->>A: ทางที่ 4 — สร้างกุญแจคู่ของตัวเองมาเซ็นแทน
    A->>S: ส่งลายเซ็นจาก key ตัวเอง
    S->>S: หยิบ public key ของ "เหยื่อ" มาตรวจ (ไม่ใช่ของ attacker)<br/>จะเอา key ตัวเองไปทับของเหยื่อก็โดนปฏิเสธ (คนแรกชนะ)
    S-->>A: ❌ 401
    Note over A: ทางตันทุกทาง ยกเว้นยิงซ้ำก้อนเดิมช่วงสั้นๆ

สรุปหนึ่งประโยค: copy curl = ได้ “เอกสารใบที่ประทับตราแล้ว 1 ใบ” — เอาไปยื่นซ้ำได้ช่วงสั้นๆ (ช่องที่รู้และมีแผนปิด) แต่เขียนใบใหม่หรือแก้ข้อความแล้วประทับใหม่ไม่ได้เด็ดขาด เพราะตราจริงออกจากเครื่องเหยื่อไม่ได้

ศัพท์ที่เจอบ่อย (แปลไทยทีละคำ)

ศัพท์แปลว่า
keypair / กุญแจคู่private + public key ที่เกิดมาคู่กันทางคณิตศาสตร์
extractable: false / non-extractableตู้เซฟล็อกตาย — สั่งเซ็นได้ แต่อ่านค่ากุญแจออกมาไม่ได้ตลอดกาล
ECDSA P-256ชื่อสูตรคณิตศาสตร์ของกุญแจคู่ที่เราใช้ (ตระกูลเส้นโค้ง)
sign / เซ็นเอา private key ประทับตราลงบนข้อความ ได้ลายเซ็นออกมา
verify / ตรวจแทนค่า (ข้อความ + ลายเซ็น + public key) ลงสมการ ดูว่าลงตัวไหม
raw bytesเนื้อหาดิบเป๊ะๆ ทุก byte ตามที่ส่งจริง — server ใช้ตัวนี้ประกอบข้อความเอง ไม่เชื่อของแปรรูป
TOFU (first-key-wins)ลงทะเบียน key ได้เฉพาะคนแรก — คนมาทีหลังโดนปฏิเสธ + แจ้งเตือน
replayยิง request เดิมซ้ำทั้งก้อน — ช่องเดียวที่เหลือ ปิดด้วย replay-guard
oidรหัสประจำตัวผู้ใช้ใน token — server ใช้หา public key ว่าต้องหยิบของใคร
HMACระบบลายเซ็นแบบ “กุญแจร่วม” ที่ server คุยกันเองใช้ — ใช้กับ browser ไม่ได้

อ่านต่อ

  • ภาพระบบ 3 ชั้น (Target Architecture) — กุญแจคู่นี้อยู่ตรงไหนของระบบใหญ่ ทำงานร่วมกับ cookie และ encryption ยังไง เส้นทางเต็มขาเข้า-ขาออก
  • แผน Implement รอบ POC — wire contract จริงที่ freeze แล้ว, ไฟล์ที่จะเขียน, ลำดับงาน, เกณฑ์ตรวจรับ