ข้ามไปยังเนื้อหา

แนวปฏิบัติด้านความปลอดภัย

กุญแจ 1 ใบอ่านประวัติการขายของร้านและรายชื่อสมาชิกได้ แต่แก้อะไรไม่ได้เลย เพราะทุกเส้นอ่านอย่างเดียว ⇒ ความเสี่ยงคือ “ข้อมูลรั่ว” ไม่ใช่ “ของพัง” ถึงอย่างนั้นก็ยังต้องดูแลกุญแจให้เท่ากับรหัสผ่านฐานข้อมูล

ความจริง 2 ข้อที่กำหนดทุกอย่างในหน้านี้:

  • กุญแจโชว์ครั้งเดียว — เราเก็บไว้เป็นค่าที่ย้อนกลับไม่ได้ (hash) เท่านั้น ⇒ ไม่มีใครใน ScanFood อ่านกลับมาให้คุณได้ · ทำหายคือออกใบใหม่ ไม่ใช่ขอดูของเดิม
  • กุญแจเดินทางได้ที่เดียว — เราอ่านจาก Authorization: Bearer <key> เท่านั้น ไม่อ่านจาก query string ไม่อ่านจาก body

เก็บกุญแจใน secret manager หรือ environment variable ที่ฉีดให้ตอน deploy อย่าเก็บใน repo อย่าใส่ไฟล์ที่ commit อย่าวางในตั๋วงานหรือห้องแชท

Terminal window
# อ่านเข้า shell โดยไม่ให้ตกค้างใน history…
read -rs SCANFOOD_API_KEY && export SCANFOOD_API_KEY
curl -s -H "Authorization: Bearer $SCANFOOD_API_KEY" \
"https://api.scanfood.co/ext/v1/shops"
# …และห้ามทำแบบนี้: กุญแจจะไปโผล่ใน history ใน access log
# และในทุกตัวกลางระหว่างคุณกับเรา
# curl "https://api.scanfood.co/ext/v1/shops?key=sf_live_…" ← API ไม่อ่านช่องนี้อยู่แล้ว

เรื่องที่ควรรู้เพิ่ม:

  • วิ่งบน HTTPS เท่านั้น · อย่าปิดการตรวจใบรับรอง “แค่ตอนเทส” เพราะนั่นคือคำขอที่มักหลุดไปวิ่งบนของจริง
  • เราไม่เคยบันทึกกุญแจ — ทั้งตัวเต็มและค่า hash ไม่โผล่ในบันทึกการใช้งานของเราสักไบต์ · สิ่งที่เราเก็บคือ keyId (ท่อนกลาง) ซึ่งบอกกันได้อย่างปลอดภัยเวลาติดต่อฝ่ายสนับสนุน

ขอกุญแจแยกใบให้ทุกระบบที่เรียกเรา — งานซิงก์บัญชีตอนกลางคืน แดชบอร์ด สมุดงานของนักวิเคราะห์ ผู้ขายที่มาทำ POC

ทำไม ได้อะไร
เพิกถอนได้ตรงจุด ปิดสิทธิ์ผู้ขายได้โดยงานซิงก์บัญชีตอนตี 2 ไม่หยุด
รู้ว่าใครใช้ คำถาม “เมื่อวานใครยิง 40,000 ครั้ง” มีคำตอบ ไม่ใช่ยักไหล่
เพดานแยกกัน การทดลองกินโควตาวันของกุญแจงานจริงไม่ได้
วงความเสียหายจำกัด กุญแจหลุด 1 ใบ = เปิดเฉพาะขอบเขตของระบบนั้น ไม่ใช่ทุกอย่างที่คุณมี

กุญแจทุกใบต้องมีป้ายชื่อ · ใช้มันให้เป็นประโยชน์ เช่น accounting-nightly-sync, bi-dashboard, vendor-poc-2026-09 คือชื่อที่เพื่อนร่วมงานอ่านแล้วตัดสินใจได้เองในอีก 1 ปีข้างหน้าโดยไม่ต้องถามใคร

มีปุ่ม 2 ตัวที่กำหนดว่ากุญแจเห็นได้แค่ไหน · ขอค่าที่เล็กที่สุดที่ยังทำงานได้ แล้วค่อยขอกว้างขึ้นทีหลังก็ได้

ขอบเขต — เห็นร้านไหนบ้าง · กุญแจออกให้ได้ทั้งแบบร้านเดียวและแบบทั้งแฟรนไชส์ กุญแจแฟรนไชส์เห็นทุกสาขาใต้แฟรนไชส์นั้น ทั้งตอนนี้และในอนาคต รวมสาขาที่เปิดหลังออกกุญแจไปแล้ว ถ้าระบบแตะแค่ 2 สาขา กุญแจแฟรนไชส์ไม่ใช่เครื่องมือที่ถูก

