API key กับ OAuth แก้ปัญหาคนละเรื่องกัน และการเลือกระหว่างสองอย่างนี้เริ่มจากคำถามเดียว: ในภาพนี้มีบุคคลที่สามหรือไม่

ถ้าเซิร์ฟเวอร์ของคุณเรียก API ของคนอื่นในนามของตัวเอง คุณต้องใช้ key ถ้าแอปของคนอื่นเรียก API ของคุณในนามผู้ใช้ของคุณ คุณต้องใช้ OAuth เมื่อไม่มีบุคคลที่สาม OAuth เพิ่มพิธีการเข้ามาโดยไม่ได้เพิ่มความปลอดภัย

ต่อจากนี้: ทั้งสองต่างกันตรงไหนจริง ๆ, OAuth เปลี่ยนอะไรไปบ้างในไม่กี่ปีที่ผ่านมา และคุณสมบัติหนึ่งของ API key ที่แทบไม่มีใครเขียนถึง ทั้งที่มันเป็นตัวกำหนดว่าคุณจะอยู่กับมันอย่างไรบนระบบจริง

API key: สายอักขระหนึ่งบรรทัดที่บอกว่า "โปรแกรมนี้ได้รับอนุญาต"

key คือสายอักขระยาวและสุ่ม ที่คุณใส่ไว้ในส่วนหัวของคำขอ

X-Api-Key: lix_live_9f2c...

เซิร์ฟเวอร์หา key เจอ รู้ว่าเป็นของบัญชีใด แล้วปล่อยคำขอผ่านไป กลไกทั้งหมดมีเท่านี้

คุณสมบัติที่ตามมาจากตรงนั้น

  • key ไม่หมดอายุด้วยตัวเอง มันอยู่ไปจนกว่าจะมีคนเพิกถอน
  • key ระบุตัวโปรแกรม ไม่ใช่ตัวคน ใครในทีมเป็นคนเรียกนั้นตามปกติมองไม่เห็น
  • key อนุญาตทุกอย่างที่บัญชีทำได้ เว้นแต่คุณจำกัดสิทธิ์ไว้ต่างหาก
  • key คือความลับเพียงอย่างเดียว รั่วเมื่อไร สิทธิ์เข้าถึงก็รั่วเมื่อนั้น ไม่มีขั้นกลาง

OAuth: ข้อตกลงว่าด้วยการยินยอม

OAuth 2.0 (RFC 6749) ตอบคำถามอีกแบบ มีผู้เกี่ยวข้องสี่ฝ่าย ได้แก่ เจ้าของข้อมูล แอปที่อยากได้ข้อมูล เซิร์ฟเวอร์ให้สิทธิ์ และเซิร์ฟเวอร์ที่เก็บข้อมูลไว้

ลำดับคร่าว ๆ เป็นแบบนี้ แอปส่งผู้ใช้ไปยังหน้าเว็บของบริการนั้นเอง ผู้ใช้เห็นว่าใครขออะไร แล้วกดยินยอม แอปได้รับ code นำไปแลกเป็น access token แล้วเรียก API ด้วย token นั้น token มีอายุสั้น เป็นนาทีหรือชั่วโมง เมื่อหมดอายุ แอปก็เอา refresh token ไปแลกอันใหม่

สิ่งที่วิธีนี้ให้ และ key ให้ไม่ได้โดยหลักการ

  • รหัสผ่านของผู้ใช้ไม่เคยไปถึงแอป ความหมายทั้งหมดอยู่ตรงนี้
  • สิทธิ์ถูกซอยเป็น scope "อ่านลิงก์" กับ "ลบลิงก์" เป็นการอนุญาตคนละใบ
  • การยินยอมมองเห็นได้และถอนคืนได้ ผู้ใช้เห็นรายชื่อแอปที่เชื่อมต่ออยู่ และตัดออกทีละแอปได้โดยไม่กระทบที่เหลือ
  • token หมดอายุเอง access token ที่ถูกขโมยไป อีกหนึ่งชั่วโมงก็ไร้ค่า

ราคาที่จ่ายคือความซับซ้อน ต้องมีเซิร์ฟเวอร์ให้สิทธิ์ การลงทะเบียนแอป หน้าจอยินยอม การเก็บและหมุน refresh token และการรับมือเรื่องหมดอายุในทุกการเรียก

เปรียบเทียบตามแกนที่ตัดสินจริง

