- 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.
4.4 KiB
Captcha API (ALTCHA)
کپچای خودمیزبان مبتنی بر ALTCHA (Proof-of-Work، بدون تصویر، بدون سرویس خارجی). برای محافظت از endpointهای عمومی و بدون احراز هویت در برابر اسپم/بات.
- فعال/غیرفعال از دو جا کنترل میشود:
- پنل ادمین (
/admin/settings→ عمومی → کپچای امنیتی) — کلیدaltcha_enabledدرsite_config. - متغیر محیطی
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 موجود است، نه جایگزین آن.