WELIFE Docs Swagger Admin Web

ENV & SECURITY AUDIT REPORT

Repo: D:\welife Date: 2026-07-31 Scope: Environment variables · secrets hygiene · Expo TLS · startup validation Domain: D-OPS · WP: WP-ENV-SEC-01 Final status: PASS_WITH_OWNER_ACTION


1. Executive summary

Metric Count
Biến phát hiện (code + compose + examples) ~85
Biến hợp lệ / đang dùng đúng scope ~55
Biến còn thiếu giá trị thật (OWNER_REQUIRED) 1+ (Mapbox; payments/SMS optional)
Biến không dùng trong runtime API 1 (WS_PUBLIC_URL — chỉ deploy/docs)
Biến sai phạm vi 0 (không có secret trong EXPO_PUBLIC_*)
Rủi ro bảo mật (cần chú ý) 4
Owner decisions required 3

2. Environment inventory

Variable Component Required Scope Status Action
COMPOSE_PROJECT_NAME root compose no public OK giữ
POSTGRES_USER/PASSWORD/DB/PORT root compose yes secret (pw) OK giữ; prod không dùng mẫu
REDIS_PORT/PASSWORD root compose yes secret (pw) OK giữ
MINIO_ROOT_USER/PASSWORD + ports + bucket root compose yes secret PRODUCTION_RISK app dùng S3_*; root = bootstrap
API_PORT root compose yes public OK giữ
MAILPIT_UI_PORT/SMTP_PORT root compose no public MISSING→OK đã bổ sung vào root .env
NODE_ENV PORT api yes public OK
API_PUBLIC_URL api yes public OK prod phải https
WS_PUBLIC_URL api no public UNUSED giữ cho deploy scripts; không đọc trong api/src
CORS_ORIGINS api yes (prod) public OK local * prod cấm * (assertSecureBoot)
TRUST_PROXY BODY_JSON_LIMIT api no public OK
SWAGGER_ENABLED HEALTH_DETAIL_ENABLED HTTP_DEBUG api no public OK
JWT_ACCESS_SECRET JWT_REFRESH_SECRET api yes secret OK khác nhau; ≥32 prod
JWT_ACCESS_TTL JWT_REFRESH_TTL api no public OK
OTP_* OTP_DEV_CODE api partial secret (dev code) OK local / INSECURE prod prod cấm OTP_DEV_CODE
SMTP_* api email channel secret (pass) OK
SMTP_ALLOW_NOAUTH api no public OK assertSecureBoot
ADMIN_EMAILS/PHONES ADMIN_BOOTSTRAP_ENABLED api conditional public OK
ALLOW_INTERNAL_WALLET_CREDIT api no public PRODUCTION_RISK nếu true warn prod
DATABASE_URL + DB_* api yes secret DUPLICATED by design DATABASE_URL canonical Prisma; DB_* health
REDIS_URL + host/port/password api yes secret DUPLICATED by design URL ưu tiên
S3_* api yes secret OK map MinIO
MAPBOX_ACCESS_TOKEN api no (fallback) secret OWNER_REQUIRED trống → Haversine
PAYMENTS_MOCK + MoMo/VNPay api conditional secret OWNER_REQUIRED mock=true local
SMS_PROVIDER + eSMS/Twilio/HTTP api conditional secret OWNER_REQUIRED console local
OPENAI_* api no secret OWNER_REQUIRED optional assistant
EXPO_PUBLIC_API_URL mobile yes public OK hiện https production
EXPO_PUBLIC_WS_URL mobile yes public OK https (Socket.IO → WSS)
EXPO_PUBLIC_ENV mobile yes public OK production trên máy dev
EXPO_PUBLIC_API_DEBUG mobile no public OK code dùng; optional
EXPO_PUBLIC_MAPBOX_TOKEN mobile no public UNUSED không có code FE dùng
Driver/Partner EXPO_PUBLIC_* driver/partner yes public OK

Không thêm (không có code): PASSWORD_PEPPER, DATA_ENCRYPTION_KEY, RATE_LIMIT_*, FCM_*, APNS_*, EXPO_ACCESS_TOKEN, WS_HEARTBEAT_*, NODE_TLS_REJECT_UNAUTHORIZED.


3. Files created


4. Files modified


5. Variables added

