ข้ามไปที่เนื้อหา

ALTCHA Anti-Bot (Proof-of-Work) — มาตรฐานกลางสำหรับแบบฟอร์มสาธารณะ

สถานะ (12 ส.ค. 2569): ใช้งานจริงครบทุกแบบฟอร์มสาธารณะของ S4 eMediation — ยื่นคำร้อง (/public/request, /public/request-ap), สมัครผู้ไกล่เกลี่ย, ยื่นจัดตั้งศูนย์ — captcha บวกเลข/รหัสภาพเดิมถูกถอดออกทั้งหมด. โมดูลอยู่ในไลบรารีกลาง @rlpdxmfec-devcode/rlpdjs@0.8.0ทุกระบบ S1–S8 นำไปใช้ได้ทันที.

ทำไมต้อง ALTCHA (นอกเหนือ TOR — ไม่มีค่าใช้จ่ายเพิ่ม)

ประเด็น captcha เดิม (บวกเลข/รหัสภาพ) ALTCHA proof-of-work
ความปลอดภัย bot อ่านโจทย์จาก client ได้ → ผ่านอัตโนมัติ ต้องเผา CPU แก้โจทย์ crypto ต่อ 1 คำขอ; ตรวจซ้ำฝั่ง server เสมอ
Self-hosted ✅ 100% — ไม่มี Cloudflare/Google/hCaptcha, ไม่มี CDN, ไม่มีบัญชีภายนอก
PDPA ✅ ไม่มี cookie/fingerprint/tracking
การเข้าถึง (WCAG 2.2 AA) ⚠️ ต้องอ่านเลข/ภาพ ✅ คลิกเดียว, คีย์บอร์ด/screen reader, ภาษาไทย
ใช้ซ้ำ/Replay กันด้วย jti ในหน่วยความจำ payload ใช้ครั้งเดียว + หมดอายุ 5 นาที (ผูกลายเซ็น HMAC)

สถาปัตยกรรม: server ออก challenge (HMAC-signed, มีอายุ) → widget ในเบราว์เซอร์แก้โจทย์ PBKDF2 ใน web worker → ส่ง payload มากับฟอร์ม → server ตรวจ (ลายเซ็น + PoW + หมดอายุ + single-use) ก่อนเขียนฐานข้อมูลเสมอ — สถานะ "verified" ฝั่ง client เป็นแค่ UX.

การนำไปใช้ในระบบอื่น (S1–S8) — 4 ขั้น

ไลบรารี: ใช้ vendored tarball เป็นมาตรฐาน (npm pack จาก rlpdjs mainvendor/…tgz → dependency แบบ file: — ไม่ต้องมี registry token บนเครื่อง build; ดู rlpdjs SKILL.md).

  1. ลงทะเบียนโมดูล (app.module.ts): AltchaModule.forRoot({ ...altchaOptionsFromEnv(), hmacKey: <server-only secret> })ALTCHA_HMAC_KEY สร้างด้วย openssl rand -base64 48; ระบบ ไม่ boot ใน production ถ้าไม่ตั้ง.
  2. Endpoint แจก challenge — ต้องตอบ JSON ดิบ (ห้ามครอบ envelope): res.setHeader('Cache-Control','no-store'); res.json(await altcha.createChallenge());
  3. ป้องกัน endpoint ธุรกิจ — DTO เพิ่ม @IsString() @IsNotEmpty() @MaxLength(4096) altcha!: string; แล้วใน service: if (!(await altcha.verify(dto.altcha)).ok) throw <400>; ก่อน side effect ทุกครั้ง.
  4. Frontendnpm i altcha; ใช้ <AltchaWidget> ตัวอย่างที่ emediation/frontend/src/components/common/altcha.jsx (Thai i18n, mock/test mode, reset หลัง submit เพราะ payload ใช้ครั้งเดียว).

ข้อกำหนดตอน Deploy (สำคัญ)

  • ALTCHA_HMAC_KEY ต้องถูกตั้งใน env ของ UAT/production ก่อน deploy build ที่มี ALTCHA (fail-fast by design — ไม่ตั้ง = service ไม่ start). ห้าม commit ค่าใน repo, ห้าม log.
  • TRUST_PROXY_HOPS=1 ในทุก deploy ที่อยู่หลัง nginx/reverse proxy — ไม่งั้น rate-limit ต่อ IP จะนับ IP ของ proxy แทน client (ตั้งเป็นจำนวน hop จริง ห้ามตั้ง true).
  • ปรับความยาก/อายุได้ผ่าน env: ALTCHA_COST (5000), ALTCHA_COUNTER_MIN/MAX (5000/10000), ALTCHA_TTL_MS (300000). ระบบหลาย instance: ส่ง replayStore (Redis/DB) แทน in-memory (ความเสี่ยงตกค้างถูกจำกัดด้วย TTL 5 นาทีอยู่แล้ว).

หลักฐานการตรวจสอบ (S4, 12 ส.ค. 2569)

  • rlpdjs jest 33/33 (รวมเคสโจมตี: payload ถูกแก้, กุญแจผิด, หมดอายุ, ใช้ซ้ำ) · S4 backend e2e 36/36 (journey public-intake-altcha 10 จุดตรวจ ครบ 3 endpoint) · Playwright เบราว์เซอร์จริง: แก้ PoW + ยื่นคำร้องสำเร็จ (HTTP 201) + widget reset.
  • ผ่าน security review (ไม่พบช่องโหว่ exploitable) และ code review อิสระ.
  • โค้ด: rlpdjs src/altcha/ (commit 3cdaed8, เผยแพร่เป็น 0.8.0) · emediation commits 164d4c5 + f35e912 · แผนงาน emediation/plans/altcha-anti-bot.md.

