ผลต่างระหว่างรุ่นของ "01204223/secrets"
Jittat (คุย | มีส่วนร่วม) |
Jittat (คุย | มีส่วนร่วม) |
||
| แถว 148: | แถว 148: | ||
=== การจัดการสิทธิ์และการหมุนคีย์ (Rotation) === | === การจัดการสิทธิ์และการหมุนคีย์ (Rotation) === | ||
| − | มีนโยบาย rotation เช่น ทุก 60–90 วัน (แล้วแต่ความเสี่ยง) | + | * มีนโยบาย rotation เช่น ทุก 60–90 วัน (แล้วแต่ความเสี่ยง) |
| − | + | * รองรับ rollout แบบไม่ downtime: | |
| − | รองรับ rollout แบบไม่ downtime: | ||
** backend ยอมรับทั้ง “คีย์เก่าและคีย์ใหม่” ช่วงหนึ่ง (dual keys) | ** backend ยอมรับทั้ง “คีย์เก่าและคีย์ใหม่” ช่วงหนึ่ง (dual keys) | ||
** แล้วค่อย revoke คีย์เก่า | ** แล้วค่อย revoke คีย์เก่า | ||
| แถว 158: | แถว 157: | ||
=== การทดสอบและ CI/CD === | === การทดสอบและ CI/CD === | ||
| − | ใน CI ให้ใช้ secret store ของระบบ CI (เช่น GitHub Actions Secrets / GitLab CI Variables) | + | * ใน CI ให้ใช้ secret store ของระบบ CI (เช่น GitHub Actions Secrets / GitLab CI Variables) |
| − | + | * แยก secret สำหรับ test/staging โดยเฉพาะ | |
| − | แยก secret สำหรับ test/staging โดยเฉพาะ | + | * ใน unit test หลีกเลี่ยงการใช้ secret จริง: |
| − | |||
| − | ใน unit test หลีกเลี่ยงการใช้ secret จริง: | ||
** mock external API | ** mock external API | ||
** ใช้ dummy key ที่ไม่มีสิทธิ์ | ** ใช้ dummy key ที่ไม่มีสิทธิ์ | ||
| แถว 178: | แถว 175: | ||
=== ตัวอย่างโครงสร้างไฟล์ที่พบบ่อย === | === ตัวอย่างโครงสร้างไฟล์ที่พบบ่อย === | ||
| − | ''backend/'' | + | * ''backend/'' |
** ''.env'' (ignored) | ** ''.env'' (ignored) | ||
** ''.env.example'' (commit ได้) | ** ''.env.example'' (commit ได้) | ||
** ''app/config.py'' (อ่านค่าจาก env) | ** ''app/config.py'' (อ่านค่าจาก env) | ||
| − | ''frontend/'' | + | * ''frontend/'' |
** ''.env'' (มักไม่ใส่ secret; ถ้ามีให้เป็นแค่ public config) | ** ''.env'' (มักไม่ใส่ secret; ถ้ามีให้เป็นแค่ public config) | ||
** ''.env.production'' (ระวัง: ยังถือว่า public หลัง build) | ** ''.env.production'' (ระวัง: ยังถือว่า public หลัง build) | ||
รุ่นแก้ไขเมื่อ 16:04, 26 กุมภาพันธ์ 2569
เราจะฝึกจัดการกับข้อมูลที่เป็นความลับในการพัฒนาเว็บแอพ
เนื้อหา
- 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) เดี๋ยวผมเขียนให้เป็นแพตเทิร์นครบชุดได้เลย.