Files
clinicpro/docs/api/captcha.md
T
hamed ec184dfcc8 Add AST cache files for AltchaService, API documentation, and AltchaService tests
- Created JSON representation of AltchaService class and its methods, including imports and relationships.
- Added documentation for the Captcha API, detailing endpoints and responses.
- Introduced test cases for AltchaService, covering various functionalities and edge cases.
2026-07-10 11:42:23 +03:30

4.4 KiB

Captcha API (ALTCHA)

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

  • فعال/غیرفعال از دو جا کنترل می‌شود:
    1. پنل ادمین (/admin/settings → عمومی → کپچای امنیتی) — کلید altcha_enabled در site_config.
    2. متغیر محیطی ALTCHA_ENABLED (پیش‌فرض/fallback).
    • اولویت: اگر کلید پنل ست شده باشد مقدم است؛ در غیر این صورت به ALTCHA_ENABLED برمی‌گردد.
  • وقتی غیرفعال است، همه‌ی endpointها بدون فیلد altcha کار می‌کنند و کلاینت‌ها (از /altcha/config) widget را نشان نمی‌دهند.

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 موجود است، نه جایگزین آن.