Cómo Solucionar el Bloqueo de Imágenes HTTP en Sitios HTTPS mediante un Proxy en Symfony

Cómo Solucionar el Bloqueo de Imágenes HTTP en Sitios HTTPS mediante un Proxy en Symfony

  • Ruben
El Problema: Contenido Mixto (Mixed Content)
Al integrar recursos externos (como imágenes) en un sitio HTTPS, los navegadores bloquean aquellos servidos mediante HTTP por razones de seguridad. Este escenario es común al crear lectores RSS que consumen fuentes con enlaces HTTP, resultando en imágenes rotas

La Solución: Un Proxy de Imágenes en el Backend
La estrategia consiste en crear un proxy que descargue las imágenes desde HTTP y las sirva a través de HTTPS, evitando el bloqueo. A continuación, detallamos una implementación en Symfony.

Paso 1: Crear el Controlador Proxy
#[Route(path: '/images/{url}', name: 'image_proxy', methods: ['GET'])]
public function proxyImage(
    Request $request, 
    string $url,
    CacheInterface $cache // Inyectar servicio de caché
): StreamedResponse|RedirectResponse {
    $targetUrl = urldecode(base64_decode($url));

    // Validaciones de Seguridad
    if (!$this->isValidUrl($targetUrl) || !$this->isUrlAllowed($targetUrl)) {
        throw $this->createNotFoundException('URL no permitida o inválida');
    }

    // Redirigir directamente si es HTTPS
    if (str_starts_with($targetUrl, 'https')) {
        return new RedirectResponse($targetUrl);
    }

    // Usar caché para evitar descargas repetidas
    $cacheKey = md5($targetUrl);
    if ($cachedImage = $cache->get($cacheKey)) {
        return new Response($cachedImage, 200, ['Content-Type' => 'image/jpeg']);
    }

    try {
        $response = $this->httpClient->request('GET', $targetUrl, [
            'headers' => $this->getBrowserLikeHeaders()
        ]);

        $content = $response->getContent();
        $cache->set($cacheKey, $content, 86400); // Cachear por 24h

        return new StreamedResponse(
            function () use ($response) {
                foreach ($this->httpClient->stream($response) as $chunk) {
                    echo $chunk->getContent();
                    flush();
                }
            },
            $response->getStatusCode(),
            [
                'Content-Type' => $response->getHeaders()['content-type'][0] ?? 'application/octet-stream',
                'Cache-Control' => 'public, max-age=86400'
            ]
        );
    } catch (\Exception $e) {
        // Opcional: Registrar el error en logs
        return new RedirectResponse('https://titulares.mrk-cu.com/text_titulares.png');
    }
}

// Validar formato de URL
private function isValidUrl(string $url): bool {
    return filter_var($url, FILTER_VALIDATE_URL) !== false;
}

// Restringir dominios permitidos (ajustar según necesidades)
private function isUrlAllowed(string $url): bool {
    $allowedHosts = ['.cu', 'dominio-permitido.com']; 
    $host = parse_url($url, PHP_URL_HOST);
    foreach ($allowedHosts as $allowed) {
        if (str_ends_with($host, $allowed)) return true;
    }
    return false;
}

// Headers para evitar bloqueos
private function getBrowserLikeHeaders(): array {
    return [
        'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64)...',
        'Accept' => 'image/webp,image/apng,image/*,*/*;q=0.8',
        'Referer' => 'https://google.com'
    ];
}

Paso 2: Integrar el Proxy en Twig

<img 
    src="{{ image_url starts with 'http://' 
        ? path('image_proxy', {url: image_url|url_encode|base64_encode}) 
        : image_url }}" 
    alt="Imagen"
>

Requiere un filtro personalizado para base64_encode.

Mejoras Clave Implementadas
  1. Seguridad Reforzada
    • Validación estricta de URLs con filter_var().
    • Lista blanca de dominios permitidos (evita SSRF).
  2. Optimización con Caché
    • Almacena imágenes temporalmente para reducir peticiones al servidor externo y mejorar rendimiento.
  3. Manejo de Errores
    • Redirige a una imagen por defecto en fallos, evitando rupturas en la UI.
Consideraciones Finales
  • Ajusta la Caché: Modifica max-age según la frecuencia de actualización de las imágenes.
  • Monitorización: Implementa logs para rastrear errores o intentos de acceso a URLs no permitidas.
  • Escalabilidad: Para alto tráfico, considera usar un CDN o almacenamiento en cloud para las imágenes cacheadas.

Con este enfoque, resolverás el problema de contenido mixto manteniendo la seguridad y eficiencia de tu aplicación.
  • Desarrollo
  • Symfony
Deja un comentario: