No SSRF protection for HTTP clienttt. #61237
|
Laravel Version PHP Version Database Driver & Version Description Symfony has an implementation for this problem: https://symfony.com/blog/new-in-symfony-5-1-server-side-request-forgery-protection Steps To Reproduce |
Replies: 2 comments
|
I don't think you should manually verify the Passport access token using Laravel Passport already handles access-token validation through its authentication guard. You can protect your API route with Passport: Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:api');Then send the token as a Bearer token: Authorization: Bearer YOUR_ACCESS_TOKENPassport will handle the token parsing and signature/expiration/revocation checks for you. If you really need to inspect or verify the JWT manually, you should use a JWT library and verify it using Passport's configured public key and the algorithm specified by the token, rather than recreating the signature with So the main issue with the current implementation is that it is trying to reproduce Passport's token verification logic manually. |
ProblemLaravel's
Since Laravel doesn't validate or restrict the destination host by default, this is a classic SSRF vector any time a URL from user input reaches the HTTP client. Why it's not built inGuzzle (and thus Laravel's client) is a general-purpose HTTP client — restricting destinations by default would break legitimate internal API calls, so Laravel leaves this to the developer. But there's no opt-in helper either, which means most developers roll their own (often incorrectly, e.g. missing IPv6/DNS-rebinding checks). Suggested improvements
Http::withOptions([
'allow_redirects' => false, // prevent redirect-based bypass
])->withMiddleware(BlockPrivateIps::class)
->get($url);
Http::ssrfSafe()->get($url); // rejects private/reserved IP ranges, link-local, loopbackShould resolve DNS first and check the resolved IP (not just the hostname string) against RFC 1918 / loopback / link-local ranges, and re-check on every redirect hop (to stop DNS-rebinding and open-redirect bypasses).
Workaround todayuse GuzzleHttp\RequestOptions;
$ip = gethostbyname(parse_url($url, PHP_URL_HOST));
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
abort(422, 'URL not allowed');
}This must run after DNS resolution and be re-checked on redirects, or it's bypassable. |
I don't think you should manually verify the Passport access token using
hash_hmac().Laravel Passport already handles access-token validation through its authentication guard. You can protect your API route with Passport:
Then send the token as a Bearer token:
Authorization: Bearer YOUR_ACCESS_TOKENPassport will handle the token parsing and signature/expiration/revocation checks for you.
If you really need to inspect or verify the JWT manually, you should use a JWT library and verify it using Passport's configured public key and the algorithm specified by the token, rather than rec…