Name Reason File Secret shown?
MAILPIT_UI_PORT compose profile mail/full root .env no
MAILPIT_SMTP_PORT compose root .env no
MAPBOX_ACCESS_TOKEN= (empty) routing code api/.env n/a empty
WS_PUBLIC_URL deploy parity api/.env no
HTTP_DEBUG interceptor api/.env no

6. Variables still missing

Variable Required by Reason missing Owner action
MAPBOX_ACCESS_TOKEN routing.service.ts chưa cấp token dán token Mapbox vào api/.env (+ VPS) rồi restart API
MoMo / VNPay secrets payments.service.ts PAYMENTS_MOCK=true khi go-live: điền + PAYMENTS_MOCK=false
eSMS / Twilio sms.service.ts SMS_PROVIDER=console khi OTP SMS prod
OPENAI_API_KEY assistant optional chỉ khi bật AI

7. Security findings

Severity Finding Location Remediation
Medium Node TLS leaf verify fail (AV/corp CA) Windows Node 24 NODE_OPTIONS=--use-system-ca (user/profile) — không tắt TLS
Medium App S3 dùng credential trùng MinIO root pattern compose + S3_* tạo MinIO service account least-privilege (migration có chủ đích)
Low SSLKEYLOGFILE set trong user env Windows Env bỏ nếu không debug TLS (có thể lộ key material)
Low Mobile EXPO_PUBLIC_ENV=production trên máy dev mobile/.env* OK nếu test prod API; local device → đổi LAN URL + development
Info WS_PUBLIC_URL unused in Nest api/.env giữ cho deploy hoặc xóa sau khi dọn scripts
Info MinIO root trong root .env compose chấp nhận local; prod rotate nếu từng lộ

Rejected unsafe workaround: NODE_TLS_REJECT_UNAUTHORIZED=0 — assertSecureBoot chặn.


8. Secret rotation required

Credential Reason Rotation impact
Không bắt buộc ngay Không thấy .env trong Git history tracked —
Mapbox (khi cấp) chưa có không impact session
MinIO app key (khuyến nghị) least privilege cần update S3_* + restart
JWT prod chỉ nếu từng commit/leak mọi session logout — cần phê duyệt

9. Validation results

Check Result
npm run env:check PASS (Mapbox WARN)
docker compose config --quiet PASS (exit 0; output không dump vào báo cáo)
npm run api:build PASS
Expo Doctor 17/18 — expo-location major mismatch (ngoài scope ENV)
Expo offline (--offline --clear) PASS — Metro Waiting on http://localhost:8081; load .env.local .env
Expo online PASS với NODE_OPTIONS=--use-system-ca (doctor chạy được)
TLS fetch(expo.dev) default FAIL leaf certificate
TLS + --use-system-ca PASS 200
API connectivity (port 3000) free lúc audit (API chưa chạy)
WebSocket không smoke live; URL scheme mobile = https (Socket.IO OK)

10. TLS finding

[System.Environment]::SetEnvironmentVariable('NODE_OPTIONS','--use-system-ca','User')

Hoặc mỗi session: $env:NODE_OPTIONS='--use-system-ca' trước npx expo … / npm ….


11. Remaining blockers

  1. OWNER: MAPBOX_ACCESS_TOKEN trống → directions ước lượng.
  2. OWNER: Cấu hình TLS Windows bền vững (NODE_OPTIONS=--use-system-ca).
  3. Optional: npx expo install expo-location align SDK (không phải ENV).
  4. Device thật: LAN 192.168.1.109 — nếu không dùng prod API, set:
EXPO_PUBLIC_ENV=development
EXPO_PUBLIC_API_URL=http://192.168.1.109:3000/api/v1
EXPO_PUBLIC_WS_URL=http://192.168.1.109:3000

12. Final status

PASS_WITH_OWNER_ACTION

Lý do không PASS thuần: Mapbox token + TLS user-level config + (khuyến nghị) MinIO least-privilege chưa thực hiện; không tự bịa credential ngoài.


Appendix — Socket.IO vs wss://

Client WELIFE dùng EXPO_PUBLIC_WS_URL dạng https://host (Socket.IO). Transport nâng cấp lên WSS tự động. Không bắt buộc đổi sang scheme wss:// trừ khi đổi client stack.