ข้อมูลส่วนบุคคล — เบอร์โทรและอีเมล · เบอร์โทรและอีเมลของสมาชิก ไม่ถูกส่งออก เป็นค่าตั้งต้น กุญแจธรรมดาจะเห็น hasTel และ hasEmail เป็นจริง/เท็จ ซึ่งพอตอบคำถาม “ติดต่อสมาชิกคนนี้ได้ไหม” โดยที่ข้อมูลส่วนบุคคลไม่ต้องออกจาก ScanFood · กุญแจที่คืนค่าจริงมีอยู่ แต่ออกให้แบบตั้งใจ โดยระบุวัตถุประสงค์ และมาพร้อมภาระหน้าที่ด้านการคุ้มครองข้อมูลส่วนบุคคล

อีกส่วนที่อยู่นอกมือ API โดยการออกแบบ: ต้นทุน สูตร และข้อมูลวัตถุดิบไม่ออกทางนี้ และไม่มีเส้นไหนเขียนอะไรกลับเข้า ScanFood ได้

  • ปิดบังก่อนบันทึก — บันทึก path, สถานะ, จำนวนแถว และเวลา · อย่าบันทึกหัว Authorization · ฟังก์ชันช่วยแบบข้างบนที่ใส่ไว้ตรงขอบเพียงจุดเดียว มีค่ากว่ากฎที่ต้องอาศัยให้คนจำ
  • บันทึก keyId ไม่ใช่ตัวกุญแจ — ใน sf_live_<keyId>_<secret> ท่อน keyId เป็นค่าที่เปิดเผยได้ · บันทึกไว้ทุกรอบ = คำถามฝ่ายสนับสนุนตอบได้ในไม่กี่นาที
  • จับรูปแบบของการถูกใช้ผิด — คำขอที่คุณไม่ได้ตั้งไว้ ในเวลาที่คุณไม่ได้รัน คือสัญญาณว่ากุญแจหลุด · บันทึกฝั่งคุณจะเห็นก่อนใครเพื่อน
  • ตั้งเตือนที่ 401 — ระบบที่ทำงานปกติไม่ผลิตข้อผิดพลาดการยืนยันตัวตน · ถ้าโผล่มาเป็นชุด แปลว่ากุญแจถูกเพิกถอน ถูกหมุนโดยไม่มีใครบอก หรือมีคนกำลังลองกุญแจที่ได้ไปไม่ครบ
  • เฝ้าโควตาวัน — การใช้งานที่กระโดดโดยอธิบายไม่ได้ คือเครื่องจับการรั่วที่ถูกที่สุดที่คุณมี · ตัวเลขที่ต้องตามคือ X-RateLimit-Remaining ที่ติดมากับทุกคำตอบ

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

ลำดับสำคัญ และถ้าทำตามลำดับนี้จะไม่มีช่วงที่ระบบล่ม:

  1. ขอกุญแจใบใหม่ สำหรับระบบเดิมและขอบเขตเดิม
  2. เอาไปติดตั้ง ใน secret store แล้วรีสตาร์ตหรือโหลดใหม่ฝั่งผู้เรียก
  3. ยืนยันว่าใบใหม่คือใบที่ทำงานอยู่ — เห็นการใช้งานของใบใหม่ ส่วนใบเก่าเงียบลง
  4. แจ้งให้เราเพิกถอนใบเก่า — ก่อนหน้านั้นทั้ง 2 ใบใช้ได้พร้อมกัน จึงไม่มีช่วงที่ไม่มีใบไหนใช้ได้เลย

หลังเพิกถอน กุญแจใบนั้นจะตอบ 401 พร้อม "error": "key_revoked" และเป็นแบบนั้นถาวร · สั่งเพิกถอนซ้ำไม่มีผลเสีย

กุญแจที่ไปอยู่ใน repo สาธารณะ ในภาพหน้าจอ ในล็อกที่วางแชร์ หรือในเครื่องของอดีตพนักงาน = กุญแจที่หลุดแล้ว ไม่แน่ใจก็ให้ถือว่าหลุด

  1. แจ้งเราทันที และขอให้เพิกถอนใบนั้น · บอก keyId (ท่อนกลางของกุญแจ) ไม่ใช่กุญแจทั้งใบ
  2. ออกใบแทน แล้วติดตั้ง · ถ้าระบบหยุดไม่ได้ ให้ทำใบใหม่ก่อนตามลำดับการหมุนข้างบน แล้วค่อยเพิกถอน
  3. ให้ถือว่าช่วง 5 นาทีนั้นถูกใช้ไปแล้ว — ระหว่างที่การเพิกถอนยังกระจายไม่ครบ กุญแจยังใช้ได้ · ถามเราได้ว่ากุญแจใบนั้นอ่านอะไรไปบ้างและเมื่อไหร่ เราเก็บบันทึกการใช้งานรายกุญแจ พร้อม path สถานะ และเวลา
  4. ปิดรูที่ทำให้มันหลุด — กุญแจใหม่ที่กลับไปอยู่ใน commit history เดิม ห้องแชทเดิม หรือไดรฟ์แชร์เดิม จะหลุดอีก
  5. ดูว่าขอบเขตของมันเปิดอะไรไว้ — กุญแจร้านเดียวที่ไม่มีสิทธิ์ข้อมูลส่วนบุคคลและหลุดไป 5 นาที เป็นคนละเรื่องกับกุญแจแฟรนไชส์ที่คืนเบอร์โทรได้

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