Tratar con contenido HTTP en páginas HTTPS

89

Tenemos un sitio al que se accede completamente a través de HTTPS, pero a veces muestra contenido externo que es HTTP (imágenes de fuentes RSS, principalmente). La gran mayoría de nuestros usuarios también están atrapados en IE6.

Idealmente me gustaría hacer las dos siguientes

  • Evite el mensaje de advertencia de IE sobre contenido inseguro (para que pueda mostrar uno menos intrusivo, por ejemplo, reemplazando las imágenes con un icono predeterminado como se muestra a continuación)
  • Presentar algo útil a los usuarios en lugar de las imágenes que de otro modo no podrían ver; si hubiera algo de JS que pudiera ejecutar para averiguar qué imágenes no se han cargado y reemplazarlas con una imagen nuestra, sería genial.

Sospecho que el primer objetivo simplemente no es posible, pero el segundo puede ser suficiente.

En el peor de los casos, analizo las fuentes RSS cuando las importamos, tomo las imágenes y las guardo localmente para que los usuarios puedan acceder a ellas de esa manera, pero parece una gran molestia por una ganancia razonablemente pequeña.

El Yobo
fuente

Respuestas:

147

Tu peor escenario no es tan malo como crees.

Ya está analizando la fuente RSS, por lo que ya tiene las URL de la imagen. Digamos que tiene una URL de imagen como http://otherdomain.com/someimage.jpg. Vuelve a escribir esta URL como https://mydomain.com/imageserver?url=http://otherdomain.com/someimage.jpg&hash=abcdeafad. De esta manera, el navegador siempre realiza una solicitud a través de https, por lo que se deshace de los problemas.

La siguiente parte: cree una página proxy o un servlet que haga lo siguiente:

  1. Lea el parámetro de URL de la cadena de consulta y verifique el hash
  2. Descargue la imagen del servidor y vuelva a enviarla por proxy al navegador
  3. Opcionalmente, caché la imagen en el disco

Esta solución tiene algunas ventajas. No es necesario descargar la imagen al momento de crear el html. No es necesario que almacene las imágenes localmente. Además, eres apátrida; la url contiene toda la información necesaria para servir la imagen.

Finalmente, el parámetro hash es por seguridad; solo desea que su servlet sirva imágenes para las URL que ha construido. Entonces, cuando crees la URL, calcúlala md5(image_url + secret_key)y añádela como el parámetro hash. Antes de entregar la solicitud, vuelva a calcular el hash y compárelo con lo que se le pasó. Dado que solo usted conoce la clave_secreta, nadie más puede construir URL válidas.

Si está desarrollando en Java, el Servlet son solo unas pocas líneas de código. Debería poder transferir el siguiente código a cualquier otra tecnología de back-end.

/*
targetURL is the url you get from RSS feeds
request and response are wrt to the browser
Assumes you have commons-io in your classpath
*/

protected void proxyResponse (String targetURL, HttpServletRequest request,
 HttpServletResponse response) throws IOException {
    GetMethod get = new GetMethod(targetURL);
    get.setFollowRedirects(true);    
    /*
     * Proxy the request headers from the browser to the target server
     */
    Enumeration headers = request.getHeaderNames();
    while(headers!=null && headers.hasMoreElements())
    {
        String headerName = (String)headers.nextElement();

        String headerValue = request.getHeader(headerName);

        if(headerValue != null)
        {
            get.addRequestHeader(headerName, headerValue);
        }            
    }        

    /*Make a request to the target server*/
    m_httpClient.executeMethod(get);
    /*
     * Set the status code
     */
    response.setStatus(get.getStatusCode());

    /*
     * proxy the response headers to the browser
     */
    Header responseHeaders[] = get.getResponseHeaders();
    for(int i=0; i<responseHeaders.length; i++)
    {
        String headerName = responseHeaders[i].getName();
        String headerValue = responseHeaders[i].getValue();

        if(headerValue != null)
        {
            response.addHeader(headerName, headerValue);
        }
    }

    /*
     * Proxy the response body to the browser
     */
    InputStream in = get.getResponseBodyAsStream();
    OutputStream out = response.getOutputStream();

    /*
     * If the server sends a 204 not-modified response, the InputStream will be null.
     */
    if (in !=null) {
        IOUtils.copy(in, out);
    }    
}
Sripathi Krishnan
fuente
1
Muy sano, y creo que esto es con lo que voy a rodar. Estamos usando PHP, pero la implementación también será trivial. También implementaré el almacenamiento en caché de nuestro lado, ya que no quiero descargar la imagen cada vez que alguien la solicite (para rendimiento y uso de ancho de banda). Las sugerencias para el enfoque de seguridad son sólidas (aunque también aplicaremos nuestro modelo de seguridad estándar, así como el anterior). Gracias por tu sugerencia.
El Yobo
32
El único inconveniente serio de este enfoque es que está enrutando todos los recursos externos a través de sus propios sistemas. Lo cual no solo es una desventaja, sino que también puede resultar bastante costoso.
Tim Molendijk
Segundo @TimMolendijk, agregando que no solo agrega costos y mantenimiento, sino que también derrota a cualquier CDN que supuestamente se enrute a servidores cercanos o equilibre a los inactivos.
Levente Pánczél
2
¿Cuál es la solución para NodeJS?
stkvtflw
1
otro +1 para @TimMolendijk, pero ¿cuál sería la solución entonces? El sitio servido a través de HTTPS no parece funcionar bien con las imágenes entregadas a través de HTTP
FullStackForger
15

