01204223/secrets
เราจะฝึกจัดการกับข้อมูลที่เป็นความลับในการพัฒนาเว็บแอพ
เนื้อหา
- 1 แนวปฏิบัติทั่วไปในการจัดการ secrets (รหัสผ่าน / API keys) สำหรับเว็บแอป Flask + React
- 1.1 หลักคิดพื้นฐาน
- 1.2 ทำความเข้าใจ “อะไรเป็น secret” ในสถาปัตยกรรม Flask + React
- 1.3 Git และการป้องกันการหลุดเข้าระบบควบคุมเวอร์ชัน
- 1.4 การเก็บ secret ระหว่าง Development
- 1.5 การเก็บ secret ใน Production
- 1.6 Flask: จุดที่เกี่ยวกับ secret โดยตรง
- 1.7 React: สิ่งที่ต้องระวังเป็นพิเศษ
- 1.8 การส่ง secret ระหว่างบริการ (backend ↔ external services)
- 1.9 Logging / Monitoring / Error handling
- 1.10 การจัดการสิทธิ์และการหมุนคีย์ (Rotation)
- 1.11 การทดสอบและ CI/CD
- 1.12 Checklist แบบสั้น
- 1.13 ตัวอย่างโครงสร้างไฟล์ที่พบบ่อย
แนวปฏิบัติทั่วไปในการจัดการ secrets (รหัสผ่าน / API keys) สำหรับเว็บแอป Flask + React
- เขียนโดย chatgpt
หลักคิดพื้นฐาน
ห้ามฝัง secret ลงในโค้ด (รวมถึง commit เข้า git, paste ลง issue, หรือส่งในแชต)
ให้สิทธิ์เท่าที่จำเป็น (least privilege) เช่น key ที่ใช้เรียก API แค่ read ก็อย่าให้ write
แยกสภาพแวดล้อม (dev / staging / prod) และใช้ secret คนละชุดเสมอ
หมุนคีย์ได้ (rotation) ออกแบบให้เปลี่ยน key/รหัสผ่านแล้วระบบรันต่อได้ ไม่ต้อง deploy แบบเสี่ยง ๆ
ทุกอย่างที่อยู่ฝั่ง client ถือว่า “เปิดเผยได้” เพราะ React build แล้วผู้ใช้สามารถดูไฟล์ JS ได้
ทำความเข้าใจ “อะไรเป็น secret” ในสถาปัตยกรรม Flask + React
อะไรที่ควรอยู่ฝั่ง Backend (Flask) เท่านั้น
รหัสผ่าน DB, connection string
API key ของบริการภายนอกที่มีสิทธิ์ “ทำอะไรได้จริง” (เช่น จ่ายเงิน, ส่งอีเมล, อ่านข้อมูลส่วนตัว)
JWT signing key / session secret / Flask SECRET_KEY
Private keys (เช่น OAuth client secret, service account key)
อะไรที่ “ไม่ใช่ secret” และอยู่ฝั่ง Frontend ได้
ค่า config ที่เป็น public เช่น base URL, feature flag บางชนิด, public analytics key (ที่ผู้ให้บริการออกแบบให้เปิดเผยได้)
OAuth client id มักไม่ถือเป็น secret (แต่ client secret เป็น secret)
Git และการป้องกันการหลุดเข้าระบบควบคุมเวอร์ชัน
ใส่ไฟล์ secret ใน .gitignore เช่น
- .env
- config.local.json
- .pem, .key
ใช้ pre-commit hook หรือเครื่องมือสแกน secret เพื่อกันพลาด (เช่นตรวจพบ pattern ของ key ก่อน commit)
ถ้าเผลอ commit ไปแล้ว:
- ถือว่า secret “รั่ว” แล้ว (แม้จะ revert ก็ยังอยู่ใน history)
- รีบ revoke/rotate key
- พิจารณา rewrite history (แต่ต้องเข้าใจผลกระทบกับทีม)
การเก็บ secret ระหว่าง Development
ใช้ Environment Variables
แนวทางที่พบบ่อยสุดคือเก็บ secret ใน environment variables แล้วให้ Flask อ่านจาก env
ข้อดี: ไม่ต้องใส่ลงโค้ด, แยก env ได้ง่าย
ข้อควรระวัง: อย่า log ค่า env ออกมา, และอย่าให้ dump env ใน error page
ใช้ไฟล์ .env ในเครื่องตัวเอง (dev only)
ใช้ .env สำหรับ dev และใส่ลง .gitignore
ทำไฟล์ตัวอย่าง (.env.example) ที่ไม่มี secret จริง เช่น
- DATABASE_URL=postgresql://USER:PASSWORD@HOST/DB (ใส่ placeholder)
- STRIPE_SECRET_KEY=... (placeholder)
การเก็บ secret ใน Production
ใช้ Secret Manager ของแพลตฟอร์ม
แนวปฏิบัติทั่วไป:
Docker/Kubernetes: ใช้ Kubernetes Secrets + mount เป็น env หรือไฟล์
Cloud: ใช้ secret manager (เช่น AWS/GCP/Azure) แล้วดึงตอนรัน
PaaS: ตั้งค่า secret ผ่าน dashboard/CLI (เช่น environment config ของ platform)
หลักสำคัญ:
จำกัดสิทธิ์การเข้าถึง secret manager ให้เฉพาะ service account ของแอป
audit log ได้ว่าใคร/อะไรเข้าถึง secret
หลีกเลี่ยงการวาง secret ไว้ใน image/build artifact
อย่า bake secret เข้า Docker image
อย่าให้ขั้นตอน build ของ React รับ secret จริง (เพราะจะถูก bundle ลงไฟล์ JS)
Flask: จุดที่เกี่ยวกับ secret โดยตรง
Flask SECRET_KEY และ session security
ตั้งค่า SECRET_KEY จาก env
ห้ามใช้ค่าเดิมระหว่างหลาย environment
หมุนคีย์แล้วกระทบ session (ผู้ใช้อาจถูก logout) — ถือเป็นเรื่องปกติ
Database credentials / connection string
อ่านจาก env เช่น DATABASE_URL
ให้ DB user มีสิทธิ์เท่าที่ต้องใช้ (แยก read/write ถ้าทำได้)
JWT / OAuth / API signing keys
เก็บ private/signing key ฝั่ง backend เท่านั้น
แยก key ต่อ environment
พิจารณาใช้ key id (kid) เพื่อรองรับ rotation หลายคีย์พร้อมกัน
React: สิ่งที่ต้องระวังเป็นพิเศษ
ทุกอย่างที่อยู่ใน React build “ไม่ลับ”
ค่าใน process.env ที่ถูก inject ตอน build จะไปอยู่ใน bundle
ต่อให้ obfuscate ก็ยังดึงได้
ถ้าต้องเรียกบริการภายนอก ให้ทำผ่าน Backend เป็น proxy
ตัวอย่างสถานการณ์:
React ต้องเรียก API ที่ต้องใช้ secret → ให้ React เรียก Flask ก่อน แล้ว Flask ใส่ secret เรียกต่อ
จะได้ควบคุม:
- rate limit
- validation
- logging/audit
- permission ตาม user
การตั้งค่าฝั่ง frontend แบบปลอดภัย
เก็บเฉพาะ public config เช่น REACT_APP_API_BASE_URL
ถ้าต้องการ runtime config ให้เสิร์ฟไฟล์ config จาก backend (ที่ไม่มี secret) แทนการ bake ตอน build
การส่ง secret ระหว่างบริการ (backend ↔ external services)
ใช้ HTTPS เสมอ
ตั้ง timeout และ retry policy (ลดโอกาสหลุด log แปลก ๆ)
อย่าใส่ secret ใน query string (URL) เพราะอาจหลุดใน:
- server access logs
- browser history
- referrer headers
Logging / Monitoring / Error handling
อย่า log secret (รวมถึง request headers เช่น Authorization)
ทำ redaction ใน logger:
- mask token เหลือท้าย 4 ตัว
- remove fields เช่น password, api_key, secret
ระวัง error page / stack trace ใน production:
- ปิด debug mode
- จัดการ exception แล้วตอบ error แบบไม่เผยข้อมูลภายใน
การจัดการสิทธิ์และการหมุนคีย์ (Rotation)
มีนโยบาย rotation เช่น ทุก 60–90 วัน (แล้วแต่ความเสี่ยง)
รองรับ rollout แบบไม่ downtime:
- backend ยอมรับทั้ง “คีย์เก่าและคีย์ใหม่” ช่วงหนึ่ง (dual keys)
- แล้วค่อย revoke คีย์เก่า
เก็บประวัติการออกคีย์ / revoke / ผู้รับผิดชอบ
การทดสอบและ CI/CD
ใน CI ให้ใช้ secret store ของระบบ CI (เช่น GitHub Actions Secrets / GitLab CI Variables)
แยก secret สำหรับ test/staging โดยเฉพาะ
ใน unit test หลีกเลี่ยงการใช้ secret จริง:
- mock external API
- ใช้ dummy key ที่ไม่มีสิทธิ์
Checklist แบบสั้น
- ไม่มี secret ใน repo (รวม history)
- ใช้ env vars / secret manager แทน hardcode
- React ไม่มี secret ใด ๆ ใน bundle
- Flask ปิด debug ใน production
- logger มีการ redaction (Authorization/password/api_key)
- แยก secret ต่อ environment + least privilege
- มีแผน rotation/revoke และทำได้จริง
ตัวอย่างโครงสร้างไฟล์ที่พบบ่อย
backend/
- .env (ignored)
- .env.example (commit ได้)
- app/config.py (อ่านค่าจาก env)
frontend/
- .env (มักไม่ใส่ secret; ถ้ามีให้เป็นแค่ public config)
- .env.production (ระวัง: ยังถือว่า public หลัง build)
- src/config.ts (อ่าน public config)
ถ้าคุณอยากได้ “ตัวอย่าง template” สำหรับ Flask config + วิธีเสิร์ฟ runtime config ให้ React (แบบไม่ bake ตอน build) บอกสแต็กที่ใช้ deploy (เช่น Docker/K8s/Render/Fly/EC2) เดี๋ยวผมเขียนให้เป็นแพตเทิร์นครบชุดได้เลย.