非常老的问题,但我在类似的帖子上发布了我对类似问题的解决方案,所以我也在这里分享它在 iOS 浏览器中流式传输 AWS S3 HLS 视频
---------------以下更新---------------
(并找个舒适的座位)
使用安全 cookie(适用于基于 iOS 的浏览器 + 所有 Safari)和适用于 Chrome 和其他所有内容的安全 URL,通过 Cloudfront 从 S3 提供受保护的视频。
网站架构:
- 前端:ReactJS
- 后端:NodeJS
- 云服务架构:https ://aws.amazon.com/blogs/media/creating-a-secure-video-on-demand-vod-platform-using-aws/ (以及随附的实验室指南)
假设:与上述云架构等效的设置,特别是 CF 到 S3 存储桶的 IAM 配置,以及 IAM 和 CORS 的相关 S3 安全配置。
TL/DR:
NON-SAFARI aka Chrome 等 - 使用安全网址(非常简单的 OOTB);上述指南适用于 chrome,但不适用于 safari。
Safari 需要安全的 cookie 来本地流式传输 hls,并且根本不会识别 xhr.beforeRequest 重载。基于 SAFARI 的 SAFARI / iOS 浏览器 - 使用安全 cookie 下面的所有内容都解释了这一点。
设置cookies,听起来很容易!这可能是为什么在 AWS CloudFront、AWS 论坛或 AWSDeveloper Slack 频道中没有端到端示例的原因,它被认为很简单,因为,嘿,它只是 cookie 对吗?
正确的。
结束 TL/DR
解决方案详情
“啊哈!” 片刻终于明白,要使其正常工作,您需要能够从您自己的服务器为云端服务器设置 cookie,这基本上是一个巨大的网络安全禁忌。aka - '域需要相同,一直向下/向上网络调用'</p>
在这里评论https://jwplayer-support-archive.netlify.app/questions/16356614-signed-cookies-on-cloudfront-with-hls-and-dash
在这里链接https://www.spacevatican.org/2015/5/1/using-cloudfront-signed-cookies/
两者都与原始 AWS 文档相结合,该文档带有一个域的 cname 以应用于子域,最终都为我结合了。
解决方案是:
- 为您的云端实例设置 CNAME;即:您不能针对 5j1h24j1j.cloudfront.net 设置 cookie,因为您不拥有它,但您可以在 DNS 中对 cloudfront.<your-domain>.com 进行 CNAME。在https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html中存在此特定步骤的良好文档
- 重要 - 您还需要在 CF 分发中设置此 CNAME 的引用。(如果您想要 SSL,您需要使用 cloudfront.<your-domain>.com 重新签署您的域证书,然后将此证书上传到 AWS Certificate Manager,以便在 CF Distribution 编辑屏幕下拉列表中引用它(其中,除非您选择某些内容,否则您无法保存)。
- 对于您的本地开发框,为 NodeJS 侦听/绑定的 IP 设置主机文件重载。即:如果您的节点绑定到 0.0.0.0,则编辑您的 /etc/hosts 以具有一行 0.0.0.0 dev.<your-domain>.com - 当您部署到生产主机时,域显然会在那里工作.
- 现在,在您将设置 cookie 的后端(也称为服务器端)代码中,您需要设置域参数,并且您不能直接使用通配符,但您可以将其保留为 <your-domain.com>(在一个浏览器,如果您使用开发人员工具进行检查,您将看到列为 .<your-domain>.com 注意前导点。这很好,并且是现代浏览器的预期行为;本质上是说“<your-domain 的任何子域>.com 将访问这些 cookie。
以上所做的是确保 END TO END,您能够发送 cookie,从 dev.<your-domain>.com 开始的调用或您未来的生产中分配给 .<your-domain>.com <your-domain>.com 到相同的 uri,但在后端的不同端口上,然后通过 CNAME 到 CF,这是 cookie 现在可以看到的子域。此时,由 CF 将所需的标头传递给 S3 实例。
但是等等,首先要做的是客户端。一开始甚至阻止我看到cookie的事情是,除非请求者/发起者在启动它的网络调用中使用'withCredentials:true'标志,否则它们不会被设置。在我的代码中,这是一个基于 ReactJS componentDidMount() 的 Axios 网络 REST GET 调用对我的后端 nodeJS 端点的视频列表(nodeJS 从 AWS 中的 graphQL 获取,但这对于我的修复的解释不需要)。
componentDidMount() {
axios.get('http://dev.<your-domain>.com:3000/api/my-data-endpoint'
,{
withCredentials: true,
})
.then(vidData => {
this.setState({
....//set stuff for player component include to use
});
})
}
当我的 axios 调用没有“withCredentials:true”时,cookie 永远不会被发回;我一有那个?我的 cookie 至少被发送回第一个调用者 localhost(cookie 中没有域参数,它默认调用,我当时作为本地主机),因此这意味着它永远不会将它传递给 CF,它是当时的 2435h23l4jjfsj.cloudfront.net 名称。
因此,在调用有关视频的后端信息时,更新 axios 以使用 dev.<your-domain>.com 进行服务器访问,并设置了我的 cookie 的 withCredentials 标志。正如 AWS 文档所指出的那样,cookie 需要在调用安全内容之前完全设置,这样就完成了。
在上述对我的 api 的调用中,我得到了类似的东西
{src:’https://cloudfront.<your-domain>.com/path-to-secure-register-m3u8-file’, qps:’?policy=x&signature=y&key-pair-id=z’, blah blah}
[侧边栏 - 签名的 url 均由 lambda 在云中生成]
对于 Chrome,播放器代码会将两者附加在一起,然后无论您在哪里实例化 video.js 播放器,重载 videojs.Hls.xhr.beforeRequest 如下
videojs.Hls.xhr.beforeRequest = function (options) {
options.uri = `${options.uri}${videojs.getAllPlayers()[0].options().token}`;
return options;
};
在寄存器 m3u8 文件启动后,将 ?policy=x&signature=y&Key-Pair-ID=z 的查询字符串放在流中每个子文件的末尾。
对上述 api 的后端调用,也会在 json 作为响应发送之前拆开 QP 以设置 cookie,如下所示
res.cookie("CloudFront-Key-Pair-Id", keypair, {httpOnly: true, path: "/", domain: ‘<your-domain>.com'});
res.cookie("CloudFront-Signature", sig, {httpOnly: true, path: "/", domain: ‘<your-domain>.com'});
res.cookie("CloudFront-Policy", poli, {httpOnly: true, path: "/", domain: ‘<your-domain>.com'});
中断- 现在我们已将 withCredentials 设置为 true,您可能会看到 CORS 问题;乐趣。在您的服务器端代码(我的 reactJS)中,我在我的 nodejs 路由器中设置了一些标头
res.header("Access-Control-Allow-Credentials", "true");
res.header("Access-Control-Allow-Origin", "http://dev.<your-domain>.com:8080"); // will be set to just <your-domain>.com for production
在这一点上,东西仍然没有工作。这是因为云代码将 CF 234hgjghg.cloudfront.net 域放入策略中,而不是我的 CNAME 映射。我在云端更新了这个。所以现在我对视频数据的调用,使用 cloudfront.<your-domain>.com 将 URL 返回到安全 m3u8,而不是https://forums.aws.amazon.com/thread.jspa 描述的 cloudfront.net? messageID=610961劑在最后一个响应步骤 3 中。
此时,如果我使用 safari 调试工具,我知道我已经接近了,因为我对尝试流式传输的响应从无密钥或 cookie xml 更改为
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
错误,其中是对我的 S3 存储桶的引用。这对我来说意味着,我的 CF 分发基本上对基于 cookie 的策略、密钥 ID 和签名感到满意,并且已将我传递给 S3,但 S3 让我迷路。
不过,此时的好处是,3 个必需的云端 cookie 是从 dev.<your-domain>.com 一直设置到 cloudfront.<your-domain>.com 调用 m3u8 注册文件,并且然后在对 .ts 或 .m3u8 的所有后续调用中
好的,所以我在 s3 配置中花了一些时间(没有编辑任何内容,只是查看所有内容……这对我来说看起来 100% 没问题),然后返回 CF 分发行为编辑页面,您可以在其中设置要转发的标题。设置(下面列出,然后是我的截图):
- 缓存和原始请求设置:使用旧缓存设置
- 基于所选请求标头的缓存 - 白名单
- 添加来源、访问控制请求标头、访问控制请求方法。您需要明确输入最后 2 个,它们没有为我自动完成,也没有显示在建议列表中,但添加自定义按钮有效。
- 对象缓存:使用原始缓存标头
- 转发 cookie/查询字符串 - 两者都没有(改进缓存)
- 限制查看者访问(使用签名的 url 或 cookie) - 是的(这是这个令人头疼的问题,哈哈)
- 受信任的签名者,自己
分发保存并传播后,Safari 和 Chrome 视频播放都可以正常工作!
这是一个相当大的漏洞,比我预期的要困难一个(或 15 个)学位,但当然,一旦它全部写出来,这一切似乎都是如此合乎逻辑和显而易见的。我希望这至少部分地帮助我在互联网上找到的其他人,使用 S3 前面的 AWS Cloudfront 在所有主要浏览器上安全地流式传输私有内容