Si está buscando una solución rápida para cargar imágenes a través de HTTPS, el servicio de proxy inverso gratuito en https://images.weserv.nl/ puede interesarle. Era exactamente lo que estaba buscando.

Si está buscando una solución paga, he usado anteriormente Cloudinary.com, que también funciona bien, pero en mi opinión, es demasiado costoso únicamente para esta tarea.

anulable
fuente
¿Cuál es el truco? Funciona muy bien
Jack
5
@JackNicholson Lo he estado usando bajo una carga relativamente pesada durante 2 años. ¡Funciona genial! Felicitaciones a los dos desarrolladores.
anulable
Tengo algunos enlaces (video o sitio) que comienzan con Http y no puedo mostrarlos en un iframe en nuestro sitio https. Como este enlace no es seguro, no funciona. para una imagen, resolví el problema usando la caché de imágenes. Alguien tiene alguna idea
int14
@ int14 Deberá configurar un proxy inverso para el sitio http, puede hacerlo con algo como AWS API Gateway.
anulable
3

No sé si esto encajaría con lo que estás haciendo, pero como solución rápida "envolvería" el contenido http en un script https. Por ejemplo, en su página que se sirve a través de https, introduciría un iframe que reemplazaría su feed rss y en el atributo src del iframe pondría una URL de un script en su servidor que captura el feed y genera el html. el script lee el feed a través de http y lo envía a través de https (por lo tanto, "envuelve")

Solo un pensamiento

hndcrftd
fuente
Me parece que esto me dejaría en la misma situación en la que estoy ahora; Ya estoy mostrando el contenido en una página HTTPS; el problema es que hay etiquetas <img> en el contenido con valores http: // src, que no se muestran y provocan la aparición de un mensaje molesto.
El Yobo
bueno, sí, si conserva los enlaces originales a las imágenes, no hay forma de evitar el problema. La secuencia de comandos de envoltura tendría que escanear el contenido del feed rss en busca de imágenes y eliminarlas. Como mencionó en otro comentario, no desea cargar el contenido que causa la ventana emergente y mostrar algo informativo en su lugar. Esa es la razón del "guión en el medio"
hndcrftd
Incluso puede hacer esto sin el iframe, directamente en su secuencia de comandos de backend principal, pero en este caso está esperando que el feed rss regrese antes de ser procesado y enviado a una página. Haría un iFrame para que su página se cargue de forma asincrónica con el feed rss. También hay una opción ajax si desea ir allí para evitar el iframe. Solo por curiosidad, ¿cuál es su plataforma de backend?
hndcrftd
2

Con respecto a su segundo requisito, es posible que pueda utilizar el evento onerror, es decir. <img onerror="some javascript;"...

Actualizar:

También puede intentar iterar document.imagesen el dom. Hay una completepropiedad booleana que podría utilizar. No estoy seguro de si esto será adecuado, pero valdría la pena investigarlo.

Hasta el arroyo
fuente
Interesante, yo ni siquiera sabía que no era un evento onerror. Tendría que reescribir el HTML (ya que proviene de una fuente externa), pero ya se está desinfectando con un purificador de HTML, por lo que agregarlo como filtro puede ser posible.
El Yobo
¿No se producirá ninguna advertencia de seguridad del navegador antes de que JavaScript haya tenido la oportunidad de hacer algo?
MrWhite
0

Sería mejor tener el contenido http en https

Daniel
fuente
5
Si no dejé esto claro en mi pregunta, el contenido HTTP está en el servidor de otras personas, no en el mío. Específicamente, son los enlaces <img> en HTML los que he recuperado de las fuentes RSS. He enfatizado esto en la pregunta ahora.
El Yobo
Oh, está bien, ¿ webproworld.com/webmaster-forum/threads/… ayudaría en algo?
Daniel
0

A veces, como en las aplicaciones de Facebook, no podemos tener contenidos no seguros en una página segura. tampoco podemos hacer local esos contenidos. por ejemplo, una aplicación que se cargará en iFrame no es un contenido simple y no podemos hacerlo local.

Creo que nunca deberíamos cargar contenido http en https, y tampoco deberíamos retroceder la página https a la versión http para evitar el diálogo de error.

