Работа с содержимым HTTP на страницах HTTPS


90

У нас есть сайт, доступ к которому осуществляется полностью через HTTPS, но иногда отображается внешний контент, который является HTTP (в основном изображения из RSS-каналов). Подавляющее большинство наших пользователей также застряли на IE6.

В идеале я бы хотел сделать оба из следующих

  • Предотвратить предупреждающее сообщение IE о небезопасном содержимом (чтобы я мог показать менее навязчивое, например, заменив изображения значком по умолчанию, как показано ниже)
  • Предложите пользователям что-нибудь полезное вместо изображений, которые они иначе не увидят; если бы был какой-то JS, я мог бы запустить, чтобы выяснить, какие изображения не были загружены, и заменить их нашим изображением, это было бы здорово.

Подозреваю, что первая цель просто невозможна, но второй может быть достаточно.

В худшем случае я анализирую RSS-каналы, когда мы их импортируем, захватываю изображения и сохраняю их локально, чтобы пользователи могли получить к ним доступ таким образом, но это кажется большой проблемой при разумно небольшом выигрыше.

Ответы:


149

Ваш худший сценарий не так плох, как вы думаете.

Вы уже анализируете RSS-канал, поэтому у вас уже есть URL-адреса изображений. Скажем, у вас есть URL-адрес изображения, например http://otherdomain.com/someimage.jpg. Вы переписываете этот URL как https://mydomain.com/imageserver?url=http://otherdomain.com/someimage.jpg&hash=abcdeafad. Таким образом, браузер всегда делает запрос через https, поэтому вы избавляетесь от проблем.

Следующая часть - создать прокси-страницу или сервлет, который выполняет следующие действия:

  1. Прочтите параметр url из строки запроса и проверьте хэш
  2. Загрузите изображение с сервера и проксируйте его обратно в браузер.
  3. При желании кешировать изображение на диске

Это решение имеет ряд преимуществ. Вам не нужно загружать изображение во время создания html. Вам не нужно хранить изображения локально. Кроме того, вы не имеете гражданства; URL-адрес содержит всю информацию, необходимую для показа изображения.

Наконец, хэш-параметр предназначен для безопасности; вы хотите, чтобы ваш сервлет обслуживал изображения только для созданных вами URL-адресов. Итак, когда вы создаете URL-адрес, вычислите md5(image_url + secret_key)и добавьте его в качестве хеш-параметра. Прежде чем обслуживать запрос, пересчитайте хеш и сравните его с тем, что вам было передано. Поскольку secret_key известен только вам, никто другой не может создавать действительные URL-адреса.

Если вы разрабатываете на java, сервлет - это всего лишь несколько строк кода. Вы должны иметь возможность перенести приведенный ниже код на любую другую внутреннюю технологию.

