在HTTPS页面中处理HTTP内容


89

我们有一个完全通过HTTPS访问的站点,但有时会显示外部内容,即HTTP(主要来自RSS feed的图像)。我们的绝大多数用户还停留在IE6上。

理想情况下,我希望同时执行以下两项操作

  • 防止有关内容不安全的IE警告消息(这样,我就可以显示干扰性较小的消息,例如,将图像替换为下面的默认图标)
  • 向用户呈现一些有用的信息,以代替他们原本无法看到的图像;如果有一些JS,我可以运行以找出尚未加载的图像,并用我们的图像替换它们,那会很棒。

我怀疑第一个目标根本不可能实现,但第二个目标可能就足够了。

最坏的情况是,当我导入RSS提要时,我会对其进行解析,抓取将它们存储在本地的图像,以便用户可以以这种方式访问​​它们,但是似乎却很难受得了很多痛苦。

Answers:


147

最坏的情况并不像您想的那样糟糕。

您已经在解析RSS feed,因此已经有了图像URL。假设您有一个图片网址,例如http://otherdomain.com/someimage.jpg。您将该网址重写为https://mydomain.com/imageserver?url=http://otherdomain.com/someimage.jpg&hash=abcdeafad。这样,浏览器始终通过https发出请求,因此您可以摆脱这些问题。

下一部分-创建执行以下操作的代理页面或servlet-

  1. 从查询字符串中读取url参数,并验证哈希
  2. 从服务器下载图像,并将其代理回浏览器
  3. (可选)将映像缓存在磁盘上

该解决方案具有一些优点。创建html时,您不必下载图像。您不必将图像存储在本地。而且,你是无国籍的。网址包含提供图片所需的所有信息。

最后,hash参数是出于安全考虑;您只希望Servlet为已构造的网址提供图片。因此,当您创建url时,请计算md5(image_url + secret_key)并附加为hash参数。在处理请求之前,请重新计算哈希并将其与传递给您的哈希进行比较。由于只有您知道secret_key,因此没有其他人可以构造有效的url。

如果您使用Java开发,则Servlet只是几行代码。您应该能够在任何其他后端技术上移植以下代码。