S6 OCIPA — ใช้ ALTCHA ผ่าน "central captcha service" ของ S9 (ต่างจาก S4)

สถานะ (30 ส.ค. 2569): ใช้งานจริงบน UAThttps://uat-ocipa-front.rlpd.go.th แบบฟอร์มประชาชน (/ocipa วิซาร์ดยื่นคำขอ + OTP, ติดตามสถานะ, /rightsnotification/submit สชง.1/03) มี ALTCHA gate ครบ. e2e 4/4 ผ่าน (challenge 200, ไม่ติ๊ก → Ref1=CAPTCHA, payload ที่แก้แล้วผ่าน + replay ถูกปฏิเสธ).

S6 ocipa เป็น .NET Framework 4.7.2 (legacy) รันแยกไม่ใช่บนสแตก Node — จึง รัน rlpdjs verifier เองไม่ได้. แทนที่จะฝัง verifier, ocipa เป็น ผู้บริโภค ของ central captcha service บน S9 web_portal BFF:

  • widget เรียก /captcha/challenge ของ ocipa เอง (same-origin) ซึ่ง proxy ต่อไปที่ BFF GET {base}/captcha/altcha/challenge (เลี่ยง CORS)
  • ฝั่ง server ของ ocipa (AltchaVerifier) POST payload แบบ server-to-server ไปที่ BFF POST {base}/captcha/altcha/verify พร้อม header x-api-key (คีย์รายระบบ) ก่อน side effect
  • BFF ยังคงเป็นเจ้าของ HMAC/PoW/single-use เหมือน S4 — ocipa แค่ถามผลว่า {ok:true|false}

คีย์คอนฟิก (ชื่อ env เท่านั้น — ค่าลับอยู่ที่ credentials/uat_devphase2_sso_secrets.md)

ฝั่ง ที่ตั้งค่า (แหล่งจริง = GitHub pipeline env uat) ชื่อ ความหมาย
S9 BFF secret WEBPORTAL_CAPTCHA_API_KEYS คีย์รายระบบ app:key (คั่นด้วย comma) — ที่นี่คือ ocipa:<คีย์>
S9 BFF container rlpd-webportal-be (.155) CAPTCHA_API_KEYS มาจาก secret ข้างบน (compose deploy/webportal/uat) — ไม่ตั้ง = verify ตอบ 503
S6 ocipa secret OCIPA_CAPTCHA_API_KEY คีย์เดิม (raw ไม่มี prefix) ที่ ocipa ส่งใน x-api-key
S6 ocipa variable OCIPA_CAPTCHA_BASE_URL base URL ของ BFF รวม /api = https://uat-webportal.rlpd.go.th/api
S6 ocipa เครื่อง .156 machine env OCIPA_CAPTCHA_API_KEY + secrets.config CaptchaServiceBaseUrl render โดย ocipa.yml จาก secret/variable ข้างบน

OCIPA_CAPTCHA_API_KEY (=<คีย์>) และ WEBPORTAL_CAPTCHA_API_KEYS (=ocipa:<คีย์>) ต้องเป็น คีย์เดียวกัน ไม่งั้น verify 401. ทั้งสองด้าน "ครึ่ง ๆ" = gate ปิด (ไม่ fail-closed) — ตั้งครบทั้งคู่เสมอ.

⚠️ กับดัก TLS 1.2 (สาเหตุอาการ "ตั้งค่าครบแล้วแต่ challenge ยัง 502")

ocipa (.156) เป็น .NET Framework ที่ตั้ง <httpRuntime targetFramework="4.5.2"> → ดีฟอลต์ ServicePointManager เป็น TLS 1.0; แต่ nginx ของ BFF รับ เฉพาะ TLS 1.2 → การเรียก server-to-server ทุกครั้ง throw "Could not create SSL/TLS secure channel" แล้ว CaptchaController ตอบ 502 (gate ใช้ไม่ได้ทั้งที่ค่าถูกครบ). แก้แบบ machine-wide บน .156: SchUseStrongCrypto=1 + SystemDefaultTlsVersions=1 (DWORD) ใต้ HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319 และ WOW6432Node (app pool รันแบบ 32-bit) แล้ว recycle app pool. ทำให้ถาวรใน pipeline deploy/ocipa/uat/deploy-iis.ps1.

วิธีตรวจ / รันซ้ำ

cd RLPD_REPOS/ocipa/e2e && npm install && OCIPA_BASE_URL=https://uat-ocipa-front.rlpd.go.th npx playwright test (ต้องต่อ VPN). โค้ด/แผน: ocipa CommonProvide/AltchaVerifier.cs · Controllers/CaptchaController.cs · Views/Shared/_AltchaWidget.cshtml · แผนงาน ocipa/plans/2026-08-16-altcha-public-form-gate.md · BFF webportal/backend/src/{middlewares/CaptchaApiKey.ts,controllers/core/captcha} · pipeline deploy/{webportal,ocipa}/uat/.