如何以安全的方式(授权和认证)从 S3 提供 HLS 流
How to serve HLS streams from S3 in secure way (authorized & authenticated)
问题:
我正在 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 urls 用于私人 S3 内容。每当客户想要播放一些特定的视频时
向我的 API 发送请求。如果用户有权限 API 正在生成签名 url
并将其返回给正在传递给玩家的客户端。
我在这里看到的问题是真实的视频内容存储在 media-* 目录中的十几个片段文件中
我真的不知道如何保护所有这些文件——我需要分别签署每个段文件 url 吗?
2) S3 内容是私人的。玩家发出的视频流请求正在通过我的 API 或单独的 reverse-proxy。
因此,每当客户端决定播放特定视频时,API / reverse-proxy 都会收到请求,进行身份验证和授权
并传递正确的内容(主播放列表文件和片段)。
在这种情况下,我仍然需要将 S3 内容设为私有,并且只能由我的 API / 反向代理。这里推荐的方式应该是什么?
S3 rest authentication via tokens?
3) 使用受保护密钥进行加密。在这种情况下,所有视频片段都被加密并公开可用。密钥也存储在 S3
但不公开。玩家发出的每个密钥请求都经过我的 API / reverse-proxy.
的身份验证和授权
这是我现在想到的 3 个解决方案。不相信所有这些。我正在寻找简单且防弹安全的东西。有什么建议吗?
使用技术:
would I need to sign each of the segment file urls separately?
如果播放器直接从 S3 请求,则可以。所以这可能不是理想的方法。
一个选项是存储桶前面的 CloudFront。 CloudFront 可以配置源访问身份,这允许它签署请求并将它们发送到 S3,以便它可以代表授权用户获取私有 S3 objects,并且 CloudFront 支持签名 URLs(使用与 S3 不同的算法,有两个重要的区别,我将在下面解释)或 signed cookies。 CloudFront 中的签名请求和 cookie 的工作方式非常相似,重要的区别在于 cookie 可以设置一次,然后浏览器会自动为每个后续请求使用它,从而避免了对单个 URL 进行签名的需要。 (啊哈。)
对于 CloudFront 中的已签名 URL 和已签名 cookie,如果您使用自定义策略,您将获得两个使用 S3 不容易完成的附加功能:
与 CloudFront 签名关联的策略可以允许 wildcard in the path,因此您可以授权访问 /media/Video1/*
中的任何文件,直到签名过期为止。 S3 signed URLs 不支持任何形式的通配符 -- S3 URL 只能对单个 object.
有效
只要 CloudFront 分配仅针对 IPv4 配置,您就可以将签名绑定到特定的客户端 IP 地址,只允许从单个 IP 地址使用该签名进行访问(CloudFront 现在支持 IPv6作为可选功能,但目前与此选项不兼容)。这有点激进,对于移动用户群来说可能是不可取的,因为移动用户群会在从提供商网络切换到 Wi-Fi 并返回时切换源地址。
仍然必须为所有内容链接生成已签名的 URL,但您可以只生成并签署 URL 一次,然后重复使用签名,只需 string-rewriting每个文件的 URL 使该选项在计算上更便宜......但仍然很麻烦。另一方面,签名的 cookie 应该 "just work" 用于任何匹配 object.
当然,添加 CloudFront 还应该通过缓存和 Internet 路径缩短来提高性能,因为请求会跳到离浏览器更近的托管 AWS 网络,而不是直接发送到 S3 的请求。使用 CloudFront 时,来自浏览器的请求将被发送到 60+ global "edge locations" 中的任意一个,假定最接近发出请求的浏览器。 CloudFront 可以为具有不同 URL 或 cookie 的不同用户提供相同的缓存 object,当然,只要信号或 cookie 有效。
要使用 CloudFront 签名的 cookie,至少您的应用程序的一部分(设置 cookie 的部分)需要 "behind" 指向存储桶的相同 CloudFront 分配。这是通过将您的应用程序声明为分发的附加来源,并为特定路径模式创建缓存行为来完成的,当请求时,CloudFront 将其转发到您的应用程序,然后应用程序可以响应适当的 Set-Cookie:
headers.
我不隶属于 AWS,因此请不要将以下内容误认为是 "pitch"——只是期待您的下一个问题:CloudFront + S3 的定价与单独使用 S3 相比的成本差异是通常可以忽略不计——当通过 CloudFront 请求 objects 时,S3 不会向您收取带宽费用,并且 CloudFront 的带宽费用在某些情况下略低于直接使用 S3 的费用。虽然这似乎违反直觉,但 AWS 以鼓励在其网络中分发请求而不是将它们全部集中在单个 S3 区域的方式构建定价是有道理的。
请注意,无论是上面的机制还是下面的机制,都无法完全避免未经授权的情况 "sharing,",因为根据用户的专业知识,身份验证信息必须可供浏览器使用,从而可供用户使用......但是这两种方法似乎都足以让诚实的用户保持诚实,这是你所能希望做的。由于签名 URL 和 cookie 上的签名有过期时间,因此 share-ability 的持续时间是有限的,您可以通过 CloudFront 日志分析识别此类模式,并做出相应的反应。无论您采用何种方法,都不要忘记掌握日志的重要性。
反向代理也是一个好主意,可能很容易实现,并且如果 EC2 机器 运行 代理位于同一个 AWS 区域,则应该在没有额外数据传输费用或吞吐量问题的情况下执行相当可接受作为存储桶,代理基于可靠、高效的代码,例如 Nginx 或 HAProxy 中的代码。
你不需要在这个环境中签署任何东西,因为你可以配置 bucket 允许反向代理访问私有 objects 因为它有一个固定的 IP 地址。
在存储桶策略中,您通过授予 "anonymous" 用户 s3:getObject
权限来实现此目的,仅当 他们的源 IPv4 地址与代理人之一。代理代表授权用户从 S3 匿名请求 objects(无需签名)。这要求您不使用 S3 VPC 端点,而是为代理提供弹性 IP 地址或将其置于 NAT 网关或 NAT 实例后面,并让 S3 信任 NAT 设备的源 IP。如果您 do 使用 S3 VPC 端点,应该可以允许 S3 信任该请求,因为它遍历了 S3 VPC 端点,尽管我还没有对此进行测试。 (S3 VPC 端点是可选的;如果您没有明确配置一个,那么您就没有,也可能不需要)。
如果我理解正确的话,你的第三个选项似乎最弱。获得授权但恶意的用户获取密钥并可以整天共享它。
问题:
我正在 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 urls 用于私人 S3 内容。每当客户想要播放一些特定的视频时 向我的 API 发送请求。如果用户有权限 API 正在生成签名 url 并将其返回给正在传递给玩家的客户端。
我在这里看到的问题是真实的视频内容存储在 media-* 目录中的十几个片段文件中 我真的不知道如何保护所有这些文件——我需要分别签署每个段文件 url 吗?
2) S3 内容是私人的。玩家发出的视频流请求正在通过我的 API 或单独的 reverse-proxy。 因此,每当客户端决定播放特定视频时,API / reverse-proxy 都会收到请求,进行身份验证和授权 并传递正确的内容(主播放列表文件和片段)。
在这种情况下,我仍然需要将 S3 内容设为私有,并且只能由我的 API / 反向代理。这里推荐的方式应该是什么? S3 rest authentication via tokens?
3) 使用受保护密钥进行加密。在这种情况下,所有视频片段都被加密并公开可用。密钥也存储在 S3 但不公开。玩家发出的每个密钥请求都经过我的 API / reverse-proxy.
的身份验证和授权这是我现在想到的 3 个解决方案。不相信所有这些。我正在寻找简单且防弹安全的东西。有什么建议吗?
使用技术:
would I need to sign each of the segment file urls separately?
如果播放器直接从 S3 请求,则可以。所以这可能不是理想的方法。
一个选项是存储桶前面的 CloudFront。 CloudFront 可以配置源访问身份,这允许它签署请求并将它们发送到 S3,以便它可以代表授权用户获取私有 S3 objects,并且 CloudFront 支持签名 URLs(使用与 S3 不同的算法,有两个重要的区别,我将在下面解释)或 signed cookies。 CloudFront 中的签名请求和 cookie 的工作方式非常相似,重要的区别在于 cookie 可以设置一次,然后浏览器会自动为每个后续请求使用它,从而避免了对单个 URL 进行签名的需要。 (啊哈。)
对于 CloudFront 中的已签名 URL 和已签名 cookie,如果您使用自定义策略,您将获得两个使用 S3 不容易完成的附加功能:
与 CloudFront 签名关联的策略可以允许 wildcard in the path,因此您可以授权访问
/media/Video1/*
中的任何文件,直到签名过期为止。 S3 signed URLs 不支持任何形式的通配符 -- S3 URL 只能对单个 object. 有效
只要 CloudFront 分配仅针对 IPv4 配置,您就可以将签名绑定到特定的客户端 IP 地址,只允许从单个 IP 地址使用该签名进行访问(CloudFront 现在支持 IPv6作为可选功能,但目前与此选项不兼容)。这有点激进,对于移动用户群来说可能是不可取的,因为移动用户群会在从提供商网络切换到 Wi-Fi 并返回时切换源地址。
仍然必须为所有内容链接生成已签名的 URL,但您可以只生成并签署 URL 一次,然后重复使用签名,只需 string-rewriting每个文件的 URL 使该选项在计算上更便宜......但仍然很麻烦。另一方面,签名的 cookie 应该 "just work" 用于任何匹配 object.
当然,添加 CloudFront 还应该通过缓存和 Internet 路径缩短来提高性能,因为请求会跳到离浏览器更近的托管 AWS 网络,而不是直接发送到 S3 的请求。使用 CloudFront 时,来自浏览器的请求将被发送到 60+ global "edge locations" 中的任意一个,假定最接近发出请求的浏览器。 CloudFront 可以为具有不同 URL 或 cookie 的不同用户提供相同的缓存 object,当然,只要信号或 cookie 有效。
要使用 CloudFront 签名的 cookie,至少您的应用程序的一部分(设置 cookie 的部分)需要 "behind" 指向存储桶的相同 CloudFront 分配。这是通过将您的应用程序声明为分发的附加来源,并为特定路径模式创建缓存行为来完成的,当请求时,CloudFront 将其转发到您的应用程序,然后应用程序可以响应适当的 Set-Cookie:
headers.
我不隶属于 AWS,因此请不要将以下内容误认为是 "pitch"——只是期待您的下一个问题:CloudFront + S3 的定价与单独使用 S3 相比的成本差异是通常可以忽略不计——当通过 CloudFront 请求 objects 时,S3 不会向您收取带宽费用,并且 CloudFront 的带宽费用在某些情况下略低于直接使用 S3 的费用。虽然这似乎违反直觉,但 AWS 以鼓励在其网络中分发请求而不是将它们全部集中在单个 S3 区域的方式构建定价是有道理的。
请注意,无论是上面的机制还是下面的机制,都无法完全避免未经授权的情况 "sharing,",因为根据用户的专业知识,身份验证信息必须可供浏览器使用,从而可供用户使用......但是这两种方法似乎都足以让诚实的用户保持诚实,这是你所能希望做的。由于签名 URL 和 cookie 上的签名有过期时间,因此 share-ability 的持续时间是有限的,您可以通过 CloudFront 日志分析识别此类模式,并做出相应的反应。无论您采用何种方法,都不要忘记掌握日志的重要性。
反向代理也是一个好主意,可能很容易实现,并且如果 EC2 机器 运行 代理位于同一个 AWS 区域,则应该在没有额外数据传输费用或吞吐量问题的情况下执行相当可接受作为存储桶,代理基于可靠、高效的代码,例如 Nginx 或 HAProxy 中的代码。
你不需要在这个环境中签署任何东西,因为你可以配置 bucket 允许反向代理访问私有 objects 因为它有一个固定的 IP 地址。
在存储桶策略中,您通过授予 "anonymous" 用户 s3:getObject
权限来实现此目的,仅当 他们的源 IPv4 地址与代理人之一。代理代表授权用户从 S3 匿名请求 objects(无需签名)。这要求您不使用 S3 VPC 端点,而是为代理提供弹性 IP 地址或将其置于 NAT 网关或 NAT 实例后面,并让 S3 信任 NAT 设备的源 IP。如果您 do 使用 S3 VPC 端点,应该可以允许 S3 信任该请求,因为它遍历了 S3 VPC 端点,尽管我还没有对此进行测试。 (S3 VPC 端点是可选的;如果您没有明确配置一个,那么您就没有,也可能不需要)。
如果我理解正确的话,你的第三个选项似乎最弱。获得授权但恶意的用户获取密钥并可以整天共享它。