/*
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 2010年

32
这种方法的唯一严重缺点是,您正在通过自己的系统路由所有外部资源。这不仅是一种责任,而且还可能会造成很高的代价。
Tim Molendijk 2011年

我第二个@TimMolendijk,补充说,它不仅增加了成本和维护成本,而且击败了所有可能路由到附近服务器或平衡到空闲服务器的CDN。
LeventePánczél2015年

2
NodeJS的解决方案是什么?
stkvtflw

1
@TimMolendijk的另一个+1,那么解决方案是什么?通过HTTPS服务的站点似乎无法很好地处理通过HTTP传送的图像
FullStackForger 2016年

15

如果您正在寻找一种通过HTTPS加载图像的快速解决方案,那么https://images.weserv.nl/上的免费反向代理服务可能会让您感兴趣。这正是我想要的。

如果您正在寻找付费解决方案,我以前曾使用过Cloudinary.com,它也能很好地工作,但我认为单独执行此任务太昂贵了。


有什么收获?效果很棒
杰克

5
@JackNicholson我已经在相对较重的负载下使用了2年。很棒!对两个开发人员表示敬意。

我有一些以Http开头的链接(视频或网站),但无法在https网站的iframe中显示它们。由于这是非安全链接,因此无法正常工作。对于图像,我已经使用图像缓存解决了该问题。任何人都有任何想法
int14

@ int14您将需要为http站点设置一个反向代理,您可以使用AWS API Gateway之类的方法进行此操作。

3

我不知道这是否适合您的工作,但是作为快速解决方案,我会将HTTP内容“包装”到https脚本中。例如,在通过https服务的页面上,我会引入一个iframe来代替您的rss feed,并在iframe的src属性中将一个脚本的url放置在服务器上,以捕获该feed并输出html。脚本正在通过http阅读提要,并通过https输出提要(因此“包装”)

只是一个想法


在我看来,这将使我处于与现在相同的境地。我已经在HTTPS页面中显示了内容-问题是内容中包含带有http:// src值的<img>标记-这些标记不会显示并引起烦人的消息。
El Yobo 2010年

好吧,是的,如果您保留了指向图像的原始链接,则无法避免该问题。包装脚本必须扫描rss feed内容中的图像并将其删除。正如您在另一条评论中提到的那样-您不想加载导致弹出窗口的内容并显示一些有用的信息。这就是“中间脚本”的原因
hndcrftd

您甚至可以在主后端脚本中没有iframe的情况下执行此操作,但是在这种情况下,您正在等待rss feed回来,然后再进行处理并在页面上输出。我会做一个iFrame,以便您的页面与rss feed异步加载。如果您想去那里避免iframe,也可以使用ajax选项。很好奇-您的后端平台是什么?
hndcrftd 2010年

2

关于第二个要求-您可以使用onerror事件,即。 <img onerror="some javascript;"...

更新:

您也可以尝试document.images在dom中进行迭代。有一个complete布尔属性,您可能可以使用。我不确定这是否合适,但可能值得调查。


有趣的是,我甚至不知道有一个onerror事件。我不得不重写HTML(因为它来自外部),但是已经被HTML净化器净化了,因此可以添加它作为过滤器。
El Yobo 2010年

JavaScript有机会做任何事情之前,不会发生任何浏览器安全警告吗?
怀特先生


0

有时像在Facebook应用中一样,我们不能在安全页面中包含非安全内容。我们也不能本地化那些内容。例如,要在iFrame中加载的应用不是简单的内容,因此我们无法将其设为本地。

我认为我们绝不应该在https中加载http内容,也不应将https页面回退到http版本,以防止出现错误对话框。

确保用户安全的唯一方法是使用所有内容的https版本,http://developers.facebook.com/blog/post/499/


3
使用Facebook可能是可行的,但并非所有内容都适用,并且此问题与Facebook无关。
El Yobo,2012年

0

接受的答案帮助我将其更新到了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 有关CORS的更多信息,请参见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;
}

-2

简单地说:不要这样做。HTTPS页面中的Http内容本质上是不安全的。点。这就是IE显示警告的原因。摆脱警告是一种愚蠢的洗碗方法。

相反,HTTPS页面应具有HTTPS内容。确保内容也可以通过HTTPS加载,如果页面通过https加载,则通过https引用。对于外部内容,这将意味着在本地加载和缓存元素,以便可以通过https访问它们-确保。可悲的是,没有办法解决。

出现警告是有充分理由的。说真的 花5分钟思考如何处理包含自定义内容的https显示页面-您会感到惊讶。


3
很容易,我知道这样做是有充分理由的。在这方面,我认为IE的行为优于FF。我的目标是加载内容;我只是想避免出现干扰性的弹出样式警告,并在内容上显示一些有用的信息。
El Yobo 2010年

2
没有机会-除非您在退出时重写HTML。任何javascript后加载尝试都已显示该对话框。
TomTom 2010年

他只是询问图像,而他并没有要求任何不安全的文本或脚本,因此我们可以通过重写网址来传递警告。
Jayapal Chandran 2011年

1
答案没有变化。图像也可能不安全。这是很普通的事情-来自安全来源,或者可能被中间攻击的人所取代。
TomTom

8
投反对票的原因是此“答案”没有回答如何实现OP的目标。
MikeSchinkel 2014年

-3

我意识到这是一个旧线程,但是一个选择就是从图像URL中删除http:部分,以使' http://some/image.jpg '变为'//some/image.jpg'。这也适用于CDN


7
这有时会起作用,有时则不会;这取决于上游内容是否可以通过HTTPS获得。如果没有,它会破裂。
El Yobo

-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" /> 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.