Obsługa treści HTTP na stronach HTTPS

90

Mamy witrynę, do której można uzyskać dostęp w całości przez HTTPS, ale czasami wyświetlamy zawartość zewnętrzną, którą jest HTTP (głównie obrazy z kanałów RSS). Zdecydowana większość naszych użytkowników utknęła również w IE6.

Idealnie chciałbym wykonać obie poniższe czynności

  • Zapobiegaj wyświetlaniu ostrzeżenia IE o niezabezpieczonej zawartości (abym mógł pokazać mniej inwazyjną, np. Zastępując obrazy domyślną ikoną jak poniżej)
  • Przedstaw użytkownikom coś pożytecznego zamiast obrazów, których inaczej nie mogliby zobaczyć; gdyby był jakiś JS, który mógłbym uruchomić, aby dowiedzieć się, które obrazy nie zostały załadowane i zastąpić je naszym obrazem, byłoby świetnie.

Podejrzewam, że pierwszy cel jest po prostu niemożliwy, ale drugi może wystarczyć.

Najgorszym scenariuszem jest to, że analizuję kanały RSS, kiedy je importujemy, pobieram obrazy i przechowuję je lokalnie, aby użytkownicy mieli do nich dostęp w ten sposób, ale wydaje się, że jest to dużo bólu przy stosunkowo niewielkim zysku.

El Yobo
źródło

Odpowiedzi:

148

Twój najgorszy scenariusz nie jest taki zły, jak myślisz.

Już analizujesz kanał RSS, więc masz już adresy URL obrazów. Załóżmy, że masz adres URL obrazu, taki jak http://otherdomain.com/someimage.jpg. Przepisujesz ten adres URL jako https://mydomain.com/imageserver?url=http://otherdomain.com/someimage.jpg&hash=abcdeafad. W ten sposób przeglądarka zawsze wysyła żądania przez https, więc pozbywasz się problemów.

Następna część - utwórz stronę proxy lub serwlet, który wykonuje następujące czynności -

  1. Przeczytaj parametr url z ciągu zapytania i zweryfikuj skrót
  2. Pobierz obraz z serwera i proxy go z powrotem do przeglądarki
  3. Opcjonalnie buforuj obraz na dysku

To rozwiązanie ma kilka zalet. Nie musisz pobierać obrazu w momencie tworzenia HTML. Nie musisz przechowywać obrazów lokalnie. Ponadto jesteś bezpaństwowcem; adres URL zawiera wszystkie informacje niezbędne do wyświetlenia obrazu.

Wreszcie parametr hash służy do celów bezpieczeństwa; chcesz, aby serwlet obsługiwał obrazy tylko dla utworzonych adresów URL. Tak więc podczas tworzenia adresu URL oblicz go md5(image_url + secret_key)i dołącz jako parametr hash. Zanim obsłużysz żądanie, ponownie oblicz skrót i porównaj go z tym, co zostało Ci przekazane. Ponieważ tajny_klucz jest znany tylko Tobie, nikt inny nie może tworzyć prawidłowych adresów URL.

Jeśli tworzysz w Javie, serwlet to tylko kilka linijek kodu. Powinieneś być w stanie przenieść poniższy kod do dowolnej innej technologii zaplecza.

