Gestire il contenuto HTTP nelle pagine HTTPS


89

Abbiamo un sito a cui si accede interamente tramite HTTPS, ma a volte viene visualizzato contenuto esterno che è HTTP (principalmente immagini da feed RSS). Anche la stragrande maggioranza dei nostri utenti è bloccata su IE6.

Idealmente vorrei fare entrambe le seguenti cose

  • Impedisci il messaggio di avviso di IE sui contenuti non sicuri (in modo che io possa mostrarne uno meno invadente, ad esempio sostituendo le immagini con un'icona predefinita come di seguito)
  • Presentare qualcosa di utile agli utenti al posto delle immagini che altrimenti non potrebbero vedere; se ci fosse un po 'di JS potrei correre per capire quali immagini non sono state caricate e sostituirle con una nostra immagine invece sarebbe fantastico.

Sospetto che il primo obiettivo non sia semplicemente possibile, ma il secondo potrebbe essere sufficiente.

Uno scenario peggiore è che analizzo i feed RSS quando li importiamo, prendo le immagini e le memorizzo localmente in modo che gli utenti possano accedervi in ​​quel modo, ma sembra un sacco di dolore per un guadagno ragionevolmente piccolo.

Risposte:


147

Il tuo scenario peggiore non è così grave come pensi.

Stai già analizzando il feed RSS, quindi hai già gli URL delle immagini. Supponi di avere un URL immagine come http://otherdomain.com/someimage.jpg. Riscrivi questo URL come https://mydomain.com/imageserver?url=http://otherdomain.com/someimage.jpg&hash=abcdeafad. In questo modo, il browser effettua sempre richieste tramite https, in modo da eliminare i problemi.

La parte successiva: creare una pagina proxy o un servlet che esegue le seguenti operazioni:

  1. Leggere il parametro url dalla stringa di query e verificare l'hash
  2. Scarica l'immagine dal server e inviala nuovamente al browser
  3. Facoltativamente, memorizzare nella cache l'immagine su disco

Questa soluzione presenta alcuni vantaggi. Non è necessario scaricare l'immagine al momento della creazione dell'html. Non è necessario memorizzare le immagini in locale. Inoltre, sei apolide; l'url contiene tutte le informazioni necessarie per servire l'immagine.

Infine, il parametro hash è per la sicurezza; vuoi solo che il tuo servlet serva immagini per gli URL che hai costruito. Quindi, quando crei l'URL, calcolalo md5(image_url + secret_key)e aggiungilo come parametro hash. Prima di servire la richiesta, ricalcola l'hash e confrontalo con ciò che ti è stato passato. Poiché secret_key è noto solo a te, nessun altro può costruire URL validi.

Se stai sviluppando in java, il Servlet è solo poche righe di codice. Dovresti essere in grado di trasferire il codice seguente su qualsiasi altra tecnologia di 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);
    }    
}

1
Molto solido e penso che questo sia ciò con cui girerò. Stiamo usando PHP, ma anche l'implementazione sarà banale. Implementerò anche la memorizzazione nella cache dalla nostra parte, poiché non voglio scaricare l'immagine ogni volta che qualcuno lo richiede (per prestazioni e utilizzo della larghezza di banda). I suggerimenti per l'approccio alla sicurezza sono validi (sebbene applicheremo anche il nostro modello di sicurezza standard e quanto sopra). Grazie per il tuo suggerimento.
El Yobo

32
L'unico grave svantaggio di questo approccio è che stai instradando tutte le risorse esterne attraverso i tuoi sistemi. Che non è solo una responsabilità, ma può anche diventare piuttosto costoso.
Tim Molendijk

Secondo @TimMolendijk, aggiungendo che non solo aggiunge costi e manutenzione, ma sconfigge anche qualsiasi CDN che dovrebbe essere indirizzato a server vicini o bilanciato a quelli inattivi.
Levente Pánczél

2
Qual è la soluzione per NodeJS?
stkvtflw

1
un altro +1 per @TimMolendijk ma quale sarebbe la soluzione allora? il sito servito su HTTPS non sembra funzionare bene con le immagini fornite tramite HTTP
FullStackForger

15

Se stai cercando una soluzione rapida per caricare le immagini su HTTPS, il servizio di proxy inverso gratuito su https://images.weserv.nl/ potrebbe interessarti. Era esattamente quello che stavo cercando.

Se stai cercando una soluzione a pagamento, ho già utilizzato Cloudinary.com che funziona anche bene ma è troppo costoso solo per questo compito, secondo me.


Qual è il trucco? Funziona alla grande
Jack

5
@JackNicholson Lo uso sotto un carico relativamente pesante da 2 anni. Funziona alla grande! Complimenti ai due sviluppatori.
nullable

Ho alcuni link (video o sito) che iniziano con Http e non riesco a visualizzarli in un Iframe sul nostro sito https. Poiché questo è un collegamento non sicuro, non funziona. per un'immagine, ho risolto il problema utilizzando la cache delle immagini. Qualcuno ha qualche idea
int14

@ int14 Dovrai configurare un proxy inverso per il sito http, puoi farlo con qualcosa come AWS API Gateway.
nullable

3

Non so se questo si adatterebbe a quello che stai facendo, ma come soluzione rapida vorrei "avvolgere" il contenuto http in uno script https. Ad esempio, sulla tua pagina che viene servita tramite https introdurrei un iframe che sostituirebbe il tuo feed rss e nell'attr src dell'iframe mettere un URL di uno script sul tuo server che cattura il feed e genera l'html. lo script sta leggendo il feed tramite http e lo restituisce tramite https (quindi "wrapping")

Solo un pensiero


