Files
clinicpro/docs/api/captcha.md
T
hamed c3c520801d feat(captcha): add ALTCHA configuration endpoint and integrate into HomeController
- Introduced a new endpoint `/api/v1/altcha/config` in CaptchaController to return the status of the ALTCHA captcha.
- Updated HomeController to inject AltchaService and pass the captcha status to the home page template.
- Modified the home.html.twig template to conditionally render the ALTCHA widget based on the captcha status.
- Updated manifest.json and cache files to reflect changes in the codebase.
2026-07-10 11:18:40 +03:30

4.0 KiB

Captcha API (ALTCHA)

کپچای خودمیزبان مبتنی بر ALTCHA (Proof-of-Work، بدون تصویر، بدون سرویس خارجی). برای محافظت از endpointهای عمومی و بدون احراز هویت در برابر اسپم/بات.

  • فعال/غیرفعال با ALTCHA_ENABLED (پیش‌فرض false در dev/test، در prod باید true شود).
  • وقتی غیرفعال است، همه‌ی endpointها بدون فیلد altcha کار می‌کنند.

GET /api/v1/altcha/challenge

صدور یک challenge امضاشده برای حل proof-of-work سمت مرورگر. عمومی (بدون توکن).

Response 200

{
  "algorithm": "SHA-256",
  "challenge": "417af7b4d4b661c437b99c7e4d5d55747d36f01a8c28d77013af95683b71e642",
  "maxnumber": 100000,
  "salt": "1d5e0b983af8930ed6d4081f?expires=1783665554&",
  "signature": "5d797fdbe9d9cd322fdb02c95c4a673ea92fae09bb63122091713895d0897a11"
}
  • signature — HMAC-SHA256 روی challenge با کلید سرور (ALTCHA_HMAC_KEY). قابل جعل نیست.
  • salt شامل expires است؛ پس از انقضا challenge نامعتبر می‌شود.
  • خروجی مستقیماً به <altcha-widget> داده می‌شود (پاسخ خام است، نه envelope استاندارد).

GET /api/v1/altcha/config

وضعیت فعال بودن کپچا. عمومی. کلاینت‌ها قبل از رندر widget این را می‌خوانند؛ اگر enabled=false باشد، هیچ widgetی نشان داده نمی‌شود و فرم‌ها بدون فیلد altcha ارسال می‌شوند (بک‌اند هم no-op است).

Response 200

{ "enabled": false }

اعمال کپچا روی endpointهای محافظت‌شده

کلاینت مقدار حل‌شده‌ی widget (base64) را با کلید altcha در بدنه‌ی همان درخواست می‌فرستد:

{ "mobile": "09120000000", "altcha": "eyJhbGdvcml0aG0iOiJTSEEt..." }

endpointهایی که وقتی ALTCHA_ENABLED=true است فیلد altcha را الزامی می‌کنند:

Endpoint Method
/api/v1/user/send-code POST
/api/v1/user/register POST
/api/v1/user/otp-login POST
/api/v1/user/reset-password POST
/api/v1/pre-registration POST
/api/v1/user/login POST

ورود با رمز عبور (/api/v1/user/login) توسط PasswordAuthenticator قبل از controller intercept می‌شود؛ کپچا داخل authenticate() (بعد از rate-limit) با CaptchaGuard::assertValid() بررسی می‌شود.

endpointهای امتیاز/نظر (POST /api/v1/rate، POST /api/v1/comment) پشت JWT هستند (کاربر لاگین‌شده)، بنابراین کپچا نمی‌گیرند — بات برای رسیدن به آن‌ها باید توکن معتبر داشته باشد که خودش از مسیر OTP (کپچا‌دار) عبور می‌کند.

خطای اعتبارسنجی کپچا 422

{
  "success": false,
  "data": null,
  "errors": [{ "code": "ERR_CAPTCHA_001", "field": "altcha", "message": "تأیید امنیتی ناموفق بود. لطفاً صفحه را رفرش کنید و دوباره تلاش کنید" }]
}

این خطا زمانی برمی‌گردد که altcha غایب، نامعتبر، منقضی، یا قبلاً استفاده شده (replay) باشد. هر challenge فقط یک‌بار معتبر است (امضایش در Redis سوزانده می‌شود).


امنیت

  • Challenge با HMAC-SHA256 و کلید سرور امضا می‌شود؛ کلید هرگز به client نمی‌رود.
  • انقضا (ALTCHA_EXPIRE_SECONDS) داخل salt امضاشده است.
  • استفاده‌ی یک‌باره: signature هر challenge در pool اختصاصی Redis (altcha.pool) تا زمان انقضا نگه‌داری می‌شود → ضدِ replay.
  • کپچا مکملِ rate-limit موجود است، نه جایگزین آن.