fix(security): Enforce admin authentication across sensitive backend endpoints and eliminate BOM data leak #15

Merged
admin merged 1 commits from fix/backend-admin-auth-guard into master 2026-10-18 23:56:01 +00:00

🛡️ Security Audit & Hardening Report

1. Executive Summary

PR นี้ทำการตรวจสอบและปิดช่องโหว่ด้านความปลอดภัยของ Backend API (Elysia.js) ทั่วทั้งระบบ โดยบังคับใช้ Admin Authentication Guard (adminOnly Middleware) กับทุก Endpoint ที่เกี่ยวข้องกับ:

  1. การจัดการและแก้ไขข้อมูลแคตตาล็อกสินค้า (Product & Category CRUD, Bulk Import/Delete)
  2. ข้อมูลความลับทางการค้าและสูตรการผลิต (Bill of Materials - BOM, ต้นทุน Raw Materials, Profitability/MRP Forecast)
  3. ข้อมูลส่วนบุคคลของลูกค้า (PII เช่น รายชื่อลูกค้า CRM, RFM Segment, ข้อความติดต่อ Contact Form, อีเมล Newsletter Subscribers)
  4. การจัดการคำสั่งซื้อและสถานะ (Order Status Update, Tracking Assignment)
  5. การจัดการ Assets และเนื้อหาเว็บไซต์ (Articles, Media Deletion, Category Layout Sections)

พร้อมทั้งกำจัดจุดรั่วไหลของข้อมูลสูตรสินค้า (bomItems และราคาต้นทุน rawMaterial) ที่เคยหลุดออกไปใน Public Catalog Query (GET /products และ GET /products/:id)


2. Root Cause & Technical Findings

2.1 Elysia Lifecycle Plugin Scoping Flaw

  • ปัญหาเดิม: Middleware adminOnly ถูกสร้างด้วย new Elysia({ name: 'admin-auth' }).onBeforeHandle(...) โดยไม่มีออปชัน { as: 'scoped' }
  • ผลกระทบ: ใน Elysia.js v1.2+ หากไม่ระบุ { as: 'scoped' } หรือ { as: 'global' } ฮุก onBeforeHandle ที่ประกาศใน plugin ย่อยจะไม่ถูก propagate ไปยัง Parent Route เมื่อถูกเรียกใช้ผ่าน .use(adminOnly) ส่งผลให้ middleware ไม่ทำงานจริงในบาง Controller
  • การแก้ไข: เพิ่ม { as: 'scoped' } ใน onBeforeHandle ของ apps/backend/src/middleware/auth.ts และรวมศูนย์ JWT_SECRET ให้เป็น Single Source of Truth

2.2 Business Confidentiality Leak in Public Queries

  • ปัญหาเดิม: GET /api/products และ GET /api/products/:id ทำการ query variants โดย include: { bomItems: { include: { rawMaterial: true } } }
  • ผลกระทบ: ผู้ใช้ทั่วไปหรือ Bot ที่เรียกดูหน้าร้าน สามารถมองเห็นสูตรผสม ปริมาณสารเคมี/วัตถุดิบ และราคาต้นทุนต่อหน่วย (pricePerUnit) ของสินค้าทุกรายการ
  • การแก้ไข: ตัด bomItems ออกจาก Public Queries ทั้งหมด ข้อมูล BOM จะเปิดให้เข้าถึงเฉพาะผ่าน GET /api/products/:id/bom และ GET /api/products/bom-status ซึ่งถูกครอบด้วย adminOnly เท่านั้น

3. Comprehensive Security Matrix

