使用 CloudFront 签名 URL 实现 S3 私有内容访问

为了实现 S3 私有内容的访问,可以使用 CloudFront 的签名 URL 功能。具体来说,可以将 S3 设置为私有访问,然后通过 CloudFront 提供的 https + URL 签名功能来实现访问。此外,还可以对签名 URL 进行 IP 和时间限制,以进一步提高访问的安全性。

1、S3 创建 bucket

创建一个 S3 bucket后,为了防止 S3 bucket 的公开访问,需要在权限设置中对其进行屏蔽。具体来说,可以选择阻止所有公共访问权限,以确保只有授权的用户才能够访问该 bucket。这样可以更好地保护存储在 S3 bucket 中的数据安全。

image-20230324141134022

2、CloudFront 创建分配

在 S3 bucket 创建完成后,您可以为其创建一个 CloudFront 分配,以便更加高效地访问其中的内容。具体来说,您可以在 CloudFront 中选择该 S3 bucket,并创建一个新的分配。这将使您能够通过全球分布的边缘节点快速、可靠地访问存储在 S3 bucket 中的数据。

image-20230324142002580

要对刚刚创建的 CloudFront 分配进行设置,可以进入其管理界面,并点击“源”标签页。在这里,您可以选择分配的源并进行编辑,以进一步优化分配的性能和安全性。

image-20230324142357321

要复制策略以便在 S3 bucket 中配置,请在当前页面点击“复制策略”按钮,然后将其应用于所需的 S3 bucket。这个策略将帮助您更好地管理 S3 bucket 中的对象,确保其安全性和一致性。

image-20230324142723407

3、设置 bucket 权限

回到 S3 bucket 的权限页面,在“存储桶策略”下点击“编辑”,将在 CloudFront 中复制的策略粘贴到此处,然后保存即可。这将确保 S3 bucket 受到相应的访问限制,并与 CloudFront 配合工作,为您提供更安全、高效的内容分发服务。

image-20230324142900119

image-20230324143045883

通过上述步骤的设置,S3 bucket 已经被成功限制访问,只能通过 CloudFront 进行代理访问。然而,目前为止所有的 URL 都是公开可见的,这可能会对数据的安全性造成潜在威胁。为了进一步保护数据的安全,需要对每个 URL 进行单独加密。下面将介绍如何进行 URL 加密的操作。

4、创建秘钥

在对 URL 进行加密之前,需要先创建一个秘钥对。创建完成后,您需要将公钥添加到 CloudFront 的秘钥管理中,以便将其用于加密 URL。

image-20230324143647862

image-20230324143559120

成功创建秘钥后,请务必牢记其对应的秘钥 ID。在生成 URL 签名时,秘钥 ID 是必需的参数。

image-20230324143802654

接下来,需要创建一个密钥组,并将刚刚创建的秘钥与该组相关联。在 CloudFront 中配置秘钥时,需要选择该密钥组。这样做的好处是,可以针对不同的 URL 使用不同的签名,进一步增强数据的安全性。

image-20230324144133580

image-20230324144202250

image-20230324144223015

5、限制查看器访问

在完成秘钥和密钥组的创建之后,回到之前创建的 CloudFront 分配页面。然后,点击“行为”标签页,并编辑行为设置。

image-20230324144637102

在编辑页面中,将“限制查看器访问”选项设为“Yes”,并选择“可信授权类型”为“ Trusted key groups (recommended)”。选择刚刚创建的密钥组,点击“保存”即可完成 CloudFront 的配置。

image-20230324144850214

6、编写签名代码

完成上述配置后,下一步是在代码中实现 URL 签名。以下是 Java 代码示例:

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

// CloudFront 分配的域名,也可以绑定自己的域名。
String distributionDomain = "d30xklqmm46leu.cloudfront.net";

// S3 即将被访问的对接
String s3ObjectKey = "avatar/n01582220_6305.jpg";

// 秘钥 ID
String keyPairId = "K1S0R8IKNRE91H";

// 私钥
File privateKeyFile = new File("F:\\data\\cloudfront_ssl\\private_key.der");

// 允许被访问的 IP
String ipRange = "0.0.0.0/0";

// URL 签名的有效时间
Date dateLessThan = DateUtils.parseISO8601Date("2024-11-14T22:20:00.000Z");
Date dateGreaterThan = DateUtils.parseISO8601Date("2023-03-20T22:20:00.000Z");

String signedUrl = CloudFrontUrlSigner.getSignedURLWithCustomPolicy(
    SignerUtils.Protocol.https, distributionDomain, privateKeyFile,
    s3ObjectKey, keyPairId, dateLessThan,
    dateGreaterThan, ipRange);
System.out.println(signedUrl);

7、测试

运行代码后,将会输出以下结果:

https://d30xklqmm46leu.cloudfront.net/avatar/n01582220_6305.jpg?Policy=eyJTdGF0ZW1lbnQiOiBbeyJSZXNvdXJjZSI6Imh0dHBzOi8vZDMweGtscW1tNDZsZXUuY2xvdWRmcm9udC5uZXQvYXZhdGFyL24wMTU4MjIyMF82MzA1LmpwZyIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTczMTYyMjgwMH0sIklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIwLjAuMC4wLzAifSwiRGF0ZUdyZWF0ZXJUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE2NzkzNTA4MDB9fX1dfQ__&Signature=CArbpEYmHU3XKLQEFHQmKCbQSERnCRovQeto6JjVV0-58gC2oY7uu0~B2OHJ3NHT72ZBDcI~2JSMZOhuy7L3qKcVS2bVfr62BOT3KajbZADClB2F1v-F0MumqOGyJFJqCptntkG1gZMahvBEXtOUi5d1bcua7u04yQNNwky7o4PLKqzMFIB~fp1Ub0IYO0fPdcC1iS8eQIKh6ehMiX2iLBtSqWz-KtJwvadWArkf~PIm6RHHycTj8U95ZrD-DV69Qh0pE2sJan-g3YhyqEmyN7Nq1GRK8B9X-yMoRwEdX68jemviOnfSOmAUZtrhH6-Lc-BNriV6knUHzRmokyo5Fg__&Key-Pair-Id=K1S0R8IKNRE91H

需要注意的是,签名后的 URL 在使用 http 和 https 协议时需要分别签名,不能通用。

8、附录

8.1、生成秘钥

使用 OpenSSL 生成长度为 2048 位的 RSA 密钥对,并将其保存到名为 private_key.pem 的文件中。

openssl genrsa -out private_key.pem 2048

上面生成的文件同时包含公有密钥和私有密钥。从名为 private_key.pem 的文件中提取公有密钥。

openssl rsa -pubout -in private_key.pem -out public_key.pem

针对 Java 不能以默认 PEM 格式使用密钥对中的私有密钥来创建签名,要重新设置秘钥对格式为 DER。

openssl pkcs8 -topk8 -nocrypt -in private_key.pem -inform PEM -out private_key.der -outform DER

8.2、参考资料

使用签名 URL 和签名 Cookie 提供私有内容

AWS SDK for Java 文档

CloudFront 使用 SDK for Java 2.x

使用 Java 创建 URL 签名

本博客采用 知识共享署名-禁止演绎 4.0 国际许可协议 进行许可

本文标题:使用 CloudFront 签名 URL 实现 S3 私有内容访问

本文地址:https://jizhong.plus/post/2023/03/aws-s3-cloudfront-url-signed.html