API key OAuth 2.0
รับรองใคร โปรแกรม ผู้ใช้ที่โปรแกรมทำการแทน
อายุการใช้งาน ไม่มีกำหนด access token ระดับนาทีถึงชั่วโมง
ตัดสิทธิ์อย่างไร เพิกถอน key ถอนการยินยอมหรือ token
ขอบเขตสิทธิ์ โดยทั่วไปคือทั้งบัญชี scope แยกตามการกระทำ
ความลับที่อยู่กับบุคคลที่สาม ตัว key เอง เฉพาะ token ไม่เคยเป็นรหัสผ่าน
แรงที่ต้องลงในการเชื่อมต่อ ส่วนหัวอันเดียว เซิร์ฟเวอร์ให้สิทธิ์และวงจรทั้งชุด
เหมาะกับ เซิร์ฟเวอร์ถึงเซิร์ฟเวอร์ สคริปต์ การเชื่อมต่อภายใน แอปสาธารณะ มาร์เก็ตเพลส "เข้าสู่ระบบด้วย…"

กฎในการเลือก

คำถามไม่ใช่ว่าอย่างไหนปลอดภัยกว่าในเชิงนามธรรม แต่คือใครกำลังมอบสิทธิ์เข้าถึงให้ใคร

เซิร์ฟเวอร์ของคุณ → API ของคนอื่น บัญชีเป็นของคุณ
    → API key

แอปของคนอื่น → API ของคุณ บัญชีเป็นของผู้ใช้คุณ
    → OAuth

งานตามกำหนดเวลา CI การเชื่อมต่อฝั่งหลังบ้าน
    → API key

ผู้ใช้ต้องเห็นและถอนสิทธิ์เป็นรายแอป
    → OAuth

การรับ postback คือกรณีตัวอย่างในตำราของ key เมื่อเซิร์ฟเวอร์ของคุณแจ้งเซิร์ฟเวอร์ของเราว่าคำสั่งซื้อชำระเงินแล้ว ในการแลกเปลี่ยนนั้นไม่มีผู้ใช้อยู่เลยสักคน ไม่มีใครให้ยินยอม และไม่มีที่ทางจะวางหน้าจอยินยอม webhook ในทิศทางกลับกันก็เช่นเดียวกัน

ตอนที่คุณไม่ได้มอง OAuth เปลี่ยนไปแล้ว

บทความครึ่งหนึ่งในหัวข้อนี้อธิบาย OAuth แบบปี 2015 หลังจากนั้นมันแคบลงไม่น้อย

มกราคม 2025 IETF เผยแพร่ RFC 9700, Best Current Practice for OAuth 2.0 Security เอกสารนี้รวบรวมประสบการณ์จากการโจมตีจริงหลายปีไว้ด้วยกัน และประกาศเลิกใช้อย่างเป็นทางการสองวิธีที่เคยถือว่ายอมรับได้

  • implicit grant — วิธีที่คืน token มาในแถบที่อยู่ตรง ๆ
  • resource owner password credentials — ที่แอปถามชื่อผู้ใช้และรหัสผ่านจากผู้ใช้โดยตรง ซึ่งคือสิ่งที่ OAuth เกิดมาเพื่อกำจัดพอดี

พร้อมกันนั้น PKCE กลายเป็นข้อบังคับสำหรับไคลเอนต์ทุกชนิด รวมถึงฝั่งเซิร์ฟเวอร์ด้วย ไม่ใช่เฉพาะฝั่งมือถืออย่างแต่ก่อน

อีกเรื่องที่ควรรู้แยกต่างหาก: OAuth 2.1 จนถึงตอนนี้ยังเป็นร่าง ไม่ใช่ RFC ที่เผยแพร่แล้ว มันรวบการเปลี่ยนแปลงชุดเดียวกันไว้ในเอกสารเดียว ได้แก่ PKCE บังคับ การเทียบ redirect URI แบบตรงตัว การเลิก implicit และ password grant และการห้ามวาง token ไว้ในสตริงคำค้น การอ้างถึงมันในฐานะมาตรฐานที่ใช้อยู่จึงยังเร็วเกินไป

ข้อสรุปเชิงปฏิบัติ ถ้าคู่มือ OAuth ที่คุณอ่านยังแนะนำ implicit flow แปลว่าคู่มือนั้นเก่าไปแล้ว

จุดอ่อนของ key ที่แทบไม่มีใครพูดถึง

key ไม่หมดอายุ นั่นทำให้การเพิกถอนเป็นคันโยกเดียวที่คุณมี แล้วตรงนี้เองที่เรื่องน่าอึดอัดโผล่ขึ้นมา: การเพิกถอนมักไม่เกิดผลทันที

การตรวจ key ทุกคำขอหมายถึงการวิ่งไปถามฐานข้อมูลทุกครั้ง API ใดก็ตามที่มีปริมาณใช้งานจริงย่อมแคชผลนั้นไว้ ที่เรา การจับคู่ระหว่าง key กับบัญชีอยู่ในแคชห้านาที ดังนั้นระหว่างการกด "เพิกถอน" กับการที่สิทธิ์ถูกตัดจริงจึงห่างกันได้ถึงห้านาที และในช่วงนั้น key ที่หลุดออกไปยังทำงานได้ตามปกติ

