保护无服务器应用程序的 Amazon S3 预签名 URL

作者: Raaga N.G, Ryan Hillard |

现代无服务器应用程序必须能够无缝处理大文件上传。本博客演示了如何利用 Amazon Simple Storage Service (Amazon S3) 的预签名 URL,让您的用户无需在亚马逊云科技账户中获得明确权限即可安全地将文件上传到 S3。这篇博客文章特别关注使用 S3 预签名 URL 的安全后果,并解释了无服务器开发人员为使用 S3 预签名 URL 提高系统的安全性可以采取的缓解措施。此外,该博客文章还介绍了一种符合所提建议的 Amazon Lambda 函数,确保采用强大而安全的方法处理 S3 预签名 URL。有关 S3 预签名 URL 的更多信息,请参阅使用预签名 URL。

无服务器应用程序的预签名 URL 工作流程

以下架构图说明了生成 S3 预签名 URL 的无服务器应用程序。通过使用 S3 预签名 URL,无服务器应用程序可以将接收文件所需的计算工作转移到 S3。该图记录了客户端、Amazon API Gateway、Lambda 函数和 S3 之间的七步流程。

将文件上传到 S3 上托管的无服务器应用程序的典型工作流程包括以下步骤:

  1. 客户提交上传文件的请求。
  2. API Gateway 接收客户端请求并调用 Lambda 函数,该函数随后生成 S3 预签名 URL。
  3. Lambda 函数对 S3 进行 getSignedURL API 调用。
  4. S3 返回要上传的对象的预签名 URL。
  5. Lambda 函数向 API 返回一个预签名的 URL。
  6. 客户端接收 S3 预签名 URL 来上传文件。
  7. 客户端使用预签名 URL 将文件直接上传到 S3。

如何保护预签名 URL

在设计利用 S3 预签名 URL 在 S3 中存储数据的无服务器应用程序时,开发人员必须考虑几个主要的安全方面。S3 预签名 URL 是不对用户进行身份验证的公共资源,任何拥有有效 S3 预签名 URL 的人都可以访问关联资源。因此,必须实施额外的安全措施,确保这些 URL 不会被未经授权的各方滥用或访问。以下博客文章包含可用于提高预签名 URL 安全的技术。

1. 使用 X-Amz-Signed 标头添加 Content-MD5 校验和

将对象上传到 S3 时,可以在请求中加入预先计算的对象校验和。S3 将执行完整性检查并验证发送的对象是否与收到的对象相同。S3 支持使用 MD5 校验和来验证上传对象的完整性。您可以通过在初始 PUT 请求中包含 Content-MD5 标头来提供 MD5 摘要。收到对象后,S3 将计算 MD5 摘要并将其与您最初提供的摘要进行比较。只有当两个 MD5 摘要都匹配时,上传操作才能成功,从而确保端到端数据的完整性。如果意外方获得了 S3 预签名 URL,则如果没有相同的对象,他们将无法使用它。这可以防止任意文件上传。

开发人员要记住的关键要素是,当客户端将文件上传到 S3 预签名 URL 时,它必须使用 Content-MD5 标头在 Base64 中提供正确的 MD5。开发人员可以看到带有客户端代码的示例无服务器应用程序,用于提取 MD5 摘要、请求 S3 预签名 URL 以及在此 GitHub 存储库中上传文件。此示例应用程序在 Lambda 函数中使用 NodeJS v20。

2. 过期 S3 预签名 URL

S3 预签名 URL 在生成 URL 时指定的时间段内保持有效。请务必确保 S3 预签名 URL 的访问时间不会超过所需的时间,因为它可以在仍然有效时重复使用。您可以通过 X-Amz-Expires 作为查询参数传递来定义 S3 预签名 URL 的到期时间,也可以在使用适用于 JavaScript 的亚马逊云科技开发工具包时设置 expiresIn 参数。

S3 在初始 HTTP 请求时验证过期时间和日期。但是,为了支持连接断开且客户端需要重新开始上传文件的情况,您可能希望 S3 预签名 URL 在将文件上传到 S3 所需的整个预期时间内保持有效。挑战在于生成一个 S3 预签名 URL,该 URL 的有效期足够长,足以容纳文件上传,但仍足够短,可以防止重复使用。

我们提出的克服这些挑战的解决方案是使用浏览器网络信息 API 动态设置 S3 预签名 URL 的到期时间。使用这个新的 API,当客户端浏览器发出 S3 预签名 URL 的初始请求时,客户端还会传输文件的大小和网络类型,因此 Lambda 函数可以计算预期的传输时间。

在 Lambda 函数中,我们现在可以使用此 GitHub 存储库中的示例代码来估算此类网络上这种大小的文件的传输时间。

计算出预计传输时间后,Lambda 函数现在可以请求 S3 预签名 URL 并将 expiresIn 参数设置为传输时间,从而生成 S3 预签名 URL,该预签名 URL 仅在该类型的网络上传该大小的文件所需的时间内可用。

