Umgang mit HTTP-Inhalten auf HTTPS-Seiten

89

Wir haben eine Site, auf die vollständig über HTTPS zugegriffen wird, aber manchmal werden externe Inhalte angezeigt , bei denen es sich um HTTP handelt (hauptsächlich Bilder aus RSS-Feeds). Die überwiegende Mehrheit unserer Benutzer steckt auch im IE6 fest.

Idealerweise möchte ich beides tun

  • Verhindern Sie die IE-Warnmeldung über unsicheren Inhalt (damit ich einen weniger aufdringlichen anzeigen kann, z. B. indem Sie die Bilder durch ein Standardsymbol wie unten ersetzen).
  • Präsentieren Sie den Benutzern etwas Nützliches anstelle der Bilder, die sie sonst nicht sehen können. Wenn es JS gäbe, könnte ich herausfinden, welche Bilder nicht geladen wurden, und sie stattdessen durch ein Bild von uns ersetzen, das wäre großartig.

Ich vermute, dass das erste Ziel einfach nicht möglich ist, aber das zweite kann ausreichen.

Ein Worst-Case-Szenario ist, dass ich die RSS-Feeds beim Importieren analysiere, die Bilder greife, sie lokal speichere, damit die Benutzer auf diese Weise darauf zugreifen können, aber es scheint viel Schmerz zu sein, wenn einigermaßen wenig Gewinn erzielt wird.

El Yobo
quelle

Antworten:

147

Ihr Worst-Case-Szenario ist nicht so schlimm, wie Sie denken.

Sie analysieren bereits den RSS-Feed, sodass Sie bereits über die Bild-URLs verfügen. Angenommen, Sie haben eine Bild-URL wie http://otherdomain.com/someimage.jpg. Sie schreiben diese URL um alshttps://mydomain.com/imageserver?url=http://otherdomain.com/someimage.jpg&hash=abcdeafad . Auf diese Weise stellt der Browser immer eine Anfrage über https, sodass Sie die Probleme beseitigen können.

Der nächste Teil - Erstellen Sie eine Proxyseite oder ein Servlet, das Folgendes ausführt:

  1. Lesen Sie den URL-Parameter aus der Abfragezeichenfolge und überprüfen Sie den Hash
  2. Laden Sie das Bild vom Server herunter und übertragen Sie es zurück in den Browser
  3. Optional können Sie das Image auf der Festplatte zwischenspeichern

Diese Lösung hat einige Vorteile. Sie müssen das Bild zum Zeitpunkt der Erstellung des HTML-Codes nicht herunterladen. Sie müssen die Bilder nicht lokal speichern. Außerdem bist du staatenlos; Die URL enthält alle Informationen, die zur Bereitstellung des Bildes erforderlich sind.

Schließlich dient der Hash-Parameter der Sicherheit. Sie möchten nur, dass Ihr Servlet Bilder für von Ihnen erstellte URLs bereitstellt. Wenn Sie also die URL erstellen, berechnen Siemd5(image_url + secret_key) und hängen Sie sie als Hash-Parameter an. Bevor Sie die Anfrage bearbeiten, berechnen Sie den Hash neu und vergleichen Sie ihn mit dem, was an Sie übergeben wurde. Da der secret_key nur Ihnen bekannt ist, kann niemand sonst gültige URLs erstellen.

Wenn Sie in Java entwickeln, besteht das Servlet nur aus wenigen Codezeilen. Sie sollten in der Lage sein, den folgenden Code auf jede andere Back-End-Technologie zu portieren.

