尝试使用 cloudfront 和 privateKey 访问存储在 S3(静态 Web 托管)中的私有内容时获得 HTTP/1.1 403 Forbidden

Posted

技术标签:

【中文标题】尝试使用 cloudfront 和 privateKey 访问存储在 S3(静态 Web 托管)中的私有内容时获得 HTTP/1.1 403 Forbidden【英文标题】:get HTTP/1.1 403 Forbidden when trying to access private content stored in S3(static web hosting) using cloudfront and privateKey 【发布时间】:2019-12-28 20:09:20 【问题描述】:

我从 Java 应用程序收到来自 AWS CloudFront 的以下响应,我正在尝试使用签名的 cookie 访问私有内容(网页)。

HTTP/1.1 403 Forbidden [Content-Type: application/xml, Transfer-Encoding: chunked, Connection: keep-alive, Date: Fri, 23 Aug 2019 12:47:53 GMT, Server: AmazonS3, X-Cache :来自云端的错误,通过:1.1 1b964435****************d975cdd***.cloudfront.net (CloudFront),X-Amz-Cf-Pop:MXP64-C1,X-Amz-Cf- ID:6Waw****_ukbfaev1nrJZZYBl**********t66R9ctZ*****A==] org.apache.http.conn.BasicManagedEntity@5fdba6f9