如果您使用的是亚马逊云科技开发工具包,则可能还会使用亚马逊云科技签名版本 4 (SigV4) 来签署您的请求。要创建深度防御方法,对总到期时间设定上限,你可以在存储桶策略中使用条件密钥。有关策略示例,请参阅限制预签名 URL 功能。

3. 生成 UUID 来替换上传的文件名

当应用程序允许用户上传文件时,该应用程序会面临各种安全威胁,例如路径遍历攻击。路径遍历漏洞允许攻击者访问不打算访问的文件或覆盖预期目录结构之外的文件。为了保护您的应用程序免受此类漏洞的侵害,最有效的方法是整合用户输入验证和清理。您可以通过将文件名替换为生成的 UUID(通用唯一标识符)来对其进行消毒。

您可以在 GitHub 存储库中 Lambda 的服务器端代码中看到示例函数。

4. 应用最小权限原则并使用单独的 Lambda 函数创建 S3 预签名 URL

S3 预签名 URL 的功能受创建者权限的限制。为了提供精细的访问权限,限制使用 S3 预签名 URL 的第一步应该是构建生成这些 URL 的特定 Lambda 函数。通过使用专门用于此目的的 Lambda 函数,您就不会冒过于宽松的 Lambda 函数的风险。第二步是限制您的特定 Lambda 函数对 S3 的访问权限。

遵循最小权限原则,必须将 Lambda 函数的权限限制为仅限存储桶中所需的前缀,并仅允许其对存储桶执行所需的操作,而不是授予完整的存储桶访问权限。这最大限度地减少了潜在的攻击面,并降低了意外数据泄露或修改的风险。将权限限制为所需的最低操作和资源集非常重要。

这个示例 Amazon Identity and Access Management (IAM) 策略演示了如何授予 Lambda 函数对特定 S3 存储桶 "Example-Prefix" 前缀内的对象的读取访问权限 (GET)。IAM 策略通过执行角色附加到 Lambda 函数,这些角色共同确定 Lambda 函数可以执行哪些操作。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadStatement",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::EXAMPLE-BUCKET/Example-Prefix/",
        "arn:aws:s3:::EXAMPLE-BUCKET/Example-Prefix/*"
      ],
      "Effect": "Allow"
    }
  ]
}

此示例 IAM 策略演示如何授予 Lambda 函数在特定 S3 存储桶 "Example-Prefix" 前缀内上传 (PUT) 对象的权限。

{   
    "Version": "2012-10-17",
    "Statement": [
        {   
            "Sid": "UploadStatement",
            "Action": [
                "s3:PutObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::EXAMPLE-BUCKET/Example-Prefix/",
                "arn:aws:s3:::EXAMPLE-BUCKET/Example-Prefix/*"
            ],
            "Effect": "Allow"
        }
    ]
}

这种方法将确保您的 Lambda 函数拥有执行其预期任务所需的最低权限,并降低意外访问或修改数据的风险。

如果您想限制使用 S3 预签名 URL 以及对特定网络路径的所有 S3 访问权限,还可以在 S3 存储桶上定义网络路径限制策略。对存储桶的这种限制要求对存储桶的所有请求都来自指定的网络。亚马逊云科技规范性指南说,最低权限的扩展是维护符合组织需求的数据边界。亚马逊云科技边界的目标是确保只有当请求来自可信实体时,才允许访问来自可信网络的可信资源。这些数据边界也适用于 S3 预签名 URL。

5. 创建一次性使用的 S3 预签名 URL

无服务器应用程序开发人员可能希望每个 S3 预签名 URL 仅使用一次。开发人员可以整合基于令牌的机制,以促进安全地一次性使用 S3 预签名 URL。这包括为每个授权用户或客户端生成唯一的令牌,并将这些令牌与 S3 预签名 URL 相关联。当客户端尝试使用 S3 预签名 URL 访问资源时,他们必须提供相应的令牌进行验证。这一额外的安全层确保只有授权实体才能访问 S3 预签名 URL 和关联资源。此外,您可以利用数据库来跟踪已发行的令牌并在每次使用后将其过期。如何安全传输带有预签名 URL 的文件中详细讨论了实现这种机制的解决方案。

正在清理

您可以通过删除 API Gateway、Lambda 函数和 S3 存储桶来清理示例应用程序。此外,请不要忘记删除您为 Lambda 函数创建的任何 IAM 执行角色。

结论

在本博客中,我们讨论了开发人员在设计利用 S3 预签名 URL 的应用程序时必须考虑的各种注意事项。通过整合强大的安全措施,例如适当的访问控制、输入清理、到期处理和完整性检查,开发人员可以降低使用 S3 预签名 URL 时的潜在风险。


*前述特定亚马逊云科技生成式人工智能相关的服务仅在亚马逊云科技海外区域可用,亚马逊云科技中国仅为帮助您发展海外业务和/或了解行业前沿技术选择推荐该服务。