/*
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
quelle
1
Sehr gut, und ich denke, das ist es, womit ich rollen werde. Wir verwenden PHP, aber die Implementierung wird auch trivial sein. Ich werde auch das Caching auf unserer Seite implementieren, da ich das Bild nicht jedes Mal herunterladen möchte, wenn jemand es anfordert (für Leistung und Bandbreitennutzung). Die Vorschläge für den Sicherheitsansatz sind fundiert (obwohl wir neben dem oben genannten auch unser Standard-Sicherheitsmodell anwenden werden). Danke für Ihren Vorschlag.
El Yobo
32
Der einzige schwerwiegende Nachteil dieses Ansatzes besteht darin, dass Sie alle externen Ressourcen über Ihre eigenen Systeme weiterleiten. Das ist nicht nur eine Haftung, sondern kann auch ziemlich kostspielig werden.
Tim Molendijk
Ich stimme @TimMolendijk zu und füge hinzu, dass es nicht nur Kosten und Wartung hinzufügt, sondern auch alle CDNs besiegt, die an nahe Server weiterleiten oder auf nicht genutzte Server ausgleichen sollen.
Levente Pánczél
2
Was ist die Lösung für NodeJS?
stkvtflw
1
noch +1 für @TimMolendijk, aber was wäre dann eine Lösung? Website, die über HTTPS
bereitgestellt wird,
15

Wenn Sie nach einer schnellen Lösung zum Laden von Bildern über HTTPS suchen, könnte Sie der kostenlose Reverse-Proxy-Dienst unter https://images.weserv.nl/ interessieren. Es war genau das, wonach ich gesucht habe.

Wenn Sie nach einer kostenpflichtigen Lösung suchen, habe ich zuvor Cloudinary.com verwendet, das ebenfalls gut funktioniert, aber meiner Meinung nach nur für diese Aufgabe zu teuer ist.

nullable
quelle
Was ist der Haken? Funktioniert super
Jack
5
@ JackNicholson Ich benutze es seit 2 Jahren unter relativ hoher Last. Funktioniert super! Ein großes Lob an die beiden Entwickler.
nullable
Ich habe einige Links (Video oder Site), die mit HTTP beginnen, und ich kann sie nicht in einem Iframe auf unserer https-Site anzeigen. Da dies eine nicht sichere Verbindung ist, funktioniert sie nicht. Für ein Bild habe ich das Problem mithilfe des Bildcaches gelöst. Jeder hat eine Idee
int14
@ int14 Sie müssen einen Reverse-Proxy für die http-Site einrichten. Sie können dies mit etwas wie AWS API Gateway tun.
nullable
3

Ich weiß nicht, ob dies zu Ihrer Arbeit passt, aber als schnelle Lösung würde ich den http-Inhalt in ein https-Skript einbinden. Zum Beispiel würde ich auf Ihrer Seite, die über https bereitgestellt wird, einen Iframe einführen, der Ihren RSS-Feed ersetzen würde, und im src-Attribut des Iframes eine URL eines Skripts auf Ihrem Server einfügen, das den Feed erfasst und den HTML-Code ausgibt. Das Skript liest den Feed über http und gibt ihn über https aus (also "Wrapping").

Nur ein Gedanke

hndcrftd
quelle
Es scheint mir, dass dies mich in die gleiche Situation bringen würde, in der ich mich jetzt befinde; Ich zeige den Inhalt bereits auf einer HTTPS-Seite an. Das Problem besteht darin, dass der Inhalt <img> -Tags mit http: // src-Werten enthält. Diese werden nicht angezeigt und verursachen eine nervige Meldung.
El Yobo
Ja, wenn Sie die ursprünglichen Links zu den Bildern beibehalten, können Sie das Problem nicht vermeiden. Das Wrapper-Skript müsste den RSS-Feed-Inhalt nach Bildern durchsuchen und diese entfernen. Wie Sie in einem anderen Kommentar erwähnt haben, möchten Sie den Inhalt, der das Popup verursacht, nicht laden und stattdessen etwas Informatives anzeigen. Das ist der Grund für das "Skript in der Mitte"
hndcrftd
Sie können dies sogar ohne den Iframe direkt in Ihrem Haupt-Backend-Skript tun. In diesem Fall warten Sie jedoch darauf, dass der RSS-Feed zurückkommt, bevor Sie ihn verarbeiten und auf einer Seite ausgeben. Ich würde einen iFrame erstellen, damit Ihre Seite asynchron mit dem RSS-Feed geladen wird. Es gibt auch eine Ajax-Option, wenn Sie dorthin gehen möchten, um den Iframe zu vermeiden. Nur neugierig - was ist Ihre Backend-Plattform?
hndcrftd
2

In Bezug auf Ihre zweite Anforderung können Sie möglicherweise das Fehlerereignis verwenden, d. H. <img onerror="some javascript;"...

Aktualisieren:

Sie können auch versuchen, document.imagesim Dom zu durchlaufen . Es gibt eine completeboolesche Eigenschaft, die Sie möglicherweise verwenden können. Ich weiß nicht genau, ob dies geeignet ist, aber es könnte sich lohnen, es zu untersuchen.

UpTheCreek
quelle
Interessanterweise wusste ich nicht einmal, dass es ein Fehlerereignis gab. Ich müsste den HTML-Code neu schreiben (da er von einer externen Quelle stammt), aber er wird bereits mit dem HTML-Reiniger bereinigt, sodass das Hinzufügen als HTML möglicherweise möglich ist.
El Yobo
Wird keine Browser-Sicherheitswarnung angezeigt, bevor JavaScript die Möglichkeit hatte, etwas zu tun?
MrWhite
0

Es ist am besten, nur den http-Inhalt auf https zu haben

Daniel
quelle
5
Wenn ich dies in meiner Frage nicht klargestellt habe, befindet sich der HTTP-Inhalt auf dem Server anderer Personen, nicht auf meinem. Insbesondere sind es <img> -Links in HTML, die ich aus RSS-Feeds abgerufen habe. Ich habe dies jetzt in der Frage betont.
El Yobo
0

Manchmal, wie in Facebook-Apps, können wir keine nicht sicheren Inhalte auf einer sicheren Seite haben. Auch können wir diese Inhalte nicht lokalisieren. Zum Beispiel ist eine App, die in iFrame geladen wird, kein einfacher Inhalt und wir können ihn nicht lokal machen.

Ich denke, wir sollten niemals http-Inhalte in https laden, außerdem sollten wir die https-Seite nicht auf die http-Version zurückgreifen, um einen Fehlerdialog zu vermeiden.

Die einzige Möglichkeit, die Sicherheit des Benutzers zu gewährleisten, besteht darin, die https-Version aller Inhalte zu verwenden: http://developers.facebook.com/blog/post/499/

Mohammad Ali Akbari
quelle
3
Das mag mit Facebook möglich sein, aber nicht für alle Inhalte und diese Frage betraf nicht Facebook.
El Yobo
0

Die akzeptierte Antwort half mir, dies sowohl auf PHP als auch auf CORS zu aktualisieren, sodass ich dachte, ich würde die Lösung für andere einschließen:

reines 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 Weitere Informationen zu CORS finden Sie unter http://stackoverflow.com/questions/8719276/cors-with-php-headers?noredirect=1&lq=1

<?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;
}
CFP-Unterstützung
quelle
-2

Einfach: TUN SIE ES NICHT. HTTP-Inhalt innerhalb einer HTTPS-Seite ist von Natur aus unsicher. Punkt. Aus diesem Grund zeigt der IE eine Warnung an. Die Warnung loszuwerden ist ein dummer Hogwash-Ansatz.

Stattdessen sollte nur eine HTTPS-Seite verwendet werden HTTPS-Inhalt enthalten. Stellen Sie sicher, dass der Inhalt auch über HTTPS geladen werden kann, und verweisen Sie auf https, wenn die Seite über https geladen wird. Für externe Inhalte bedeutet dies, dass die Elemente lokal geladen und zwischengespeichert werden, damit sie über https verfügbar sind. Daran führt leider kein Weg vorbei.

Die Warnung ist aus gutem Grund da. Ernsthaft. Überlegen Sie sich 5 Minuten lang, wie Sie eine https-angezeigte Seite mit benutzerdefinierten Inhalten übernehmen können - Sie werden überrascht sein.

TomTom
quelle
3
Einfach da, ich bin mir bewusst, dass es einen guten Grund dafür gibt; Ich denke, dass das Verhalten von IE in dieser Hinsicht besser ist als das von FF. Was ich anstrebe, ist nicht den Inhalt zu laden; Ich möchte nur die aufdringliche Warnung im Popup-Stil vermeiden und anstelle des Inhalts etwas Informatives anzeigen.
El Yobo
2
Keine Chance dafür - es sei denn, Sie schreiben den HTML-Code auf dem Weg nach draußen neu. Jeder Javascript-Versuch nach dem Laden hat den Dialog bereits angezeigt.
TomTom
Er hat nur nach Bildern gefragt und er fordert keinen unsicheren Text oder Skript an, damit wir die Warnung umgehen können, indem wir URLs neu schreiben.
Jayapal Chandran
1
Keine Änderung der Antwort. Bilder können auch unsicher sein. Es ist eine allgemeine Sache - entweder kommt es von der gesicherten Quelle oder es kann durch einen Mann im mittleren Angriff ersetzt werden.
TomTom
8
Abgestimmt, weil diese "Antwort" nicht antwortete, wie das Ziel des OP erreicht werden soll.
MikeSchinkel
-3

Mir ist klar, dass dies ein alter Thread ist, aber eine Option besteht darin, den http: -Teil aus der Bild-URL zu entfernen, sodass aus " http: //some/image.jpg " "//some/image.jpg" wird. Dies funktioniert auch mit CDNs

Shmarkus
quelle
7
Dies wird manchmal funktionieren und manchmal nicht; Dies hängt davon ab, ob der Upstream-Inhalt über HTTPS verfügbar ist. Wenn nicht, wird es einfach brechen.
El Yobo
-3

Beste Arbeitsweise für mich

<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
quelle