如何处理网站中应作为 https 保护的混合内容?
Posted
技术标签:
【中文标题】如何处理网站中应作为 https 保护的混合内容?【英文标题】:How to deal with mixed content in a website which should be secured as https? 【发布时间】:2017-05-26 11:01:56 【问题描述】:我正在服务器 A 上构建一个网站(已注册域名),供人们创建和运行他们的“应用程序”。 这些“应用程序”实际上是运行在服务器 B 上的 docker 容器,在容器中,有一个可以直接访问的小型 Web 应用程序:
http://IP_ADDR_OF_SERVER_B:PORT
PORT 是一个随机的大数字,它映射到 docker 容器。 现在我可以使 SSL 证书在服务器 A 上运行,以便通过访问它可以正常工作:
https://DOMAIN_NAME_OF_SERVER_A
问题是,我通过访问上面的“http”将“应用程序”包含在 iframe 中,因此我的浏览器(Chrome)拒绝打开它并报告错误为:
Mixed Content: The page at 'https://DOMAIN_NAME_OF_SERVER_A/xxx' was loaded over HTTPS, but requested an insecure resource 'http://IP_ADDR_OF_SERVER_B:PORT/xxx'. This request has been blocked; the content must be served over HTTPS.
那么,我应该如何处理这样的问题呢? 我是一个全栈新手,如果您能分享一些有关如何构建健康的 https 网站的知识,同时以适当的方式解决此类问题,我将不胜感激。
补充说明
好的,我想我只是把问题的大纲扔掉了,这里有更多细节。
我认为使用 https 提供 iframe 请求是完整且直接的,这样就不会再让我感到困惑了。
但问题是,由于所有“应用程序”都是动态创建/删除的,看来我需要为每个应用程序准备许多证书。
自签名证书是否可以正常工作而不会被浏览器阻止或投诉?还是我有办法使用一个 SSL 证书为所有“应用程序”提供服务?
软件环境
服务器 A:运行 node.js 网站,监听 5000 端口并使用 nginx proxy_pass 提供服务。
server
listen 80;
server_name DOMAIN_NAME_OF_SERVER_A;
location /
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:5000;
server
listen 443;
server_name DOMAIN_NAME_OF_SERVER_A;
ssl on;
ssl_certificate /etc/nginx/ssl/DOMAIN_NAME_OF_SERVER_A.cer;
ssl_certificate_key /etc/nginx/ssl/DOMAIN_NAME_OF_SERVER_A.key;
ssl_session_timeout 5m;
location /
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:5000;
服务器 B:运行 node.js 应用程序监听不同的随机大端口号,例如 50055,在创建“应用程序”时动态分配。 (实际上这些应用程序在 docker 容器中运行,而我认为这无关紧要)如果需要可以运行 Nginx。
服务器 A 和 服务器 B 在公共交通中相互交谈。
解决方案
就像所有答案一样,尤其是来自@eawenden 的答案,我需要一个反向代理来实现我的目标。
除此之外,我还做了几件事: 1. 为 Server B 分配一个域名以使用letsencrypt 证书。 2. 将预定义的 url 代理到特定端口。
因此,我在 Server B 上使用 nginx 设置了一个反向代理服务器,代理所有请求,例如:
https://DOMAIN_NAME_OF_SERVER_B/PORT/xxx
到
https://127.0.0.1:PORT/xxx
Ps:Server B
上的nginx反向代理配置server
listen 443;
server_name DOMAIN_NAME_OF_SERVER_B;
ssl on;
ssl_certificate /etc/nginx/ssl/DOMAIN_NAME_OF_SERVER_B.cer;
ssl_certificate_key /etc/nginx/ssl/DOMAIN_NAME_OF_SERVER_B.key;
ssl_session_timeout 5m;
rewrite_log off;
error_log /var/log/nginx/rewrite.error.log info;
location ~ ^/(?<port>\d+)/
rewrite ^/\d+?(/.*) $1 break;
proxy_pass http://127.0.0.1:$port;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
因此,一切似乎都按预期工作! 再次感谢所有回答者。
【问题讨论】:
为什么不能使用带有 https 的 iframe 加载应用程序?解决此问题的唯一正确方法是通过 https 加载 iframe 内容。否则,您将面临创建安全漏洞的风险。 我想对我的问题添加一些补充说明。 太好了,感谢您的解决方案!这是工作! 【参考方案1】:正如其他人所说,您应该通过 HTTPS 提供所有内容。
您可以使用 http 代理来执行此操作。这意味着服务器 A 将处理 HTTPS 连接并通过 HTTP 将请求转发到服务器 B。然后 HTTP 会将响应发送回服务器 A,服务器 A 将更新响应标头,使其看起来像响应来自服务器 A 本身并将响应转发给用户。
您可以使服务器 B 上的每个应用程序在域 A 上的 URL 上可用,例如 https://www.domain-a.com/appOnB1 和 https://www.domain-a.com/appOnB2。然后代理会将请求转发到服务器 B 上的正确端口。
对于 Apache,这意味着在每个应用的配置中需要多出两行:
ProxyPass "/fooApp" "http://IP_ADDR_OF_SERVER_B:PORT"
ProxyPassReverse "/fooApp" "http://IP_ADDR_OF_SERVER_B:PORT"
第一行将确保 Apache 将此请求转发到服务器 B,第二行将确保 Apache 更改 HTTP 响应标头中的地址,使其看起来响应来自服务器 A 而不是服务器 B。
由于您需要使此代理动态化,因此在服务器 A 上的 NodeJS 应用程序中设置此代理可能更有意义,因为该应用程序可能已经了解服务器 B 上的不同应用程序。我我不是 NodeJS 专家,但快速搜索出现了 https://github.com/nodejitsu/node-http-proxy,看起来它可以解决问题,并且看起来像是一个维护良好的项目。
但总体思路保持不变:使用服务器 A 的 HTTPS 设置,您可以通过服务器 A 使用代理访问服务器 B 上的应用程序。在用户看来,服务器 B 上的所有应用程序都托管在域 A 上。
设置完成后,您可以使用 https://DOMAIN_NAME_OF_SERVER_A/fooApp
作为 iFrame 的 URL,以通过 HTTPS 加载应用程序。
警告:仅当您可以在内部路由此流量时才应该这样做(服务器 A 和 B 可以在同一网络上相互访问),否则流量可能会在来自服务器 A 的途中被拦截到服务器 B。
【讨论】:
感谢您的建议! 突然想到了,但我没有说清楚,所以还没有尝试过。猜猜我需要将请求设置为:https://DOMAIN_NAME_OF_SERVER_A/proxy?addr=NAME_OF_APP_ON_SERVER_B&port=PORT
以替换原来的 http 方式:http://IP_ADDR_OF_SERVER_B:PORT
是真的吗?即使,要实现这一点,我仍然不清楚如何实现它?我是否需要在服务器 A 上的 node.js 应用程序中添加一些 API?还是我需要在服务器 B 上使用 Nginx?
在我的原始答案中,您将为location /
块上方的每个应用程序添加一个位置块。恐怕查询参数不起作用,因为我认为 nginx 会在 location 块中忽略它。在此解决方案中,流量甚至不会影响您的 nodejs 应用程序,因此很难使其动态化。针对您的情况,一个更简单的解决方案可能是在 NodeJS 应用程序内部使用 http 代理。谷歌搜索提出了 github.com/nodejitsu/node-http-proxy 作为可能合适的包,但我不是 NodeJS 专家。
我已经编辑了我的答案,以使其更普遍地了解使用代理的想法,并包含有关 NodeJS 项目的一些内容。
谢谢伊文登!我有一个整体的了解。另一个快速的问题:由于我的服务器 A 和服务器 B 在公共流量中相互通信,根据您的 警告,似乎不可能在服务器 A 上添加反向代理。那么我应该为服务器 B 使用另一个域和证书,并在服务器 B 上执行 proxy_pass 吗?【参考方案2】:
最好的方法是使用反向代理 (Nginx supports them) 来提供对 docker 容器的访问:
反向代理服务器是一种代理服务器,通常位于 在专用网络中的防火墙后面并引导客户端请求 到适当的后端服务器。反向代理提供了一个 额外的抽象和控制级别,以确保流畅的流程 客户端和服务器之间的网络流量。
-Source
分配一个域名或只使用反向代理的IP地址并创建一个可信证书(Let's Encrypt提供免费证书)。然后,您可以使用受信任的证书通过 HTTPS 连接到反向代理,它将处理连接到正确的 Docker 容器。
以下是专门针对 Docker 的此类设置示例:https://github.com/jwilder/nginx-proxy
【讨论】:
感谢您的信息,亚历克斯!我会更深入地研究 nginx-proxy,稍后可能会寻求建议。【参考方案3】:错误消息几乎告诉您解决方案。
此请求已被阻止;内容必须通过 HTTPS 提供。
如果主页是通过 HTTPS 加载的,那么所有其他页面内容(包括 iframe)也应该通过 HTTPS 加载。
原因是不安全(非 HTTPS)流量可能在传输过程中被篡改,可能会被更改为包含更改安全内容的恶意代码。 (例如一个登录页面,其中注入了一个窃取用户 ID 和密码的脚本。)
== 更新以反映“补充信息”==
正如我所说,页面上的所有内容都需要通过 HTTPS 加载。是的,自签名证书可以工作,但有一些注意事项:首先,您必须告诉浏览器允许它们,其次,它们实际上只适合在开发环境中使用。 (您确实不想让用户养成点击安全警告的习惯。)
@eawenden 的回答提供了一种解决方案,使所有内容看起来都来自单个服务器,从而提供了一种使用单个证书的方法。请注意,反向代理是一个有些高级的主题,在生产环境中可能更难设置。
如果您控制所有 iframe 的服务器,另一种方法可能是使用通配符 SSL 证书。例如,这将针对 *.mydomain.com 发布,并且适用于 www.mydomain.com、subsite1.mydomain.com、subsite2.mydomain 等,适用于 mydomain.com 下的所有内容
【讨论】:
明白,请看我对我的问题的补充说明。 非常感谢您提供的额外说明。这是一个专业的答案,肯定会导致正确的解决方案。我认为还有一些更具体和更详细的观点让我思考,这可能超出了最初的问题。就像你提到的,关于反向代理。 是的,通配符证书将是一个很好的简单解决方案。唯一要解决的问题是您无法将端口添加到 DNS 配置中以直接指向 docker 应用程序,因此您需要在服务器 B 上设置代理以将传入流量定向到正确的应用程序。您可以使用github.com/jwilder/nginx-proxy 轻松设置。【参考方案4】:我有动态请求的混合内容问题
add_header 'Content-Security-Policy' 'upgrade-insecure-requests';
这解决了我的 ngnix 服务器问题
【讨论】:
以上是关于如何处理网站中应作为 https 保护的混合内容?的主要内容,如果未能解决你的问题,请参考以下文章