如何以安全的方式从 S3 提供 HLS 流(授权和认证)

Posted

技术标签:

【中文标题】如何以安全的方式从 S3 提供 HLS 流(授权和认证)【英文标题】:How to serve HLS streams from S3 in secure way (authorized & authenticated) 【发布时间】:2017-02-19 21:07:33 【问题描述】:

问题:

我用给定的文件结构在 S3 中存储 HLS 流的数量:

Video1
  ├──hls3
      ├──hlsv3-master.m3u8
      ├──media-1
      ├──media-2
      ├──media-3
      ├──media-4
      ├──media-5
  ├──hls4
      ├──hlsv4-master.m3u8
      ├──media-1
      ├──media-2
      ├──media-3
      ├──media-4
      ├──media-5

在我的用户 API 中,我知道哪个用户可以访问哪个视频内容 但我还需要确保视频链接不可共享且只能访问 由具有正确权限的用户。

解决方案:

1) 对私有 S3 内容使用签名/临时 S3 网址。每当客户想要播放一些特定的视频时 向我的 API 发送请求。如果用户有正确的权限 API 正在生成签名的 url 并将其返回给将其传递给播放器的客户端。

我在这里看到的问题是真实的视频内容存储在 media-* 目录中的几十个片段文件中 而且我真的不知道如何保护所有这些 - 我需要分别签署每个段文件 url 吗?

2) S3 内容是私有的。播放器发出的视频流请求通过我的 API 或单独的 reverse-proxy。 因此,每当客户端决定播放特定视频时,API / reverse-proxy 都会收到请求,进行身份验证和授权 并传递正确的内容(主播放列表文件和片段)。

在这种情况下,我仍然需要将 S3 内容设为私有,并且只能由我的 API / reverse-proxy 访问。这里应该推荐什么方式? S3 rest authentication via tokens?

3) 使用受保护的密钥进行加密。在这种情况下,所有视频片段都被加密并公开可用。密钥也存储在 S3 但不公开。玩家提出的每个密钥请求都由我的 API / reverse-proxy 进行身份验证和授权。

这是我现在想到的 3 个解决方案。不相信所有这些。我正在寻找一些简单且安全的东西。有什么建议/建议吗?

使用的技术:

ffmpeg 用于将视频编码为不同的比特率

bento4 用于视频分割

【问题讨论】:

【参考方案1】:

我需要分别对每个段文件的网址进行签名吗?

如果玩家直接从 S3 请求,那么是的。所以这可能不是理想的方法。

一个选项是存储桶前面的 CloudFront。 CloudFront 可以配置原始访问身份,允许它签署请求并将其发送到 S3,以便它可以代表授权用户获取私有 S3 对象,并且 CloudFront 支持两个签名 URL(使用与 S3 不同的算法,有两个重要的区别,我将在下面解释)或signed cookies。 CloudFront 中的签名请求和 cookie 的工作方式非常相似,重要的区别是 cookie 可以设置一次,然后浏览器自动将其用于每个后续请求,从而避免了对单个 URL 进行签名的需要。 (啊哈)

对于 CloudFront 中的签名 URL 和签名 cookie,如果您使用自定义策略,您将获得两个无法通过 S3 轻松完成的额外功能:

与 CloudFront 签名关联的策略可以允许 wildcard in the path,因此您可以授权访问其中的任何文件,例如 /media/Video1/*,直到签名过期。 S3 签名 URL 不支持任何形式的通配符 - S3 URL 只能对单个对象有效。

只要 CloudFront 分配仅配置为 IPv4,您就可以将签名绑定到特定的客户端 IP 地址,只允许使用该签名从单个 IP 地址进行访问(CloudFront 现在支持 IPv6 作为可选功能,但目前与此选项不兼容)。这有点激进,对于移动用户群来说可能是不可取的,因为移动用户群会在他们从提供商网络切换到 Wi-Fi 并返回时切换源地址。

仍然必须为所有内容链接生成已签名的 URL,但您可以只生成和签署一次 URL,然后重复使用签名,只需对每个文件的 URL 进行字符串重写,从而使该选项的计算成本更低。 .但仍然很麻烦。另一方面,签名的 cookie 应该对任何匹配的对象“正常工作”。

当然,添加 CloudFront 还应该通过缓存和缩短 Internet 路径来提高性能,因为与直接发送到 S3 的请求相比,请求跳到更靠近浏览器的托管 AWS 网络上。使用 CloudFront 时,来自浏览器的请求将发送到 60 多个全球“边缘站点”中的任意一个,这些站点被假定为离发出请求的浏览器最近。 CloudFront 可以为具有不同 URL 或 cookie 的不同用户提供相同的缓存对象,当然,只要 sig 或 cookie 有效。

要使用 CloudFront 签名的 cookie,您的应用程序的至少一部分(设置 cookie 的部分)需要位于指向存储桶的同一 CloudFront 分配的“后面”。这是通过将您的应用程序声明为分配的附加 Origin 并为特定路径模式创建缓存行为来完成的,当请求时,CloudFront 会将其转发到您的应用程序,然后可以使用适当的 Set-Cookie: 标头进行响应。

我不隶属于 AWS,所以不要将以下内容误认为是“推销”——只是预测你的下一个问题:CloudFront + S3 的定价使得与单独使用 S3 相比的成本差异通常可以忽略不计——当通过 CloudFront 请求对象时,S3 不会向您收取带宽费用,而且 CloudFront 的带宽费用在某些情况下略低于直接使用 S3 的费用。虽然这似乎违反直觉,但 AWS 以鼓励在其网络中分发请求而不是将它们全部集中在单个 S3 区域的方式来构建定价是有道理的。


请注意,任何机制,无论是上面的一种还是下面的一种都不能完全不受未经授权的“共享”的影响,因为身份验证信息必须对浏览器可用,因此对用户来说,这取决于用户的专业知识......但是这两种方法似乎都足以让诚实的用户保持诚实,这是你所能做的。由于签名 URL 和 cookie 上的签名具有过期时间,因此共享能力的持续时间是有限的,您可以通过 CloudFront 日志分析识别此类模式,并做出相应的反应。无论您采用何种方法,都不要忘记掌握日志的重要性。


如果运行该代理的 EC2 机器与存储桶位于同一 AWS 区域,并且代理基于可靠、高效的代码,例如 nginx 或 HAProxy 中的代码。

您无需在此环境中签署任何内容,因为您可以将存储桶配置为允许反向代理访问私有对象,因为它具有固定的 IP 地址。

在存储桶策略中,您可以通过授予“匿名”用户s3:getObject 权限来做到这一点,仅当他们的源 IPv4 地址与其中一个代理的 IP 地址匹配。代理代表授权用户从 S3 匿名请求对象(无需签名)。这要求您不使用 S3 VPC 终端节点,而是为代理提供弹性 IP 地址或将其放在 NAT 网关或 NAT 实例后面,并让 S3 信任 NAT 设备的源 IP。如果您确实使用 S3 VPC 端点,应该可以让 S3 信任请求,因为它遍历了 S3 VPC 端点,尽管我没有对此进行测试。 (S3 VPC 终端节点是可选的;如果您没有明确配置一个,那么您就没有,也可能不需要)。


如果我理解正确,您的第三个选项似乎最弱。授权但恶意的用户获得了可以整天共享的密钥。

【讨论】:

太棒了,非常感谢

以上是关于如何以安全的方式从 S3 提供 HLS 流(授权和认证)的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 CloudFront 从 AWS S3 安全地播放 .m3u8 流文件?

带有 HLS 冗余流和不良网络的奇怪 AVPlayer 行为

如何在 S3 中存储数据并允许用户使用 rails API / iOS 客户端以安全的方式访问?

HLS 直播服务器

如何使用 ffprobe 从 HLS/M3U8 文件中检测视频比特率

HLS介绍