Module Route / Endpoint Method Previous State New Protected State Justification
Products /api/products GET Public (leaked BOM) Public (Clean) หน้าร้านใช้แสดงสินค้า (ตัดข้อมูลต้นทุน BOM ออก)
/api/products/brands GET Public Public ตัวกรองแบรนด์สำหรับหน้าร้าน
/api/products/:id GET Public (leaked BOM) Public (Clean) หน้ารายละเอียดสินค้า (ตัดข้อมูลต้นทุน BOM ออก)
/api/products/:id/detail-sections GET Public Public CMS Page Builder สำหรับ Product Page
/api/products/bom-status GET ⚠️ Public 🔒 Admin Only รายการสถานะสูตร BOM และต้นทุนรวมของทุกล็อต
/api/products/:id/bom GET, PUT ⚠️ Public 🔒 Admin Only ความลับทางการค้า/สูตรผสมของสินค้า
/api/products POST ⚠️ Public 🔒 Admin Only ป้องกันการสร้างสินค้าโดยไม่ได้รับอนุญาต
/api/products/:id PUT, PATCH, DELETE ⚠️ Public 🔒 Admin Only ป้องกันการแก้ไข/ลบสินค้า
/api/products/bulk, /bulk-delete POST ⚠️ Public 🔒 Admin Only ป้องกันการโจมตี Mass Write/Delete
/api/products/brands POST, PUT, DELETE ⚠️ Public 🔒 Admin Only จัดการแบรนด์สินค้า
/api/products/:id/images POST ⚠️ Public 🔒 Admin Only เพิ่มรูปภาพสินค้า
/api/products/:id/detail-sections/** POST, PUT, DELETE ⚠️ Public 🔒 Admin Only แก้ไข Section ในหน้ารายละเอียดสินค้า
Categories /api/categories GET Public Public แสดงหมวดหมู่สินค้าหน้าร้าน
/api/categories/:id GET Public Public รายละเอียดหมวดหมู่
/api/categories/:id/products GET Public Public แสดงรายการสินค้าในหมวดหมู่
/api/categories/:id/sections GET Public Public Category Page Builder read
/api/categories/bulk-import POST ⚠️ Public 🔒 Admin Only นำเข้าหมวดหมู่จำนวนมาก
/api/categories POST, PUT, DELETE ⚠️ Public 🔒 Admin Only จัดการหมวดหมู่สินค้า
/api/categories/:id/media, /image PUT, PATCH ⚠️ Public 🔒 Admin Only จัดการรูปภาพหมวดหมู่
/api/categories/:id/sections/** POST, PUT, DELETE ⚠️ Public 🔒 Admin Only จัดการ Section ของหมวดหมู่
Financial & MRP /api/profitability/** ALL ⚠️ Public 🔒 Admin Only กำไร-ขาดทุน, Margin, ค่าธรรมเนียม, MRP 30 วัน
/api/raw-materials/** ALL ⚠️ Public 🔒 Admin Only สารเคมี, สต็อกวัตถุดิบ, ราคาต้นทุนต่อหน่วย
/api/business-analytics/** ALL ⚠️ Public 🔒 Admin Only แผนที่การเงิน Omnichannel, Repeat Analytics
CRM & Customers /api/crm/** ALL ⚠️ Public 🔒 Admin Only Customer RFM Scores, Churn Risk, VIP Lists
/api/customers GET, POST ⚠️ Public 🔒 Admin Only รายชื่อลูกค้า, ที่อยู่, เบอร์โทรศัพท์, การปรับแต้ม
/api/contacts GET, PATCH ⚠️ Public 🔒 Admin Only ข้อความติดต่อจากลูกค้าหน้าร้าน (PII)
/api/contacts POST Public Public ฟอร์มติดต่อเราสำหรับผู้เยี่ยมชม
/api/newsletter/subscribers GET ⚠️ Public 🔒 Admin Only รายชื่ออีเมลผู้ติดตาม Newsletter
/api/newsletter/subscribe POST Public Public ฟอร์มสมัครรับจดหมายข่าว
Orders & Sales /api/orders POST, GET /:id, PATCH /:id/slip Public Public ลูกค้าสั่งซื้อ, อัปโหลดสลิป, ดูคำสั่งซื้อของตน
/api/orders/track POST Public Public ติดตามพัสดุ (ต้องกรอก OrderId + เบอร์โทรตรงกัน)
/api/orders GET ⚠️ Public 🔒 Admin Only รายการคำสั่งซื้อทั้งหมด
/api/orders/:id/status, /tracking PATCH ⚠️ Public 🔒 Admin Only เปลี่ยนสถานะคำสั่งซื้อ, ใส่เลข Tracking
/api/orders/customer/:customerId GET ⚠️ Public 🔒 Admin Only ดูประวัติสั่งซื้อตาม Customer ID
/api/tiktok/**, /lazada-orders/**, /shopee/** ALL ⚠️ Public 🔒 Admin Only ซิงค์คำสั่งซื้อและค่าธรรมเนียม Marketplace
Promotions & CMS /api/coupons/active, /validate GET, POST Public Public ลูกค้าใช้คูปองและตรวจสอบส่วนลด
/api/coupons (Admin CRUD) GET, POST, PATCH, DELETE ⚠️ Public 🔒 Admin Only สร้าง/แก้ไข/ลบคูปอง
/api/promotions/active GET Public Public แสดงโปรโมชั่นหน้าร้าน
/api/promotions (Admin CRUD) GET, POST, PUT, DELETE ⚠️ Public 🔒 Admin Only จัดการแคมเปญโปรโมชั่น
/api/articles GET, GET /:slug, GET /id/:id Public Public อ่านบทความบล็อกหน้าร้าน
/api/articles (Mutations) POST, PUT, DELETE, PATCH ⚠️ Public 🔒 Admin Only จัดการบทความและคำแปลภาษา
/api/media GET, DELETE ⚠️ Public 🔒 Admin Only รายการ Media Library และการลบไฟล์
/api/product-attributes/definitions/** POST, PUT, PATCH ⚠️ Public 🔒 Admin Only การสร้างและแก้ไข Attribute Definitions

4. Client Compatibility & Verification

  • Storefront & Website (apps/website, apps/spa):
    • ไม่ได้รับผลกระทบใดๆ ทุก Route ที่จำเป็นสำหรับการเรนเดอร์ (Catalog, Detail, Reviews, Sections, Checkout, Tracking, Contact, Newsletter) ยังคงเป็น Public
    • Payload เบาลงและปลอดภัยขึ้นเนื่องจากไม่มีการส่ง bomItems ที่ไม่จำเป็น
  • Admin Panel (apps/admin):
    • ไม่ได้รับผลกระทบใดๆ เนื่องจากระบบ API Client ของ Admin ส่ง Authorization: Bearer <token> อยู่แล้วในทุก Request
  • Automated Test Results:
    • เพิ่ม Security Contract Test: apps/backend/src/routes/auth-guard.contract.test.ts
    • ผ่านการทดสอบทั้งหมด 95/95 tests (0 failures)
    • ทดสอบผ่าน Production Build (tsc -b && vite build) สมบูรณ์ 100%

5. OWASP Standards Compliance

  • A01:2021 – Broken Access Control: แก้ไขช่องโหว่ Direct Object Reference และ Unprotected Administrative Interfaces โดยบังคับใช้ JWT Authentication ในทุกจุดสำคัญ
  • A04:2021 – Insecure Design: แก้ไขการรั่วไหลของข้อมูลสูตรสินค้าและต้นทุนใน Public Response ด้วย Least Privilege Data Exposure Model
## 🛡️ Security Audit & Hardening Report ### 1. Executive Summary PR นี้ทำการตรวจสอบและปิดช่องโหว่ด้านความปลอดภัยของ **Backend API (Elysia.js)** ทั่วทั้งระบบ โดยบังคับใช้ **Admin Authentication Guard (`adminOnly` Middleware)** กับทุก Endpoint ที่เกี่ยวข้องกับ: 1. การจัดการและแก้ไขข้อมูลแคตตาล็อกสินค้า (Product & Category CRUD, Bulk Import/Delete) 2. ข้อมูลความลับทางการค้าและสูตรการผลิต (**Bill of Materials - BOM**, ต้นทุน Raw Materials, Profitability/MRP Forecast) 3. ข้อมูลส่วนบุคคลของลูกค้า (**PII** เช่น รายชื่อลูกค้า CRM, RFM Segment, ข้อความติดต่อ Contact Form, อีเมล Newsletter Subscribers) 4. การจัดการคำสั่งซื้อและสถานะ (Order Status Update, Tracking Assignment) 5. การจัดการ Assets และเนื้อหาเว็บไซต์ (Articles, Media Deletion, Category Layout Sections) พร้อมทั้งกำจัดจุดรั่วไหลของข้อมูลสูตรสินค้า (`bomItems` และราคาต้นทุน `rawMaterial`) ที่เคยหลุดออกไปใน Public Catalog Query (`GET /products` และ `GET /products/:id`) --- ### 2. Root Cause & Technical Findings #### 2.1 Elysia Lifecycle Plugin Scoping Flaw - **ปัญหาเดิม:** Middleware `adminOnly` ถูกสร้างด้วย `new Elysia({ name: 'admin-auth' }).onBeforeHandle(...)` โดยไม่มีออปชัน `{ as: 'scoped' }` - **ผลกระทบ:** ใน Elysia.js v1.2+ หากไม่ระบุ `{ as: 'scoped' }` หรือ `{ as: 'global' }` ฮุก `onBeforeHandle` ที่ประกาศใน plugin ย่อยจะไม่ถูก propagate ไปยัง Parent Route เมื่อถูกเรียกใช้ผ่าน `.use(adminOnly)` ส่งผลให้ middleware ไม่ทำงานจริงในบาง Controller - **การแก้ไข:** เพิ่ม `{ as: 'scoped' }` ใน `onBeforeHandle` ของ `apps/backend/src/middleware/auth.ts` และรวมศูนย์ `JWT_SECRET` ให้เป็น Single Source of Truth #### 2.2 Business Confidentiality Leak in Public Queries - **ปัญหาเดิม:** `GET /api/products` และ `GET /api/products/:id` ทำการ query `variants` โดย `include: { bomItems: { include: { rawMaterial: true } } }` - **ผลกระทบ:** ผู้ใช้ทั่วไปหรือ Bot ที่เรียกดูหน้าร้าน สามารถมองเห็นสูตรผสม ปริมาณสารเคมี/วัตถุดิบ และราคาต้นทุนต่อหน่วย (`pricePerUnit`) ของสินค้าทุกรายการ - **การแก้ไข:** ตัด `bomItems` ออกจาก Public Queries ทั้งหมด ข้อมูล BOM จะเปิดให้เข้าถึงเฉพาะผ่าน `GET /api/products/:id/bom` และ `GET /api/products/bom-status` ซึ่งถูกครอบด้วย `adminOnly` เท่านั้น --- ### 3. Comprehensive Security Matrix | Module | Route / Endpoint | Method | Previous State | New Protected State | Justification | | :--- | :--- | :---: | :---: | :---: | :--- | | **Products** | `/api/products` | GET | Public (leaked BOM) | **Public (Clean)** | หน้าร้านใช้แสดงสินค้า (ตัดข้อมูลต้นทุน BOM ออก) | | | `/api/products/brands` | GET | Public | **Public** | ตัวกรองแบรนด์สำหรับหน้าร้าน | | | `/api/products/:id` | GET | Public (leaked BOM) | **Public (Clean)** | หน้ารายละเอียดสินค้า (ตัดข้อมูลต้นทุน BOM ออก) | | | `/api/products/:id/detail-sections` | GET | Public | **Public** | CMS Page Builder สำหรับ Product Page | | | `/api/products/bom-status` | GET | ⚠️ Public | 🔒 **Admin Only** | รายการสถานะสูตร BOM และต้นทุนรวมของทุกล็อต | | | `/api/products/:id/bom` | GET, PUT | ⚠️ Public | 🔒 **Admin Only** | ความลับทางการค้า/สูตรผสมของสินค้า | | | `/api/products` | POST | ⚠️ Public | 🔒 **Admin Only** | ป้องกันการสร้างสินค้าโดยไม่ได้รับอนุญาต | | | `/api/products/:id` | PUT, PATCH, DELETE | ⚠️ Public | 🔒 **Admin Only** | ป้องกันการแก้ไข/ลบสินค้า | | | `/api/products/bulk`, `/bulk-delete` | POST | ⚠️ Public | 🔒 **Admin Only** | ป้องกันการโจมตี Mass Write/Delete | | | `/api/products/brands` | POST, PUT, DELETE | ⚠️ Public | 🔒 **Admin Only** | จัดการแบรนด์สินค้า | | | `/api/products/:id/images` | POST | ⚠️ Public | 🔒 **Admin Only** | เพิ่มรูปภาพสินค้า | | | `/api/products/:id/detail-sections/**` | POST, PUT, DELETE | ⚠️ Public | 🔒 **Admin Only** | แก้ไข Section ในหน้ารายละเอียดสินค้า | | **Categories** | `/api/categories` | GET | Public | **Public** | แสดงหมวดหมู่สินค้าหน้าร้าน | | | `/api/categories/:id` | GET | Public | **Public** | รายละเอียดหมวดหมู่ | | | `/api/categories/:id/products` | GET | Public | **Public** | แสดงรายการสินค้าในหมวดหมู่ | | | `/api/categories/:id/sections` | GET | Public | **Public** | Category Page Builder read | | | `/api/categories/bulk-import` | POST | ⚠️ Public | 🔒 **Admin Only** | นำเข้าหมวดหมู่จำนวนมาก | | | `/api/categories` | POST, PUT, DELETE | ⚠️ Public | 🔒 **Admin Only** | จัดการหมวดหมู่สินค้า | | | `/api/categories/:id/media`, `/image` | PUT, PATCH | ⚠️ Public | 🔒 **Admin Only** | จัดการรูปภาพหมวดหมู่ | | | `/api/categories/:id/sections/**` | POST, PUT, DELETE | ⚠️ Public | 🔒 **Admin Only** | จัดการ Section ของหมวดหมู่ | | **Financial & MRP** | `/api/profitability/**` | ALL | ⚠️ Public | 🔒 **Admin Only** | กำไร-ขาดทุน, Margin, ค่าธรรมเนียม, MRP 30 วัน | | | `/api/raw-materials/**` | ALL | ⚠️ Public | 🔒 **Admin Only** | สารเคมี, สต็อกวัตถุดิบ, ราคาต้นทุนต่อหน่วย | | | `/api/business-analytics/**` | ALL | ⚠️ Public | 🔒 **Admin Only** | แผนที่การเงิน Omnichannel, Repeat Analytics | | **CRM & Customers** | `/api/crm/**` | ALL | ⚠️ Public | 🔒 **Admin Only** | Customer RFM Scores, Churn Risk, VIP Lists | | | `/api/customers` | GET, POST | ⚠️ Public | 🔒 **Admin Only** | รายชื่อลูกค้า, ที่อยู่, เบอร์โทรศัพท์, การปรับแต้ม | | | `/api/contacts` | GET, PATCH | ⚠️ Public | 🔒 **Admin Only** | ข้อความติดต่อจากลูกค้าหน้าร้าน (PII) | | | `/api/contacts` | POST | Public | **Public** | ฟอร์มติดต่อเราสำหรับผู้เยี่ยมชม | | | `/api/newsletter/subscribers` | GET | ⚠️ Public | 🔒 **Admin Only** | รายชื่ออีเมลผู้ติดตาม Newsletter | | | `/api/newsletter/subscribe` | POST | Public | **Public** | ฟอร์มสมัครรับจดหมายข่าว | | **Orders & Sales** | `/api/orders` | POST, GET /:id, PATCH /:id/slip | Public | **Public** | ลูกค้าสั่งซื้อ, อัปโหลดสลิป, ดูคำสั่งซื้อของตน | | | `/api/orders/track` | POST | Public | **Public** | ติดตามพัสดุ (ต้องกรอก OrderId + เบอร์โทรตรงกัน) | | | `/api/orders` | GET | ⚠️ Public | 🔒 **Admin Only** | รายการคำสั่งซื้อทั้งหมด | | | `/api/orders/:id/status`, `/tracking` | PATCH | ⚠️ Public | 🔒 **Admin Only** | เปลี่ยนสถานะคำสั่งซื้อ, ใส่เลข Tracking | | | `/api/orders/customer/:customerId` | GET | ⚠️ Public | 🔒 **Admin Only** | ดูประวัติสั่งซื้อตาม Customer ID | | | `/api/tiktok/**`, `/lazada-orders/**`, `/shopee/**` | ALL | ⚠️ Public | 🔒 **Admin Only** | ซิงค์คำสั่งซื้อและค่าธรรมเนียม Marketplace | | **Promotions & CMS** | `/api/coupons/active`, `/validate` | GET, POST | Public | **Public** | ลูกค้าใช้คูปองและตรวจสอบส่วนลด | | | `/api/coupons` (Admin CRUD) | GET, POST, PATCH, DELETE | ⚠️ Public | 🔒 **Admin Only** | สร้าง/แก้ไข/ลบคูปอง | | | `/api/promotions/active` | GET | Public | **Public** | แสดงโปรโมชั่นหน้าร้าน | | | `/api/promotions` (Admin CRUD) | GET, POST, PUT, DELETE | ⚠️ Public | 🔒 **Admin Only** | จัดการแคมเปญโปรโมชั่น | | | `/api/articles` | GET, GET /:slug, GET /id/:id | Public | **Public** | อ่านบทความบล็อกหน้าร้าน | | | `/api/articles` (Mutations) | POST, PUT, DELETE, PATCH | ⚠️ Public | 🔒 **Admin Only** | จัดการบทความและคำแปลภาษา | | | `/api/media` | GET, DELETE | ⚠️ Public | 🔒 **Admin Only** | รายการ Media Library และการลบไฟล์ | | | `/api/product-attributes/definitions/**` | POST, PUT, PATCH | ⚠️ Public | 🔒 **Admin Only** | การสร้างและแก้ไข Attribute Definitions | --- ### 4. Client Compatibility & Verification - **Storefront & Website (`apps/website`, `apps/spa`):** - ไม่ได้รับผลกระทบใดๆ ทุก Route ที่จำเป็นสำหรับการเรนเดอร์ (Catalog, Detail, Reviews, Sections, Checkout, Tracking, Contact, Newsletter) ยังคงเป็น Public - Payload เบาลงและปลอดภัยขึ้นเนื่องจากไม่มีการส่ง `bomItems` ที่ไม่จำเป็น - **Admin Panel (`apps/admin`):** - ไม่ได้รับผลกระทบใดๆ เนื่องจากระบบ API Client ของ Admin ส่ง `Authorization: Bearer <token>` อยู่แล้วในทุก Request - **Automated Test Results:** - เพิ่ม Security Contract Test: `apps/backend/src/routes/auth-guard.contract.test.ts` - ผ่านการทดสอบทั้งหมด 95/95 tests (0 failures) - ทดสอบผ่าน Production Build (`tsc -b && vite build`) สมบูรณ์ 100% --- ### 5. OWASP Standards Compliance - **A01:2021 – Broken Access Control:** แก้ไขช่องโหว่ Direct Object Reference และ Unprotected Administrative Interfaces โดยบังคับใช้ JWT Authentication ในทุกจุดสำคัญ - **A04:2021 – Insecure Design:** แก้ไขการรั่วไหลของข้อมูลสูตรสินค้าและต้นทุนใน Public Response ด้วย Least Privilege Data Exposure Model
antigravity-bot Bot added 1 commit 2026-10-18 23:46:33 +00:00
admin merged commit 9107a64637 into master 2026-10-18 23:56:01 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/homaroma#15