/*
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
Очень здорово, и я думаю, что это то, с чем я буду заниматься. Мы используем PHP, но реализация также будет тривиальной. Я также реализую кеширование на нашей стороне, так как я не хочу загружать изображение каждый раз, когда кто-то его запрашивает (для производительности и использования полосы пропускания). Предложения по подходу к обеспечению безопасности верны (хотя мы также применим нашу стандартную модель безопасности, а также описанную выше). Спасибо за ваше предложение.
El Yobo

33
Единственным серьезным недостатком этого подхода является то, что вы маршрутизируете все внешние ресурсы через свои собственные системы. Это не только проблема, но и может обойтись довольно дорого.
Тим Молендейк

Я поддерживаю @TimMolendijk, добавляя, что он не только увеличивает стоимость и обслуживание, но и уничтожает любые CDN, которые должны маршрутизироваться на ближайшие серверы или балансировать на незанятых.
Левенте Панцель

2
Какое решение для NodeJS?
stkvtflw

1
еще +1 за @TimMolendijk, но что тогда будет решением? сайт, обслуживаемый по HTTPS, не очень хорошо работает с изображениями, доставленными по HTTP
FullStackForger

16

Если вы ищете быстрое решение для загрузки изображений через HTTPS, тогда вам может быть интересна бесплатная служба обратного прокси на https://images.weserv.nl/ . Это было именно то, что я искал.

Если вы ищете платное решение, я ранее использовал Cloudinary.com, который также хорошо работает, но, на мой взгляд, слишком дорого обходится только для этой задачи.


В чем подвох?
Джек

5
@JackNicholson Я использую его при относительно большой нагрузке уже 2 года. Работает отлично! Престижность двум разработчикам.
обнуляемый

У меня есть некоторые ссылки (видео или сайт), начинающиеся с Http, и я не могу отобразить их в iframe на нашем сайте https. Поскольку это небезопасная ссылка, она не работает. для изображения я решил проблему с помощью кеша изображений. У кого-нибудь есть идеи
cs1985

@ int14 Вам нужно будет настроить обратный прокси для http-сайта, вы можете сделать это с помощью чего-то вроде AWS API Gateway.
обнуляемым

3

Я не знаю, подойдет ли это к тому, что вы делаете, но в качестве быстрого исправления я бы «обернул» содержимое http в сценарий https. Например, на вашей странице, обслуживаемой через https, я бы представил iframe, который заменил бы ваш RSS-канал, а в атрибуте src iframe поместил URL-адрес сценария на вашем сервере, который захватывает канал и выводит html. скрипт читает фид через http и выводит его через https (таким образом "оборачивая")

Просто мысль


Мне кажется, что это оставило бы меня в той же ситуации, в которой я нахожусь сейчас; Я уже показываю содержимое на странице HTTPS - проблема в том, что в содержимом есть теги <img> со значениями http: // src, которые не отображаются и вызывают появление раздражающего сообщения.
Эль Йобо,

ну да, если вы сохраните исходные ссылки на изображения, нет никакого способа избежать проблемы. Скрипт-оболочка должен будет сканировать содержимое RSS-канала на наличие изображений и удалять их. Как вы упомянули в другом комментарии - вы не хотите загружать контент, который вызывает всплывающее окно, и вместо этого показывать что-то информативное. Это причина «сценария посередине»
hndcrftd

Вы даже можете сделать это без iframe, прямо в вашем основном внутреннем скрипте, но в этом случае вы ждете, пока RSS-канал вернется, прежде чем он будет обработан и выведен на страницу. Я бы сделал iFrame, чтобы ваша страница загружалась асинхронно с RSS-потоком. Также есть опция ajax, если вы хотите пойти туда, чтобы избежать iframe. Просто любопытно - какая у вас серверная платформа?
hndcrftd

2

Что касается вашего второго требования - вы можете использовать событие onerror, т.е. <img onerror="some javascript;"...

Обновить:

Вы также можете попробовать выполнить итерацию document.imagesв dom. Существует completeлогическое свойство, которое вы можете использовать. Я не знаю наверняка, подойдет ли это, но, возможно, стоит изучить.


Интересно, я даже не знаю , что было OnError события. Мне пришлось бы переписать HTML (поскольку он поступает из внешнего источника), но он уже очищен с помощью очистителя HTML, поэтому добавление этого в качестве фильтра может быть возможным.
Эль Йобо,

Не появится ли какое-либо предупреждение системы безопасности браузера до того, как JavaScript сможет что-либо сделать?
MrWhite

1

Принятый ответ помог мне обновить это как для PHP, так и для CORS, поэтому я подумал, что включу решение для других:

чистый 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 см. http://stackoverflow.com/questions/8719276/cors-with-php-headers?noredirect=1&lq=1 для получения дополнительной информации о 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;
}

1

Иногда, как в приложениях facebook, мы не можем иметь незащищенное содержимое на защищенной странице. также мы не можем сделать это содержимое локальным. например, приложение, которое загружается в iFrame, не является простым контентом, и мы не можем сделать его локальным.

Я думаю, что мы никогда не должны загружать содержимое http в https, также мы не должны откатывать страницу https к версии http, чтобы предотвратить диалоговое окно с ошибкой.

единственный способ обеспечить безопасность пользователя - использовать https-версию всего содержимого, http://web.archive.org/web/20120502131549/http://developers.facebook.com/blog/post/499/


3
Это возможно с facebook, но не для всего контента, и этот вопрос не касался facebook.
Эль Йобо,

0

Лучше всего иметь http-контент на https


5
Если я не прояснил это в своем вопросе, HTTP-контент находится на чужом сервере, а не на моем. В частности, это ссылки <img> в HTML, которые я получил из RSS-каналов. Я сейчас подчеркнул это в вопросе.
Эль Йобо,


-1

Просто: НЕ ДЕЛАЙТЕ ЭТОГО. Содержимое Http на странице HTTPS по своей природе небезопасно. Точка. Вот почему IE показывает предупреждение. Избавиться от предупреждения - глупая фигня.

Вместо этого на странице HTTPS должно быть только содержимое HTTPS. Убедитесь, что контент также можно загружать через HTTPS, и ссылайтесь на него через https, если страница загружается через https. Для внешнего контента это будет означать загрузку и кеширование элементов локально, чтобы они были доступны через https - конечно. К сожалению, никак иначе.

Предупреждение есть по уважительной причине. Шутки в сторону. Потратьте 5 минут на размышления, как вы могли бы захватить страницу, показанную по https, с настраиваемым контентом - вы будете удивлены.


3
Полегче, я знаю, что для этого есть веская причина; Я считаю, что IE в этом плане лучше, чем FF. Я не стремлюсь загружать контент; Я просто хочу избежать назойливого предупреждения в стиле всплывающего окна и показать что-то информативное вместо содержимого.
Эль Йобо,

2
У этого нет шансов - если вы не переписываете HTML на выходе. Любая попытка пост-загрузки javascript уже показывала диалоговое окно.
TomTom

Он просто спрашивал об изображениях, и он не запрашивает небезопасный текст или скрипт, поэтому мы можем пропустить предупреждение, переписав URL-адреса.
Джаяпал Чандран 01

1
Никаких изменений в ответе. Изображения также могут быть небезопасными. Это обычное дело - либо оно исходит из защищенного источника, либо его может заменить человек в центре атаки.
TomTom

8
Проголосовали против, потому что этот «ответ» не отвечал, как достичь цели ОП.
MikeSchinkel

-3

Я понимаю, что это старый поток, но один из вариантов - просто удалить часть http: из URL-адреса изображения, чтобы http: //some/image.jpg превратилось в //some/image.jpg. Это также будет работать с CDN


7
Иногда это срабатывает, а иногда нет; это зависит от того, доступен ли исходящий контент через HTTPS. Если нет, то просто сломается.
Эль Йобо,

-3

Лучший способ работать для меня

<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" /> 
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.