6

I am using signed cookie to restrict access content in my S3 bucket via Cloudfront in my web based application

For example, a user can access content in s3://mys3/folder1 via http://mycf.example.com/folder1. He will not be able to access content in other folders at the same level.

Here is my problem:

The user uses my web app to access his content in s3://mys3/user1. He opens up another browser instance to access his peer's content in s3://mys3/buddy. The second browser will download a new set of signed cookie and overwritten the ones for user1. Now if he switch back to his first window, he will encounter a 'Access denied' problem.

What is the best practice to avoid this issue? e.g. Is it possible to specify multiple paths in a policy's url?

Anthony Kong
  • 37,791
  • 46
  • 172
  • 304

2 Answers2

8

You cant specify multiple paths in your policy. If you folder structure won't match the wildcard values you can use, you will probably need to generate signed cookies for each path and scope them accordingly.

You can specify for what path a cookie will work for. When you Set-Cookie, specify the path to /user1 for your first user and /user2 for your second user. Then your browser will only send them when the path's match.

http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-canned-policy.html#private-content-canned-policy-signature-cookies

imperalix
  • 3,701
  • 1
  • 23
  • 19
  • 2
    I think you misunderstand my question. User1 need to access both `/user1` and `/user2` **at the same time**. The path in the policy, as far as I understand, can only be either '/user1' or '/user2'. – Anthony Kong May 02 '15 at 04:50
  • This isn't an answer. The question is about setting multiple cookies to different paths for the same browser. Not setting a single cookie for two different users on different browsers which is what this "answer" seems to assume with the notion of a "second" user. – Seivan Mar 30 '20 at 10:11
  • 2
    @AnthonyKong No, he understood your question, and his answer is likely correct. If you specify different paths in the cookie, you can keep both sets of cookies at the same time, therefore allowing access to different buckets at the same time. [See this related cookie question.](https://stackoverflow.com/questions/4056306/how-to-handle-multiple-cookies-with-the-same-name) – Cerin Apr 18 '20 at 15:28
  • I misread this at first thinking it didnt solve my own use case but it does work. Ive tested and verified it using cookies with 2 paths. Able to access just those 2 paths in cloudfront and everything else is protected. The confusing thing is we need to set the same cookie name with different paths. I didnt actually know it was possible. So for every unique path you can use a wildcard and set the cookie path to the same thing minus the wildcard. – George Apr 28 '23 at 16:10
0

Adding a bit since I struggled with this and maybe this saves someone the same pain. It has to be custom cookie policy and not canned to support more than 1 directory. For each directory you set 3 cookies (CloudFront-Key-Pair-Id, CloudFront-Policy, CloudFront-Signature). For each directory you will need 3 cookies with same name and different paths (for me it was counterintuitive to set the same cookie name more than once but Ive tested and it works). Another hard part is you have to ensure cloudfront domain matches your domain or you cant pass the cookies you ser. On localhost I use host file to create a fake dns lookup for testing (map 127.0.0.1 to myapp.com and cloudfront has to use some domain like cdn.myapp.com).

<dependencyManagement>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.16.60</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-bom</artifactId>
            <version>1.12.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-cloudfront</artifactId>
</dependency>


@GetMapping("/cloudfront-cookies")
public String getCloudfrontCookies(HttpServletResponse resp) {
    
    String path1 =  "/protected/user1/";
    String path2 =  "/protected/user2/";
    
    setCookies(resp, path1, distributionDomain, "https");
    setCookies(resp, path2, distributionDomain, "https");
    return "success";
}

protected void setCookies(HttpServletResponse resp, String path, String domain, String proto) {
    Date start = new Date(System.currentTimeMillis());
    Date expiration = new Date(System.currentTimeMillis()+expirationSeconds*1000);
    InputStream is = this.getClass().getResourceAsStream(privateKeyFilePath);
    try {
        PrivateKey privateKey = RSA.privateKeyFromPKCS8(IOUtils.toByteArray(is));

        String url = proto + "://" + domain + path + "*";
        CookiesForCustomPolicy  cp =  CloudFrontCookieSigner.getCookiesForCustomPolicy(
                url,
                privateKey, 
                keyPairId,
                expiration, 
                start,
                null);
        
        Entry<String,String> kpi = cp.getKeyPairId();
        Entry<String,String> pol = cp.getPolicy();
        Entry<String,String> sig = cp.getSignature();
        
        resp.addCookie(getCookie(pol.getKey(),pol.getValue(),path,domain));
        resp.addCookie(getCookie(sig.getKey(),sig.getValue(),path,domain));
        resp.addCookie(getCookie(kpi.getKey(),kpi.getValue(),path,domain));         
        
    }catch(Exception e) {
        e.printStackTrace();
    }
    finally {
        try {is.close();} catch(IOException ignore) {}
    }

}    

protected Cookie getCookie(String key, String value, String path, String domain) {
    Cookie c = new Cookie(key,value);
    c.setPath(path); 
    return c;
}

Some properties are set via properties files and the certificate I get from filesystem. Follow the aws signed cookie documentation for the rest.

George
  • 1,021
  • 15
  • 32