Lidar com conteúdo HTTP em páginas HTTPS

89

Temos um site que é acessado inteiramente por HTTPS, mas às vezes exibe conteúdo externo que é HTTP (imagens de feeds RSS, principalmente). A grande maioria de nossos usuários também está presa no IE6.

Eu gostaria de fazer ambos os seguintes

  • Impedir a mensagem de aviso do IE sobre conteúdo não seguro (para que eu possa mostrar um menos intrusivo, por exemplo, substituindo as imagens por um ícone padrão como abaixo)
  • Apresente algo útil aos usuários no lugar das imagens que eles não podem ver de outra forma; se houvesse algum JS que eu pudesse executar para descobrir quais imagens não foram carregadas e substituí-las por uma imagem nossa, isso seria ótimo.

Suspeito que o primeiro objetivo simplesmente não seja possível, mas o segundo pode ser suficiente.

O pior cenário é que eu analiso os feeds RSS quando os importamos, pego as imagens e os armazeno localmente para que os usuários possam acessá-los dessa forma, mas parece ser muito trabalhoso com um ganho razoavelmente pequeno.

El Yobo
fonte

Respostas:

147

Seu pior cenário não é tão ruim quanto você pensa.

Você já está analisando o feed RSS, então já tem os URLs das imagens. Digamos que você tenha um URL de imagem como http://otherdomain.com/someimage.jpg. Você reescreve este URL comohttps://mydomain.com/imageserver?url=http://otherdomain.com/someimage.jpg&hash=abcdeafad . Dessa forma, o navegador sempre faz solicitação por https, para você se livrar dos problemas.

A próxima parte - crie uma página de proxy ou servlet que faça o seguinte -

  1. Leia o parâmetro url da string de consulta e verifique o hash
  2. Baixe a imagem do servidor e faça proxy de volta para o navegador
  3. Opcionalmente, armazene a imagem em cache no disco

Essa solução tem algumas vantagens. Você não precisa baixar a imagem no momento de criar o html. Você não precisa armazenar as imagens localmente. Além disso, você não tem estado; o url contém todas as informações necessárias para veicular a imagem.

Finalmente, o parâmetro hash é para segurança; você só deseja que seu servlet forneça imagens para urls que você construiu. Então, quando você cria o url, calculemd5(image_url + secret_key) e acrescente-o como o parâmetro hash. Antes de atender à solicitação, calcule novamente o hash e compare-o com o que foi passado para você. Já que a secret_key só é conhecida por você, ninguém mais pode construir urls válidas.

Se você está desenvolvendo em java, o Servlet é apenas algumas linhas de código. Você deve conseguir portar o código abaixo em qualquer outra tecnologia 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
fonte
1
Muito bom, e acho que é com isso que vou trabalhar. Estamos usando PHP, mas a implementação também será trivial. Também implementarei o cache do nosso lado, já que não quero baixar a imagem toda vez que alguém solicitar (para desempenho e uso de largura de banda). As sugestões para a abordagem de segurança são sólidas (embora também apliquemos nosso modelo de segurança padrão, bem como o anterior). Obrigado por sua sugestão.
El Yobo
32
A única desvantagem séria dessa abordagem é que você está roteando todos os recursos externos por meio de seus próprios sistemas. O que não é apenas uma responsabilidade, mas também pode ser bastante caro.
Tim Molendijk
Eu segundo @TimMolendijk, acrescentando que não só adiciona custo e manutenção, mas também derrota quaisquer CDNs que deveriam ser roteados para servidores próximos ou balanceados para servidores ociosos.
Levente Pánczél
2
Qual é a solução para o NodeJS?
stkvtflw
1
outro +1 para @TimMolendijk mas qual seria a solução então? site servido por HTTPS não parece funcionar bem com imagens entregues via HTTP
FullStackForger
15

Se você está procurando uma solução rápida para carregar imagens por HTTPS, o serviço de proxy reverso gratuito em https://images.weserv.nl/ pode ser do seu interesse. Era exatamente o que eu procurava.

Se você está procurando uma solução paga, eu usei anteriormente o Cloudinary.com, que também funciona bem, mas é muito caro apenas para essa tarefa, na minha opinião.

anulável
fonte
Qual é o truque? Funciona muito bem
Jack
5
@JackNicholson Eu tenho usado sob carga relativamente pesada por 2 anos. Funciona bem! Parabéns aos dois desenvolvedores.
anulável em
Tenho alguns links (vídeo ou site) que começam com Http e não consigo exibi-los em um Iframe em nosso site https. Como este é um link não seguro, ele não está funcionando. para uma imagem, resolvi o problema usando o cache de imagem. Alguém tem alguma ideia
int14 de
@ int14 Você precisará configurar um proxy reverso para o site http; você pode fazer isso com algo como o AWS API Gateway.
anulável
3

Não sei se isso se encaixaria no que você está fazendo, mas como uma solução rápida, eu "embrulharia" o conteúdo http em um script https. Por exemplo, em sua página que é servida por https, eu introduziria um iframe que substituiria seu feed rss e no atributo src do iframe colocaria um url de um script em seu servidor que captura o feed e gera o html. o script está lendo o feed por meio de http e o envia por meio de https (portanto, "empacotamento")

Apenas um pensamento