/*
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
źródło
1
Bardzo solidne i myślę, że właśnie z tym będę się toczyć. Używamy PHP, ale implementacja również będzie banalna. Zaimplementuję również buforowanie po naszej stronie, ponieważ nie chcę pobierać obrazu za każdym razem, gdy ktoś o to poprosi (ze względu na wydajność i wykorzystanie pasma). Sugestie dotyczące podejścia do bezpieczeństwa są rozsądne (chociaż zastosujemy również nasz standardowy model bezpieczeństwa, jak również powyższy). Dzięki za Twoją sugestię.
El Yobo
33
Jedyną poważną wadą tego podejścia jest to, że wszystkie zasoby zewnętrzne są kierowane przez własne systemy. Co jest nie tylko zobowiązaniem, ale może też być dość kosztowne.
Tim Molendijk,
Po drugie, @TimMolendijk, dodając, że nie tylko zwiększa koszty i konserwację, ale także pokonuje wszelkie CDN, które mają kierować do bliskich serwerów lub balansować na nieaktywne.
Levente Pánczél
2
Jakie jest rozwiązanie dla NodeJS?
stkvtflw
1
kolejne +1 dla @TimMolendijk, ale jakie byłoby wtedy rozwiązanie? witryna obsługiwana przez HTTPS nie wygląda dobrze z obrazami dostarczonymi przez HTTP
FullStackForger
16

Jeśli szukasz szybkiego rozwiązania do ładowania obrazów przez HTTPS, może Cię zainteresować bezpłatna usługa odwrotnego proxy pod adresem https://images.weserv.nl/ . To było dokładnie to, czego szukałem.

Jeśli szukasz płatnego rozwiązania, korzystałem wcześniej z Cloudinary.com, który również działa dobrze, ale moim zdaniem jest zbyt drogi tylko do tego zadania.

nullable
źródło
Jaki jest haczyk? Działa świetnie
Jack
5
@JackNicholson Używam go pod stosunkowo dużym obciążeniem od 2 lat. Działa świetnie! Uznanie dla dwóch twórców.
unieważnienie
Mam kilka linków (wideo lub witryny) zaczynające się od Http i nie mogę ich wyświetlić w ramce iframe w naszej witrynie https. Ponieważ jest to niezabezpieczone łącze, nie działa. w przypadku obrazu rozwiązałem problem za pomocą pamięci podręcznej obrazu. Każdy ma jakiś pomysł
int14
@ int14 Będziesz musiał skonfigurować odwrotne proxy dla witryny http, możesz to zrobić za pomocą czegoś takiego jak AWS API Gateway.
nieważny
3

Nie wiem, czy pasowałoby to do tego, co robisz, ale w ramach szybkiego rozwiązania „opakowałbym” zawartość http w skrypt https. Na przykład na twojej stronie, która jest obsługiwana przez https, wprowadziłbym iframe, który zastąpiłby twój kanał rss, a w atr src elementu iframe umieściłbym na serwerze adres URL skryptu, który przechwytuje kanał i wyprowadza HTML. skrypt czyta kanał przez http i wyprowadza go przez https (czyli „zawija”)

Tylko myśl

hndcrftd
źródło
Wydaje mi się, że to postawi mnie w takiej samej sytuacji, w jakiej jestem teraz; Już pokazuję zawartość na stronie HTTPS - problem polega na tym, że w treści znajdują się tagi <img> z wartościami http: // src - które nie są wyświetlane i powodują irytujący komunikat.
El Yobo
cóż, tak, jeśli zachowasz oryginalne linki do obrazów, nie ma sposobu, aby uniknąć problemu. Skrypt opakowujący musiałby przeskanować zawartość źródła rss w poszukiwaniu obrazów i je usunąć. Jak wspomniałeś w innym komentarzu - nie chcesz ładować treści, która powoduje wyskakujące okienko, a zamiast tego wyświetlać coś informacyjnego. To jest powód dla "scenariusza w środku"
hndcrftd
Możesz to zrobić nawet bez ramki iframe, bezpośrednio w swoim głównym skrypcie zaplecza, ale w tym przypadku czekasz na powrót kanału rss, zanim zostanie przetworzony i wyprowadzony na stronę. Zrobiłbym iFrame, aby Twoja strona ładowała się asynchronicznie z kanałem RSS. Jest również opcja Ajax, jeśli chcesz tam przejść, aby uniknąć iframe. Po prostu ciekawy - jaka jest Twoja platforma zaplecza?
hndcrftd
2

Jeśli chodzi o twoje drugie wymaganie - możesz być w stanie wykorzystać zdarzenie onerror, tj. <img onerror="some javascript;"...

Aktualizacja:

Możesz także spróbować wykonać iterację document.imagesw dom. Istnieje completewłaściwość logiczna, której możesz użyć. Nie wiem na pewno, czy to będzie odpowiednie, ale warto zbadać.

UpTheCreek
źródło
Co ciekawe, nawet nie wiedziałem, że było zdarzenie powodujące błąd. Musiałbym przepisać kod HTML (ponieważ pochodzi z zewnętrznego źródła), ale jest już oczyszczany za pomocą oczyszczacza HTML, więc dodanie tego jako filtra może być możliwe.
El Yobo,
Czy żadne ostrzeżenie dotyczące bezpieczeństwa przeglądarki nie pojawi się, zanim JavaScript zdąży cokolwiek zrobić?
MrWhite
0

Najlepiej byłoby po prostu mieć zawartość http na https

Daniel
źródło
5
Jeśli nie wyjaśniłem tego jasno w moim pytaniu, zawartość HTTP znajduje się na serwerze innych osób, nie jest mój. W szczególności są to łącza <img> w HTML, które pobrałem z kanałów RSS. Podkreśliłem to teraz w pytaniu.
El Yobo
No dobra , czy webproworld.com/webmaster-forum/threads/ ... w ogóle pomogłoby?
Daniel,
0

Czasami, podobnie jak w aplikacjach na Facebooku, nie możemy mieć niezabezpieczonych treści na bezpiecznej stronie. nie możemy też tworzyć lokalnych treści. na przykład aplikacja, która będzie ładowana w iFrame, nie jest prostą treścią i nie możemy uczynić jej lokalną.

Myślę, że nigdy nie powinniśmy ładować zawartości http w https, a także nie powinniśmy zmieniać strony https na wersję http, aby zapobiec wyświetlaniu okna błędu.

jedynym sposobem, który zapewni bezpieczeństwo użytkownika, jest korzystanie z wszystkich treści w wersji https, http://developers.facebook.com/blog/post/499/

Mohammad Ali Akbari
źródło
3
Może to być możliwe z Facebookiem, ale nie dla wszystkich treści, a to pytanie nie dotyczyło Facebooka.
El Yobo
0

Zaakceptowana odpowiedź pomogła mi zaktualizować to zarówno do PHP, jak i CORS, więc pomyślałem, że dołączę rozwiązanie dla innych:

czysty 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 Zobacz http://stackoverflow.com/questions/8719276/cors-with-php-headers?noredirect=1&lq=1, aby uzyskać więcej informacji na temat 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;
}
Wsparcie CFP
źródło
-2

Po prostu: NIE Rób tego. Treść HTTP na stronie HTTPS jest z natury niepewna. Punkt. Dlatego IE wyświetla ostrzeżenie. Pozbycie się ostrzeżenia to głupie podejście.

Zamiast tego strona HTTPS powinna zawierać tylko zawartość HTTPS. Upewnij się, że treść może być ładowana również przez HTTPS i odwołaj się do niej przez https, jeśli strona jest ładowana przez https. W przypadku treści zewnętrznych będzie to oznaczać ładowanie i buforowanie elementów lokalnie, tak aby były dostępne przez https - jasne. Niestety nie da się tego obejść.

Ostrzeżenie jest tam z dobrego powodu. Poważnie. Poświęć 5 minut na zastanowienie się, jak przejąć wyświetlaną stronę https z niestandardową zawartością - będziesz zaskoczony.

TomTom
źródło
3
Spokojnie, zdaję sobie sprawę, że jest ku temu dobry powód; Myślę, że zachowanie IE jest pod tym względem lepsze niż FF. Co mam nastawione na to nie można załadować zawartości; Chcę po prostu uniknąć natrętnego ostrzeżenia w stylu wyskakującego okienka i pokazać coś informacyjnego zamiast treści.
El Yobo
2
Nie ma na to szans - chyba, że ​​po drodze przepisujesz kod HTML. Każda próba załadowania po załadowaniu javascript już powoduje wyświetlenie okna dialogowego.
TomTom
Pytał tylko o obrazy i nie żąda żadnego niezabezpieczonego tekstu ani skryptu, abyśmy mogli ominąć ostrzeżenie, przepisując adresy URL.
Jayapal Chandran
1
Bez zmian w odpowiedzi. Obrazy mogą być również niepewne. To rzecz ogólna - albo pochodzi z zabezpieczonego źródła, albo może zostać zastąpiony przez atakującego w środku.
TomTom
8
Głos odrzucono, ponieważ ta „odpowiedź” nie odpowiadała, jak osiągnąć cel PO.
MikeSchinkel
-3

Zdaję sobie sprawę, że jest to stary wątek, ale jedną z opcji jest po prostu usunięcie http: części z adresu URL obrazu, aby „ http: //some/image.jpg ” stało się „//some/image.jpg”. Będzie to również działać z sieciami CDN

Shmarkus
źródło
7
To czasami zadziała, a czasami nie; zależy to od tego, czy zawartość wyjściowa jest dostępna przez HTTPS. Jeśli nie, po prostu się zepsuje.
El Yobo
-3

Najlepszy sposób na pracę dla mnie

<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
źródło