我尝试了以下步骤:

    我已将 S3 配置为“静态网站托管” 将存储桶策略设置为:

    "Version": "2012-10-17",
    "Statement": [
          
            "Sid": "2",
            "Effect": "Allow",
            "Principal": 
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E1J***SIQ****"
            ,
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::my-xxxxx-s3-bucket/*"
        
    ]

    桶的CORS配置为:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
</CORSConfiguration>

    使用以下命令创建 CloudFront 分配:

    源站设置->源站域名:my-s3-bucket-name 源设置 -> 限制存储桶访问:是 限制查看者访问(使用签名 URL 或签名 Cookie):是的。 受信任的签名者:自我(已选中)。 保留其余属性的默认值。

    在(CloudFront 密钥对)下创建了安全凭证并下载了私钥。使用以下命令将 .pem 文件转换为 .der。

openssl pkcs8 -topk8 -nocrypt -in origin.pem -inform PEM -out new.der -outform DER 
    创建了一个具有以下依赖项的 Maven 项目:
<dependencies>
  <dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk</artifactId>
    <version>1.11.327</version>
  </dependency>

    <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.62</version>
</dependency>

<!-- https://mvnrepository.com/artifact/net.java.dev.jets3t/jets3t -->
<dependency>
    <groupId>net.java.dev.jets3t</groupId>
    <artifactId>jets3t</artifactId>
    <version>0.9.4</version>
</dependency>
    代码如下,尝试使用签名的 cookie 访问“index.html”文件(保存在 S3 的根目录中):
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.spec.InvalidKeySpecException;
import java.text.ParseException;
import java.util.Date;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.jets3t.service.CloudFrontServiceException;

import com.amazonaws.services.cloudfront.CloudFrontCookieSigner;
import com.amazonaws.services.cloudfront.CloudFrontCookieSigner.CookiesForCustomPolicy;
import com.amazonaws.services.cloudfront.util.SignerUtils;
import com.amazonaws.services.cloudfront.util.SignerUtils.Protocol;
import com.amazonaws.util.DateUtils;

public class SignedCookies 

    public static void withCustom() throws InvalidKeySpecException, IOException

        Protocol protocol = Protocol.http;
        String resourcePath = "index.html";
        String distributionDomain = "***ju***lu***.cloudfront.net";
        String privateKeyFilePath = "my-path/pk-APKA####K3WH####7U##.der";
        File privateKeyFile = new File(privateKeyFilePath);
        String s3ObjectKey = "index.html";
        String keyPairId = "APKA####K3WH####7U##";


         Date activeFrom = DateUtils.parseISO8601Date("2018-11-14T22:20:00.000Z");
         Date expiresOn = DateUtils.parseISO8601Date("2020-11-14T22:20:00.000Z");
         String ipRange = null;

         CookiesForCustomPolicy cookies = CloudFrontCookieSigner.getCookiesForCustomPolicy(
                      protocol, distributionDomain, privateKeyFile, s3ObjectKey,
                      keyPairId, expiresOn, activeFrom, ipRange);


         @SuppressWarnings( "resource", "deprecation" )
        HttpClient client = new DefaultHttpClient();
         HttpGet httpGet = new HttpGet(
                      SignerUtils.generateResourcePath(protocol, distributionDomain,
                      resourcePath));

         httpGet.addHeader("Cookie", "Secure");
         httpGet.addHeader("Cookie", cookies.getPolicy().getKey() + "=" +
             cookies.getPolicy().getValue());
         httpGet.addHeader("Cookie", cookies.getSignature().getKey() + "=" +
             cookies.getSignature().getValue());
         httpGet.addHeader("Cookie", cookies.getKeyPairId().getKey() + "=" +
             cookies.getKeyPairId().getValue());


         HttpResponse response = client.execute(httpGet);

         System.out.println(response.toString());

    

    public static void main(String[] args) throws FileNotFoundException, IOException, CloudFrontServiceException, ParseException, InvalidKeySpecException 
        withCustom();
    



    我收到了 403 响应。

我该如何解决这个问题?

【问题讨论】:

错误来自S3,所以你需要检查s3的权限,1.在bucket上启用list bucket权限,看看你是否得到404 2.检查你的账户是index.html对象的所有者 @JamesDean:感谢您的 cmets。在上面的例子中。我从下拉列表中选择 Origin Domain Name(作为 s3 存储桶)并使用 OAI,我使用 S3 的地方被配置为静态网站,这是我的第一个错误。无论如何,我通过提供自定义来源名称(静态网站端点 URL)解决了这个问题。在未启用“使用签名 URL 或签名 Cookie”的情况下进行了测试,并且没有任何问题。如果我启用“签名 URL 或签名 Cookie”并尝试访问签名 URL 得到 403 错误,我还无法解决。如果您能帮助我,我将提供下面的代码示例。 【参考方案1】:

请先阅读我在第一篇文章下的最后一条评论。这部分与上一篇文章有​​关。这是我用来使用 signUrlCanned 对 URL 进行签名并获取内容的代码示例,但是当我尝试使用 buildPolicyForSignedUrl 时出现访问被拒绝错误。

@JamesDean:感谢您的 cmets。在上面的例子中。我选择 下拉列表中的源域名(作为 s3 存储桶)并使用 OAI 我使用 S3 的地方被配置为静态网站,这是我的第一个 错误。无论如何,我通过提供自定义来源名称解决了这个问题 (静态网站端点 URL)。然后在不启用“使用 签名的 URL 或签名的 Cookie”,并且没有任何问题。但如果我 启用“使用签名 URL 或签名 Cookie”并尝试使用签名 URL 或签名的 cookie 出现我还无法解决的 403 错误。我是 如果您能帮助我,请提供下面的代码示例。

使用以下代码,我得到正确的签名 url 并可以访问内容:

byte[] derPrivateKey = EncryptionUtil.convertRsaPemToDer(new FileInputStream(privateKeyFilePath));
//      Generate a "canned" signed URL to allow access to a specific distribution and object

        String signedUrlCanned = CloudFrontService.signUrlCanned(
            "https://" + distributionDomain + "/" + s3ObjectKey, // Resource URL or Path
            keyPairId,     // Certificate identifier, an active trusted signer for the distribution
            derPrivateKey, // DER Private key data
            ServiceUtils.parseIso8601Date("2020-08-30T22:20:00.000Z") // DateLessThan
            );
        System.out.println(signedUrlCanned);

另一方面,当我尝试使用自定义策略访问相同 s3 内容(根目录中的 index.html)的内容时,我被拒绝访问:

String policyResourcePath = distributionDomain + "/*" ;
//      Convert an RSA PEM private key file to DER bytes

        byte[] derPrivateKey = EncryptionUtil.convertRsaPemToDer(new FileInputStream(privateKeyFilePath));

        String policy = CloudFrontService.buildPolicyForSignedUrl(
                policyResourcePath, // Resource path (optional, may include '*' and '?' wildcards)
                ServiceUtils.parseIso8601Date("2020-11-14T22:20:00.000Z"), // DateLessThan
                "0.0.0.0/0", // CIDR IP address restriction (optional, 0.0.0.0/0 means everyone)
                ServiceUtils.parseIso8601Date("2017-10-16T06:31:56.000Z")  // DateGreaterThan (optional)
                );

        String signedUrl = CloudFrontService.signUrl(
                "https://" + distributionDomain + "/" + s3ObjectKey, // Resource URL or Path
                keyPairId,     // Certificate identifier, an active trusted signer for the distribution
                derPrivateKey, // DER Private key data
                policy // Access control policy
                );
            System.out.println(signedUrl);

我收到的回复:


<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>AccessDenied</Code>
    <Message>Access denied</Message>
</Error>

代码参考:https://jets3t.s3.amazonaws.com/toolkit/code-samples.html

【讨论】:

好吧,得到了问题,问题是: String policyResourcePath = distributionDomain + "/*" ;只需将其替换为“String policyResourcePath = “http*://”+distributionDomain + “/*”;”它按预期工作。

以上是关于尝试使用 cloudfront 和 privateKey 访问存储在 S3(静态 Web 托管)中的私有内容时获得 HTTP/1.1 403 Forbidden的主要内容,如果未能解决你的问题,请参考以下文章

尝试实现 socket.io 通信时收到 GET 400。 Node.js、ELB、Route 53、CloudFront 和 s3

尝试使用 cloudfront 和 privateKey 访问存储在 S3(静态 Web 托管)中的私有内容时获得 HTTP/1.1 403 Forbidden

如何使用 Cloudfront 和基本身份验证?

在 CloudFront 中从一个源同时设置 https 和 http

使用 Cloudfront、EB 和 Route 53 时,源域名应该设置啥?

Cloudfront 访问被拒绝