hndcrftd
fonte
Parece-me que isso me deixaria na mesma situação que estou agora; Já estou mostrando o conteúdo em uma página HTTPS - o problema é que existem tags <img> no conteúdo com valores http: // src - que não são mostrados e fazem com que uma mensagem irritante ocorra.
El Yobo
bem, sim, se você mantiver os links originais para as imagens, não há como evitar o problema. O script de wrapper teria que escanear o conteúdo do feed de rss em busca de imagens e removê-las. Como você mencionou em outro comentário - você não quer carregar o conteúdo que causa o pop-up e mostrar algo informativo. Essa é a razão para o "script no meio"
hndcrftd
Você pode até fazer isso sem o iframe, direto no seu script de back-end principal, mas neste caso você está esperando que o feed rss volte antes de ser processado e enviado para uma página. Gostaria de fazer um iframe para que sua página carregue de forma assíncrona com o feed de rss. Também há a opção de ajax se você quiser ir para evitar o iframe. Apenas curiosidade - qual é a sua plataforma de back-end?
hndcrftd
2

Com relação ao seu segundo requisito - você pode utilizar o evento onerror, ou seja, <img onerror="some javascript;"...

Atualizar:

Você também pode tentar iterar document.imagesno dom. Existe uma completepropriedade booleana que você pode usar. Não sei ao certo se isso será adequado, mas pode valer a pena investigar.

Até o riacho
fonte
Interessante, eu nem sabia que havia um evento onerror. Eu teria que reescrever o HTML (porque ele vem de uma fonte externa), mas ele já está sendo higienizado com o purificador de HTML, então adicionar isso como um filtro pode ser possível.
El Yobo
Não ocorrerá nenhum aviso de segurança do navegador antes que o JavaScript tenha a chance de fazer alguma coisa?
MrWhite
0

Seria melhor ter apenas o conteúdo http em https

Daniel
fonte
5
Se eu não deixei isso claro na minha pergunta, o conteúdo HTTP está no servidor de outras pessoas, não é meu. Especificamente, são os links <img> em HTML que recuperei de feeds RSS. Eu enfatizei isso na pergunta agora.
El Yobo
Ok, webproworld.com/webmaster-forum/threads/… ajudaria em tudo?
Daniel
0

Às vezes, como nos aplicativos do Facebook, não podemos ter conteúdo não seguro na página segura. também não podemos tornar locais esses conteúdos. por exemplo, um aplicativo que será carregado no iFrame não é um conteúdo simples e não podemos torná-lo local.

Acho que nunca devemos carregar conteúdo http em https, também não devemos fazer fallback da página https para a versão http para evitar diálogo de erro.

a única maneira de garantir a segurança do usuário é usar a versão https de todos os conteúdos, http://developers.facebook.com/blog/post/499/

Mohammad Ali Akbari
fonte
3
Isso pode ser possível com o facebook, mas não para todos os conteúdos e esta questão não era sobre o facebook.
El Yobo
0

A resposta aceita me ajudou a atualizar isso para PHP e CORS, então pensei em incluir a solução para outros:

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 obter mais informações 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;
}
Suporte CFP
fonte
-2

Simplesmente: NÃO FAÇA ISSO. O conteúdo Http em uma página HTTPS é inerentemente inseguro. Ponto. É por isso que o IE mostra um aviso. Livrar-se do aviso é uma abordagem estúpida e besteira.

Em vez disso, uma página HTTPS deve apenas ter conteúdo HTTPS. Certifique-se de que o conteúdo também pode ser carregado via HTTPS e faça referência a ele via https se a página for carregada via https. Para conteúdo externo, isso significará carregar e armazenar em cache os elementos localmente para que estejam disponíveis via https - sure. Não há maneira de contornar isso, infelizmente.

O aviso existe por um bom motivo. Seriamente. Gaste 5 minutos pensando em como você poderia assumir o controle de uma página exibida em https com conteúdo personalizado - você ficará surpreso.

TomTom
fonte
3
Calma aí, estou ciente de que há um bom motivo para isso; Acho que o comportamento do IE é melhor do que o FF nesse aspecto. O que pretendo não é carregar o conteúdo; Eu só quero evitar o aviso de estilo pop-up intrusivo e mostrar algo informativo no lugar do conteúdo.
El Yobo
2
Sem chance para isso - a menos que você reescreva o HTML no caminho. Qualquer tentativa de pós-carregamento de javascript já mostrou a caixa de diálogo.
TomTom
Ele estava apenas perguntando sobre imagens e não está solicitando nenhum texto ou script inseguro para que possamos ignorar o aviso reescrevendo os urls.
Jayapal Chandran,
1
Nenhuma mudança na resposta. As imagens também podem ser inseguras. É uma coisa geral - ou vem de uma fonte segura ou pode ser substituído por um homem no meio do ataque.
TomTom
8
Votos negados porque esta "resposta" não respondia como atingir o objetivo do OP.
MikeSchinkel
-3

Sei que este é um tópico antigo, mas uma opção é apenas remover a parte http: do URL da imagem para que ' http: //some/image.jpg ' se torne '//some/image.jpg'. Isso também funcionará com CDNs

Shmarkus
fonte
7
Isso às vezes funciona e às vezes não; depende se o conteúdo upstream está disponível via HTTPS. Caso contrário, ele irá quebrar.
El Yobo
-3

Melhor maneira de trabalhar para mim

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