นี่ไม่ใช่ความบกพร่อง แต่เป็นการแลก ถ้าไม่มีแคช ทุกคำขอต้องไปเคาะประตูฐานข้อมูล API ที่ใช้ key แทบทุกเจ้าก็ทำข้อตกลงคล้าย ๆ กัน เพียงแต่ไม่ค่อยมีใครพูดออกมาดัง ๆ ที่ต้องรู้ไว้มีสองเหตุผล หนึ่ง เมื่อเกิดการรั่วไหลจริง การเพิกถอน key คือการกระทำแรก ไม่ใช่การกระทำสุดท้าย หลังจากนั้นต้องไปตรวจว่าช่วงไม่กี่นาทีนั้นเกิดอะไรขึ้นบ้าง สอง นี่คือปัญหาเดียวกับที่ token อายุสั้นของ OAuth แก้จากอีกด้านหนึ่ง คือมันหมดอายุเองโดยไม่ต้องพึ่งฐานข้อมูล

อยู่กับ key อย่างไรไม่ให้เจ็บตัว

ถ้า key เพียงพอ และในการเชื่อมต่อส่วนใหญ่ก็เพียงพอ ขั้นต่ำคือแบบนี้

  1. เก็บเฉพาะค่าแฮช เซิร์ฟเวอร์นำแฮชของ key ที่ส่งมาไปเทียบกับที่เก็บไว้ ส่วนสายอักขระต้นฉบับจะแสดงให้ผู้ใช้เห็นครั้งเดียวตอนสร้าง ของเราเทียบด้วย SHA-256
  2. ใส่คำนำหน้าให้ key สายอักขระรูปแบบ lix_live_... เป็นสิ่งที่ตัวสแกนความลับในคลังโค้ดและในสายงานบันทึกจดจำได้
  3. หนึ่ง key ต่อหนึ่งการเชื่อมต่อ เพิกถอนอันหนึ่งแล้วอันอื่นไม่ล้มตาม และจากบันทึกจะเห็นชัดว่าอันไหนหลุดไป
  4. อย่าใส่ key ลงในสตริงคำค้นเด็ดขาด ที่อยู่จะไปตกค้างในบันทึกของเว็บเซิร์ฟเวอร์ ในส่วนหัว Referer และในประวัติเบราว์เซอร์ ใช้ส่วนหัวอย่างเดียว
  5. หมุนตามปฏิทิน ไม่ใช่ตามเหตุการณ์ key ที่เคยหมุนสักครั้งจะหมุนได้เร็ว ส่วน key ที่ไม่เคยเปลี่ยนเลย สุดท้ายจะพบว่าฝังแน่นอยู่สี่ที่
  6. ดูเวลาใช้งานล่าสุด key ที่ไม่มีใครแตะมาครึ่งปีไม่ใช่ของสำรอง แต่เป็นประตูที่เปิดค้างไว้

เราใช้อะไร

API ของ Lix.li ยืนยันตัวตนด้วย key ได้แก่ ส่วนหัว X-Api-Key การสร้างและเพิกถอน key ในหน้าจัดการ การเทียบด้วยแฮช และโควตาคำขอแยกของแต่ละ key ไม่มี OAuth และนี่เป็นความตั้งใจ เพราะสถานการณ์การใช้งานของ API นี้อยู่ฝั่งเซิร์ฟเวอร์ทั้งหมด คือสร้างลิงก์ ดึงสถิติ รับคอนเวอร์ชัน และไม่มีอันใดที่มีบุคคลที่สามซึ่งผู้ใช้ต้องให้ความยินยอม

ถ้าวันหนึ่งต้องเปิดให้แอปของคนอื่นเข้าถึงบัญชีของลูกค้าเรา key จะไม่พออีกต่อไป ผู้ใช้ต้องมองเห็นว่าตนอนุญาตอะไรให้ใคร และต้องตัดแอปหนึ่งออกได้โดยไม่ทำให้ที่เหลือพัง ตรงจุดนั้นเองที่ OAuth เลิกเป็นพิธีการ และกลายเป็นทางเลือกเดียวที่ซื่อตรง

ตัวการเรียกเองอยู่ในเอกสาร API รวมถึงรหัสตอบกลับที่ได้เมื่อ key ไม่ถูกต้องและเมื่อ key ถูกเพิกถอนแล้ว ส่วนหัวข้อใกล้เคียง บทวิเคราะห์เรื่องทำไม redirect 301 จึงเปลี่ยน POST ให้เป็น GET คุ้มค่าแก่การอ่านสำหรับทุกคนที่กำลังวางระบบรับ postback เพราะคำขอที่ผ่านการยืนยันตัวตนแล้วอาจไปถึงปลายทางด้วยเมท็อดที่ต่างจากตอนส่งออก