fix(security): SSRF-гард на сохранении webhook target_url (защита будущей доставки)
- update(): WebhookUrlGuard блокирует сохранение private/reserved/loopback IP → 422 validation error на target_url; небезопасные адреса не попадают в БД, любой будущий потребитель (test() + outbound-доставка) читает только безопасные - NB: будущая outbound-доставка обязана ВДОБАВОК звать guard перед отправкой (DNS-rebinding); outbound-pipeline пока не построен (комментарий в update()) - тесты: +PUT private-IP→422 не сохраняет; webhook target_url → публичные IP-литералы (убрал DNS-резолюцию example.ru-хостов, webhook-suite 93s→5s) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
@@ -54,6 +55,16 @@ class WebhookSettingsController extends Controller
|
||||
'target_url' => ['required', 'string', 'url', 'max:2048', 'starts_with:https://'],
|
||||
]);
|
||||
|
||||
// SSRF-гард на сохранении: не даём записать URL во внутреннюю/служебную
|
||||
// сеть — тогда любой будущий потребитель (test() + будущая outbound-доставка
|
||||
// событий) читает из БД только безопасные адреса. NB: будущая доставка
|
||||
// обязана ВДОБАВОК звать WebhookUrlGuard перед отправкой (защита от
|
||||
// DNS-rebinding: хост сохранён публичным, позже переразрешается в приватный).
|
||||
$blockReason = WebhookUrlGuard::blockReason($validated['target_url']);
|
||||
if ($blockReason !== null) {
|
||||
throw ValidationException::withMessages(['target_url' => [$blockReason]]);
|
||||
}
|
||||
|
||||
$sub = $this->currentSubscription($request);
|
||||
$plainSecret = null;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user