la única forma de garantizar la seguridad del usuario es utilizar la versión https de todos los contenidos, http://developers.facebook.com/blog/post/499/

Mohammad Ali Akbari
fuente
3
Eso puede ser posible con Facebook, pero no para todo el contenido y esta pregunta no es sobre Facebook.
El Yobo
0

La respuesta aceptada me ayudó a actualizar esto tanto en PHP como en CORS, así que pensé en incluir la solución para otros:

PHP / HTML puro:

<?php // (the originating page, where you want to show the image)
// set your image location in whatever manner you need
$imageLocation = "http://example.com/exampleImage.png";

// set the location of your 'imageserve' program
$imageserveLocation = "https://example.com/imageserve.php";

// we'll look at the imageLocation and if it is already https, don't do anything, but if it is http, then run it through imageserve.php
$imageURL = (strstr("https://",$imageLocation)?"": $imageserveLocation . "?image=") . $imageLocation;

?>
<!-- this is the HTML image -->
<img src="<?php echo $imageURL ?>" />

javascript / jQuery:

<img id="theImage" src="" />
<script>
    var imageLocation = "http://example.com/exampleImage.png";
    var imageserveLocation = "https://example.com/imageserve.php";
    var imageURL = ((imageLocation.indexOf("https://") !== -1) ? "" : imageserveLocation + "?image=") + imageLocation;
    // I'm using jQuery, but you can use just javascript...        
    $("#theImage").prop('src',imageURL);
</script>

imageserve.php, consulte http://stackoverflow.com/questions/8719276/cors-with-php-headers?noredirect=1&lq=1 para obtener más información sobre CORS

<?php
// set your secure site URL here (where you are showing the images)
$mySecureSite = "https://example.com";

// here, you can set what kinds of images you will accept
$supported_images = array('png','jpeg','jpg','gif','ico');

// this is an ultra-minimal CORS - sending trusted data to yourself 
header("Access-Control-Allow-Origin: $mySecureSite");

$parts = pathinfo($_GET['image']);
$extension = $parts['extension'];
if(in_array($extension,$supported_images)) {
    header("Content-Type: image/$extension");
    $image = file_get_contents($_GET['image']);
    echo $image;
}
Soporte CFP
fuente
-2

Simplemente: NO LO HAGAS. El contenido HTTP dentro de una página HTTPS es intrínsecamente inseguro. Punto. Por eso IE muestra una advertencia. Deshacerse de la advertencia es un enfoque estúpido.

En cambio, una página HTTPS solo debe tener contenido HTTPS. Asegúrese de que el contenido también se pueda cargar a través de HTTPS y haga referencia a él a través de https si la página se carga a través de https. Para el contenido externo, esto significará cargar y almacenar en caché los elementos localmente para que estén disponibles a través de https, seguro. No hay forma de evitar eso, lamentablemente.

La advertencia está ahí por una buena razón. Seriamente. Dedique 5 minutos a pensar cómo podría hacerse cargo de una página que se muestra en https con contenido personalizado; se sorprenderá.

TomTom
fuente
3
Tranquilo, soy consciente de que hay una buena razón para ello; Creo que el comportamiento de IE es mejor que el de FF en este sentido. Lo que pretendo es no cargar el contenido; Solo quiero evitar la advertencia de estilo emergente intrusivo y mostrar algo informativo en lugar del contenido.
El Yobo
2
No hay posibilidad para eso, a menos que reescriba el HTML al salir. Cualquier intento de carga posterior a JavaScript ya ha mostrado el diálogo.
TomTom
Solo estaba preguntando sobre imágenes y no está solicitando ningún texto o guión inseguro, por lo que podemos pasar la advertencia reescribiendo las URL.
Jayapal Chandran
1
Sin cambios en la respuesta. Las imágenes también pueden ser inseguras. Es una cosa general, o proviene de la fuente segura o puede ser reemplazada por un hombre en el medio del ataque.
TomTom
8
Se votó en contra porque esta "respuesta" no respondió cómo lograr el objetivo del OP.
MikeSchinkel
-3

Me doy cuenta de que este es un hilo antiguo, pero una opción es simplemente eliminar la parte http: de la URL de la imagen para que ' http: //alguno/imagen.jpg ' se convierta en '//alguno/imagen.jpg'. Esto también funcionará con CDN

Shmarkus
fuente
7
Esto a veces funcionará y otras no; depende de si el contenido ascendente está disponible a través de HTTPS. Si no, simplemente se romperá.
El Yobo
-3

La mejor manera de trabajar para mi

<img src="/path/image.png" />// this work only online
    or
    <img src="../../path/image.png" /> // this work both
    or asign variable
    <?php 
    $base_url = '';
    if($_SERVER['HTTP_HOST'] == 'localhost')
    {
         $base_url = 'localpath'; 
    }
    ?>
    <img src="<?php echo $base_url;?>/path/image.png" /> 
sandeep kumar
fuente