DEV Community

Cover image for Defensive Programming can be very easy with Symfony HttpClient
Stiven Llupa
Stiven Llupa

Posted on

Defensive Programming can be very easy with Symfony HttpClient

If your app lets users submit URLs that you then fetch, for link previews, webhooks, or RSS feeds, you have a serious security problem waiting to happen.

An attacker can submit something like http://127.0.0.1/admin or http://169.254.169.254 (AWS metadata endpoint) and your server will happily fetch it.

That's called a Server-Side Request Forgery attack, or SSRF.

Symfony has a built-in solution: NoPrivateNetworkHttpClient.

So your code:

class LinkPreviewController
{
    public function __construct(
        private HttpClientInterface $client,
    ) {}

    #[Route('/api/preview', methods: ['POST'])]
    public function preview(
        Request $request,
    ): JsonResponse {
        $url = $request->getPayload()
            ->getString('url');

        $response = $this->client
            ->request('GET', $url);

        return new JsonResponse(
            $response->getContent()
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

Becomes:

class LinkPreviewController
{
    private HttpClientInterface $safeClient;

    public function __construct(
        HttpClientInterface $client,
    ) {
        $this->safeClient =
            new NoPrivateNetworkHttpClient(
                $client
            );
    }

    #[Route('/api/preview', methods: ['POST'])]
    public function preview(
        Request $request,
    ): JsonResponse {
        $url = $request->getPayload()
            ->getString('url');

        $response = $this->safeClient
            ->request('GET', $url);

        return new JsonResponse(
            $response->getContent()
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

It's a decorator, so you just wrap your existing client & no other changes needed. Follow the documentation to set your specific IPs or IP ranges that you want to disallow.

If you fetch user-submitted URLs and you're not using this, you should be.

Watch in on YouTube

Top comments (0)