เมื่อ VPS ถูกยึด ก็ไม่ควรหมายความว่าระบบทั้งหมดถูกยึดไปด้วย
Server ถูกยึด ไม่ควรหมายความว่าระบบทั้งหมดถูกยึดไปด้วย
ปกติเวลาเราพัฒนา Backend Application เรามักเก็บ Secret ต่าง ๆ ไว้ในไฟล์ .env ไม่ว่าจะเป็น Database Credentials, API Keys, JWT Secret หรือ Credential สำหรับเชื่อมต่อ External Services
วิธีนี้สะดวก และเป็นแนวที่ใช้กันทั่วไป แต่มีข้อเสียสำคัญอย่างหนึ่ง ถ้าวันหนึ่งมีคนเข้าถึง Server ได้ทั้งที่เราไม่ได้ให้สิทธิ์ ผู้โจมตีอาจอ่านไฟล์ .env แล้วได้ Credential ที่เอาไปเข้าถึงระบบอื่นต่อได้
พอ server ถูกเจาะแล้ว secret จะเป็นอย่างไร ผมเขียนไว้ใน ถ้า Server โดนเจาะ Secret ของเราจะเป็นอย่างไร รอบนี้เลยคิดต่อว่า เราจำเป็นต้องให้ Server มีสิทธิ์เข้าถึงทุกอย่างขนาดนั้นจริงหรือ?
เปลี่ยนจากการเก็บ Secret เป็นการให้สิทธิ์ทำ Action
แนวคิดที่ผมกำลังทดลองคือ แยก Service บางส่วนออกจาก VPS ไปทำงานบน Cloudflare Workers โดยเฉพาะ Service ที่ต้องใช้ Secret ที่มีความสำคัญสูง
แทนที่จะให้ VPS ถือ Secret แล้วเรียกใช้ External API โดยตรง ผมจะเปลี่ยนให้ VPS เรียกเป็น Action ที่กำหนดไว้ล่วงหน้าแทน
ตัวอย่างเช่น แทนที่จะเปิด API ให้ VPS สามารถใช้ Payment API Key ได้ ผมจะสร้าง Action สำหรับ Refund Order โดยเฉพาะ
Worker จะเป็นฝ่ายตรวจเงื่อนไข แล้วใช้ Secret ติดต่อ Payment Provider เอง
VPS รู้เพียงว่าต้องการทำอะไร แต่ไม่จำเป็นต้องรู้ว่า Secret ที่ใช้ทำงานนั้นคืออะไร
ความแตกต่างนี้สำคัญมาก เพราะเราไม่ได้แค่ซ่อน Secret แต่กำลังจำกัดสิ่งที่ Server สามารถสั่งให้ระบบทำได้ด้วย
ไม่เชื่อถือ Request เพียงเพราะมาจาก Server ของเรา
อีกส่วนหนึ่งคือการตรวจสอบตัวตนและสิทธิ์ของ Request
VPS จะส่ง Client JWT มาพร้อมกับ Request โดย Token นี้ออกจาก Auth Service ที่แยกต่างหาก ไม่ได้ออกโดย VPS เอง
Worker จะตรวจสอบ JWT ด้วย Public Key รวมถึงตรวจสอบ Issuer, Audience และวันหมดอายุ จากนั้นตรวจสอบ Permission ว่า User มีสิทธิ์ทำ Action ที่ร้องขอหรือไม่
แต่การมี Token ที่ถูกต้องไม่ได้หมายความว่าจะทำอะไรก็ได้ Worker ยังต้องตรวจสอบ Business Logic และเงื่อนไขของข้อมูลด้วย เช่น User คนนี้มีสิทธิ์ Refund Order นี้จริงหรือไม่ และ Order อยู่ในสถานะที่สามารถ Refund ได้หรือเปล่า
กล่าวคือ ต่อให้ Request ผ่านการ Authentication แล้ว ก็ยังต้องผ่าน Authorization และ Business Validation อีกชั้นหนึ่ง
แบ่งสิทธิ์ Database ตามหน้าที่
Database ก็ใช้แนวคิดเดียวกัน
แทนที่จะให้ Application ทุกตัวใช้ Database User เดียวกัน ผมจะแบ่งสิทธิ์ตามหน้าที่
VPS สามารถเชื่อมต่อ Managed Database ได้โดยตรง แต่เข้าถึงเฉพาะข้อมูลและ Operation ที่ไม่ Sensitive ส่วนข้อมูลหรือ Operation ที่มีความสำคัญสูงจะผ่าน Worker
ถ้า VPS ถูกยึด ผู้โจมตีก็จะได้สิทธิ์เท่าที่ Database User ของ VPS มี ไม่ใช่สิทธิ์ทั้งหมดของ Database
แยก Secret และ Environment
Cloudflare Workers ยังมีข้อดีเรื่องการจัดการ Environment โดยแยก Secret ระหว่าง Development, Staging และ Production ได้
Secret ของแต่ละ Environment ต้องตั้งแยกกัน เพราะค่าพวกนี้ไม่ถูกสืบทอดข้าม Environment จึงไม่จำเป็นต้องใช้ร่วมกัน และสามารถจำกัดการเข้าถึงตาม Service ได้
ส่วน Secret ที่ต้องใช้ร่วมกันหลาย Worker อาจเก็บไว้ส่วนกลางใน Cloudflare Secrets Store แล้ว Bind ให้เฉพาะ Worker ที่จำเป็นต้องใช้ ที่เก็บนี้เป็นคนละอย่างกับ Secret ที่ตั้งไว้กับ Worker แต่ละตัว
สิ่งที่ต้องระวัง
แนวทางนี้ไม่ได้หมายความว่า Cloudflare Worker จะปลอดภัยโดยอัตโนมัติ
ถ้าผู้โจมตีสามารถเข้าถึงระบบ Deploy หรือ Cloudflare Account ได้ ก็อาจแก้ไข Worker ให้ส่ง Secret ออกไปได้เช่นกัน
นอกจากนี้ JWT ที่ถูกขโมยจาก VPS ก็ยังอาจถูกนำไปใช้ซ้ำได้ จึงต้องมีมาตรการเรื่อง Token อายุสั้น, Rate Limiting, Idempotency และการป้องกัน Replay ตามความเสี่ยงของแต่ละ Action
ระบบ Audit Log ก็ควรแยกออกจาก VPS เพื่อไม่ให้ผู้โจมตีสามารถแก้ไขหรือลบร่องรอยการทำงานได้ง่าย ๆ
เป้าหมายไม่ใช่การป้องกันไม่ให้ Server ถูกยึด
ผมคิดว่าเราไม่ควรออกแบบระบบโดยตั้งสมมติฐานว่า Server ของเราจะไม่มีวันถูกยึด
แต่ควรตั้งสมมติฐานกลับกันว่า วันหนึ่ง Server อาจถูกยึดได้ และเมื่อเกิดเหตุการณ์นั้นขึ้น ความเสียหายควรถูกจำกัดอยู่ในขอบเขตของสิทธิ์ที่ Server นั้นมีเท่านั้น
นี่คือแนวคิดของ Least Privilege และ Zero Trust ที่ผมกำลังนำมาปรับใช้กับระบบของตัวเอง
Server ถูกยึด ไม่ควรหมายความว่าระบบทั้งหมดถูกยึดไปด้วย