Mi sembra che questo mi lascerebbe nella stessa situazione in cui mi trovo ora; Sto già mostrando il contenuto in una pagina HTTPS - il problema è che ci sono tag <img> nel contenuto con valori http: // src - che non vengono mostrati e causano un messaggio fastidioso.
El Yobo

beh, sì, se mantieni i link originali alle immagini, non c'è modo di evitare il problema. Lo script wrapper dovrebbe scansionare il contenuto del feed RSS per le immagini e rimuoverle. Come hai accennato in un altro commento, non vuoi caricare il contenuto che causa il popup e mostrare invece qualcosa di informativo. Questo è il motivo per lo "script in the middle"
hndcrftd

Puoi anche farlo senza l'iframe, direttamente nel tuo script di backend principale, ma in questo caso stai aspettando che il feed RSS ritorni prima di essere elaborato e stampato su una pagina. Vorrei fare un iFrame in modo che la tua pagina venga caricata in modo asincrono con il feed RSS. C'è anche un'opzione ajax se vuoi andare lì per evitare l'iframe. Sono solo curioso: qual è la tua piattaforma di backend?
hndcrftd

2

Per quanto riguarda il tuo secondo requisito, potresti essere in grado di utilizzare l'evento onerror, ad es. <img onerror="some javascript;"...

Aggiornare:

Puoi anche provare a scorrere document.imagesnel dom. C'è una completeproprietà booleana che potresti essere in grado di utilizzare. Non so per certo se questo sarà adatto, ma potrebbe valere la pena indagare.


Interessante, non sapevo nemmeno che ci fosse un evento di errore. Dovrei riscrivere l'HTML (poiché proviene da una fonte esterna), ma è già stato disinfettato con il purificatore HTML, quindi aggiungerlo come filtro potrebbe essere possibile.
El Yobo

Non verrà visualizzato alcun avviso di sicurezza del browser prima che JavaScript abbia avuto la possibilità di fare qualcosa?
MrWhite

0

Sarebbe meglio avere solo il contenuto http su https


5
Se non l'ho chiarito nella mia domanda, il contenuto HTTP è sul server di altre persone non è mio. Nello specifico, sono i collegamenti <img> in HTML che ho recuperato dai feed RSS. L'ho sottolineato ora nella domanda.
El Yobo


0

A volte, come nelle app di Facebook, non possiamo avere contenuti non protetti nella pagina protetta. inoltre non possiamo rendere locali quei contenuti. ad esempio un'app che verrà caricata in iFrame non è un semplice contenuto e non possiamo renderla locale.

Penso che non dovremmo mai caricare i contenuti http in https, inoltre non dovremmo eseguire il fallback della pagina https nella versione http per evitare la finestra di dialogo di errore.

l'unico modo per garantire la sicurezza dell'utente è utilizzare la versione https di tutti i contenuti, http://developers.facebook.com/blog/post/499/


3
Ciò potrebbe essere possibile con Facebook, ma non per tutti i contenuti e questa domanda non riguardava Facebook.
El Yobo

0

La risposta accettata mi ha aiutato ad aggiornare questo sia a PHP che a CORS, quindi ho pensato di includere la soluzione per altri:

puro PHP / HTML:

<?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 vedi http://stackoverflow.com/questions/8719276/cors-with-php-headers?noredirect=1&lq=1 per maggiori informazioni su 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;
}

-2

Semplicemente: NON FARLO. Il contenuto HTTP all'interno di una pagina HTTPS è intrinsecamente insicuro. Punto. Questo è il motivo per cui IE mostra un avviso. Liberarsi dell'avvertimento è uno stupido approccio stupido.

Invece, una pagina HTTPS dovrebbe avere solo contenuto HTTPS. Assicurati che il contenuto possa essere caricato anche tramite HTTPS e fai riferimento ad esso tramite https se la pagina viene caricata tramite https. Per i contenuti esterni ciò significherà caricare e memorizzare nella cache gli elementi localmente in modo che siano disponibili tramite https - certo. In nessun modo, purtroppo.

L'avvertimento c'è per una buona ragione. Sul serio. Dedica 5 minuti a pensare a come acquisire il controllo di una pagina mostrata in https con contenuti personalizzati: rimarrai sorpreso.


3
Facile lì, sono consapevole che c'è una buona ragione per questo; Penso che il comportamento di IE sia migliore di FF in questo senso. Quello a cui sto mirando non è caricare il contenuto; Voglio solo evitare l'avviso invadente in stile popup e mostrare qualcosa di informativo al posto del contenuto.
El Yobo

2
Nessuna possibilità per questo, a meno che non riscrivi l'HTML prima di uscire. Qualsiasi tentativo di caricamento del post javascript ha già mostrato la finestra di dialogo.
TomTom

Stava solo chiedendo informazioni sulle immagini e non richiede alcun testo o script non sicuri, quindi possiamo ignorare l'avvertimento riscrivendo gli URL.
Jayapal Chandran

1
Nessuna modifica alla risposta. Le immagini possono anche essere insicure. È una cosa generale: o proviene dalla fonte protetta o può essere sostituita da un uomo nel mezzo dell'attacco.
TomTom

8
Downvoted perché questa "risposta" non rispondeva a come raggiungere l'obiettivo dell'OP.
MikeSchinkel

-3

Mi rendo conto che questo è un vecchio thread ma un'opzione è solo quella di rimuovere la parte http: dall'URL dell'immagine in modo che ' http: //some/image.jpg ' diventi '//some/image.jpg'. Funzionerà anche con i CDN


7
Questo a volte funzionerà ea volte no; dipende se il contenuto a monte è disponibile tramite HTTPS. In caso contrario, si romperà.
El Yobo

-3

Il modo migliore di lavorare per me

<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" /> 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.