How to use CloudFront signed cookies in the browser? - javascript

So I have a private CDN (can't access data through S3, only by signed URL & cookies) using CloudFront that holds some files for a web application.
One of the pages on my site displays an image whose source lives on the cdn.
<img src="cdn.example.com/images/file.jpg">
Now when I generate a signed URL on the server I can just pass it to the client and set it as source...Easy
But when I try to do the same thing using signed cookies I run into 2 problems:
After generating the signed cookies server-side, I set them using res.cookie but I'm not sure how to find them in response on the client side.
In the event of actually getting the cookie on the client side (in angular/javascript), how to I "set" it so that when the browser tries to grab the image in the above mentioned html it allows access?
I'm using node, express, and cookie-parser.
I'm fairly new to this stuff so any help is appreciated

Have you checked that the domain for the cookie is the same or a subdomain of the CloudFront CDN?
The cookie will be included in the request to the CloudFront distribution only if it is coming from a related domain. For example, if you are testing locally using "localhost" that won't work as the cookie domain is "localhost" but the signed cookie domain is "cdn.example.com".
To ensure that the domain is correct:
Set up a CNAME for the CloudFront Distribution e.g.
http://cdn.example.com
Make sure the signed cookie has the correct
domain e.g. domain:'*.example.com' will match against the domain.
There's a good article here:
https://www.spacevatican.org/2015/5/1/using-cloudfront-signed-cookies/
There's also a good node module for creating signed cookies:
https://github.com/jasonsims/aws-cloudfront-sign
Using this module you would do something like:
const options = {
keypairId: YOUR_ACCESS_ID,
privateKeyPath: PATH_TO_PEM_FILE
};
const signedCookies = cf.getSignedCookies("http://cdn.example.com/*", options);
for (const cookieId in signedCookies) {
res.cookie(cookieId, signedCookies[cookieId], {domain: ".example.com"});
}
Note the way I set the domain. If not explicitly set it will default to the one that made the request, which is probably not what you want.
Last thing, to test this locally, I set up port-forwarding on my home network and created a CNAME in GoDaddy to point to my home IP e.g. "test.example.com". I then used "http://test.example.com" instead of "http://localhost". This created a cookie with the subdomain "test.example.com". Finally, I created another CNAME in GoDaddy called "cdn.example.com", pointing it to the CloudFront URL. I used the wildcard domain in my signed cookie, ".example.com" and because I now have the signed cookie on the same domain "example.com" it's passed to CloudFront and boom we have liftoff!

For those trying to access a protected resource in CloudFront from a different domain you can not set the cookies cross domain so you're limited to using Signed URLs instead. However you can use javascript on the CloudFront site to use the parameters in the Signed URL to create a Signed Cookie directly in CloudFront. I put together a simple JS script that does just that.
aws-signed-cookie-from-signed-url.js
build signed url in www.mysite.com that points to CloudFront url www.cloudfronturl.com/sample/index.html
included the JS in the CloudFront resource "sample/index.html"
upon the user using the signed URL, index.html will create the SignedCookie which will allow all additional navigation within www.cloudfronturl.com/sample to work based on the cookie

Related

set cookie on parent domain but from sub-domain website

When I visit a sub-domain website ex: https://sub2.example.com, from a browser console I can set a cookie for parent domain.
document.cookie = "nameCookie=HelloWorld; domain=.example.com;"
as per Cookie RFC this works! and this cookie should be available to all sub-domains.
ex:
https://example.com
https://sub2.example.com
https://xxx.example.com
But my problem, this concept is not working on some websites.
for ex:
Go to https://square.github.io/
open browser console
document.cookie = "nameCookie=HelloWorld; domain=.github.io;"
console.log(document.cookie)
check that nameCookie is not available.
Why it is not working here? any Http header/rule setup on those websites?
Because github.io is on the list of effective top-level domains (eTLDs) (raw list here), so each github.io subdomain is treated like a subdomain of a top-level domain (that is, _______.github.io is treated just like _______.com or _______.co.uk).

Setting document.cookie for another site then redirecting [duplicate]

Say I have a website called a.com, and when a specific page of this site is loaded, say page link, I like to set a cookie for another site called b.com, then redirect the user to b.com.
I mean, on load of a.com/link I want to set a cookie for b.com and redirect user to b.com.
I tested it, and browser actually received the cookie from a.com/link, but it didn't send that cookie on the redirection request to b.com. Is it normal?
Can we set cookies for other domains?
You cannot set cookies for another domain. Allowing this would present an enormous security flaw.
You need to get b.com to set the cookie. If a.com redirect the user to b.com/setcookie.php?c=value
The setcookie script could contain the following to set the cookie and redirect to the correct page on b.com
<?php
setcookie('a', $_GET['c']);
header("Location: b.com/landingpage.php");
?>
Similar to the top answer, but instead of redirecting to the page and back again which will cause a bad user experience you can set an image on domain A.
<img src="http://www.example.com/cookie.php?val=123" style="display:none;">
And then on domain B that is example.com in cookie.php you'll have the following code:
<?php
setcookie('a', $_GET['val']);
?>
Hattip to Subin
Probaly you can use Iframe for this. Facebook probably uses this technique. You can read more on this here. Stackoverflow uses similar technique, but with HTML5 local storage, more on this on their blog
In case you have a.my-company.com and b.my-company.com instead of just a.com and b.com you can issue a cookie for .my-company.com domain - it will be accepted and sent to both of the domains.
You can't, at least not directly. That would be a nasty security risk.
While you can specify a Domain attribute, the specification says "The user agent will reject cookies unless the Domain attribute specifies a scope for the cookie that would include the origin server."
Since the origin server is a.com and that does not include b.com, it can't be set.
You would need to get b.com to set the cookie instead. You could do this via (for example) HTTP redirects to b.com and back.
Setting cookies for another domain is not possible.
If you want to pass data to another domain, you can encode this into the url.
a.com -> b.com/redirect?info=some+info (and set cookie) -> b.com/other+page
see RFC6265:
The user agent will reject cookies unless the Domain attribute
specifies a scope for the cookie that would include the origin
server. For example, the user agent will accept a cookie with a
Domain attribute of "example.com" or of "foo.example.com" from
foo.example.com, but the user agent will not accept a cookie with a
Domain attribute of "bar.example.com" or of "baz.foo.example.com".
NOTE: For security reasons, many user agents are configured to reject
Domain attributes that correspond to "public suffixes". For example,
some user agents will reject Domain attributes of "com" or "co.uk".
(See Section 5.3 for more information.)
But the above mentioned workaround with image/iframe works, though it's not recommended due to its insecurity.
You can't, but... If you own both pages then...
1) You can send the data via query params (http://siteB.com/?key=value)
2) You can create an iframe of Site B inside site A and you can send post messages from one place to the other. As Site B is the owner of site B cookies it will be able to set whatever value you need by processing the correct post message. (You should prevent other unwanted senders to send messages to you! that is up to you and the mechanism you decide to use to prevent that from happening)
Send a POST request from A. Post requests are on the serverside only and can't be accessed by the client.
You can send a POST request from a.com to b.com using CURL (recommended, serverside) or a hidden method="POST" form (clientside). If you go for the latter, you might want to obfuscate your JavaScript so that the user won't be able to understand the algorithm and interfere with it.
Make a gateway on b.com to set cookies:
<?php
if (isset($_POST['data']) {
setcookie('a', $_POST['data']);
header("Location: b.com/landingpage");
}
?>
If you want to bring security a step further, implement a function on both sides (a.com and b.com) to encrypt (on a.com) and decrypt (on b.com) data using a cryptographic cypher.
If you're trying to do something that must be absolutely secure (e.g. transfer a login session) try oAuth or take some inspiration from https://api.cloudianos.com/docs#v2/auth
Here is what I've used. Note, this cookie is passed in the open (http) and is therefore insecure. I don't use it for anything which requires security.
Site A generates a token and passes as a URL parameter to site B.
Site B takes the token and sets it as a session cookie.
You could probably add encryption/signatures to make this secure. Do your research on how to do that correctly.
In this link, we will find the solution Link.
setcookie("TestCookie", "", time() - 3600, "/~rasmus/", "b.com", 1);

Set-Cookie is not working in Chrome - with two websites

I have a problem with Set-Cookie not working in Chrome (I didn't check other browsers). It worked in the past but it stopped working recently. I have two websites with two domain names, and I need to set the cookie in both websites. I'm calling a URL in each of the domain names to set the cookie. But it doesn't set the cookie on the other website (the website I'm not browsing now).
The users login or logout or sign up to one website, and I want them to login or logout from the other website too, automatically. Currently if they login or logout to one website, it doesn't affect the other website.
The Django view code is:
#csrf_exempt
def set_session(request):
"""
Cross-domain authentication.
"""
response = HttpResponse('')
origin = request.META.get('HTTP_ORIGIN')
if isinstance(origin, bytes):
origin = origin.decode()
netloc = urlparse(origin).netloc
if isinstance(netloc, bytes):
netloc = netloc.decode()
valid_origin = any(netloc.endswith('.' + site.domain) for site in Site.objects.all().order_by("pk"))
if (not (valid_origin)):
return response
if (request.method == 'POST'):
session_key = request.POST.get('key')
SessionStore = import_module(django_settings.SESSION_ENGINE).SessionStore
if ((session_key) and (SessionStore().exists(session_key))):
# Set session cookie
request.session = SessionStore(session_key)
request.session.modified = True
else:
# Delete session cookie
request.session.flush()
response['Access-Control-Allow-Origin'] = origin
response['Access-Control-Allow-Credentials'] = 'true'
return response
And the JavaScript code is:
window.speedy = {};
window.speedy.setSession = function (domain, key) {
$.ajax({
url: '//' + domain + '/set-session/',
method: 'post',
data: {
key: key
},
xhrFields: {
withCredentials: true
}
});
};
Then there is a JavaScript code that calls this function twice:
speedy.setSession('speedy.net', 'session_key');
speedy.setSession('speedymatch.com', 'session_key');
Where 'session_key' is replaced by the session key of the user.
Is there any solution to this problem? I think this is due to recent changes in Chrome.
Update: We have a staging server where both the websites domains are subdomains of the same registered domain name. And there, Set-Cookie works fine. But in the production websites, I think the other site's cookies are blocked by Chrome because the other site's domain is different from the domain the user is currently browsing.
I checked and the cookies from the other website also don't work with Firefox and Dolphin. It might be related to the upgrade to Django 2.1 which we upgraded recently.
The same origin policy for cookies being triggered here; from a domain you can set cookies for:
own domain
parent domain (unless the parent domain is a (g)TLD)
So as the two domains in question do not share the parent-child relationship and the only common parent of them could be the TLD (assuming same TLD), you can't do this.
From MDN doc:
Cookies use a separate definition of origins. A page can set a cookie for its own domain or any parent domain, as long as the parent domain is not a public suffix. Firefox and Chrome use the Public Suffix List to determine if a domain is a public suffix. Internet Explorer uses its own internal method to determine if a domain is a public suffix. The browser will make a cookie available to the given domain including any sub-domains, no matter which protocol (HTTP/HTTPS) or port is used. When you set a cookie, you can limit its availability using the Domain, Path, Secure and Http-Only flags. When you read a cookie, you cannot see from where it was set. Even if you use only secure https connections, any cookie you see may have been set using an insecure connection.
Thanks to #aaron I found out the problem. This problem started only recently, after I upgraded Django to 2.1. Django 2.1 introduced the SESSION_COOKIE_SAMESITE setting, which must be set to None for our websites to work properly with session cookies. On the other hand, CSRF_COOKIE_SAMESITE for our websites can be set to 'Strict', since we use separate CSRF cookies for each website. Therefore, I added the following lines to our base settings:
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = None
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'Strict'
From those lines, only SESSION_COOKIE_SAMESITE = None is necessary to fix the problem I mentioned in this question. I relied on the default setting of Django 2.1 to the value of SESSION_COOKIE_SAMESITE, which was not working for us in this case.
Currently the login and logout works in Chrome on my desktop and in one mobile phone. But I checked another mobile phone I have, and there it doesn't work - the problem persists as it was before. I'm not sure if this is due to a personal settings in this mobile phone or in the Chrome app? But login and logout to both websites simultaneously doesn't work there. If I login to one website, I'm still logged out from the other website, and vice versa.
Currently the login and logout works in Chrome. The problem was cookies settings - check your settings at chrome://settings/cookies (desktop) or settings > site settings > cookies (mobile).
(August 2020) Update: It is now required to use the following settings for Chrome. Please see this question and answer.
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = 'None'

uploading images - do they have domain name in them - chrome not sending session id

For testing I downloaded images from the net and uploaded using valum file upload in chrome...chrome is not sending session cookie along with these request header( I dont see that in the server side/though I see it on developer tool)...does chrome know that these images are from different domain . what is happening...Is there work around for this to pass the session id (as cookie). It is also happening in IE10 which makes me belive it is some standard. and not just a chrome issue. This problem is not there with firefox/safari/opera
It is fine when uploading to localhost. only when uploading to different server with domain name there is this problem leading to creating a new session for this.
Update:
I have added xhr.withCredentials = true still no use.
Also added on the server side to the upload url...
res.setHeader 'Access-Control-Allow-Origin', '*'
res.setHeader 'Access-Control-Allow-Credentials', true
I dont know how helpful this would be, because I would have already sent the upload file and response header will not of much help.
basically the problem is I don't have access to the session variable at the server side, since the session id/sid cookie is not coming back /I am not able to save some of this upload details into the current session(because this is a new session) .
Update:
I tried creating an image in teh desktop using paint..even then chrome would not sent the cookies. Really drives me crazy...
First of all, to get the basics out of the way, this is unrelated to the origin of the image. Chrome or other browsers don't care where you get your images.
It's rather difficult to guess exactly what's going on, would have helped to see a jsfiddle or some more setup explanation, but based on what I'm guessing, you might be using different domains for the page where the upload button is hosted and the target url where you're sending your files (even using ssl for one and http for the other makes it different). Even different subdomains will not allow cookies to be passed if the cookies were not set with a base domain (yourdomain.com)
So, if sub-domains are the problem, you know what to do - set a base domain so you get your cookies to go on any sub domain.
If it's http vs. https you need to always use https (or http) because you can't switch cookies between those two.
If that's not it, or if you're using completely different domains, you can access your cookies locally via script (if they're not marked as http only) and add them to the upload request. Valum 2.0 (don't know about v1.0) lets you add parameters to the request like so:
var uploader = new qq.FileUploader({
element: document.getElementById('file-uploader'),
action: '/server-side.upload',
// additional data to send, name-value pairs
params: {
param1: 'value1',
param2: 'value2'
}
});
You can't set cookies on a domain which is not the page's domain via script so for using completely different domains your only choice is using request params.
It is possible that the uploader is using Flash under some circumstances to do the upload. There is a bug in Flash which prevents cookies being sent for these types of requests. This would explain the behaviour you are seeing. The workaround is to pass in the sessionId and transmit it in a different way eg. querystring.

How to set a cookie for another domain

Say I have a website called a.com, and when a specific page of this site is loaded, say page link, I like to set a cookie for another site called b.com, then redirect the user to b.com.
I mean, on load of a.com/link I want to set a cookie for b.com and redirect user to b.com.
I tested it, and browser actually received the cookie from a.com/link, but it didn't send that cookie on the redirection request to b.com. Is it normal?
Can we set cookies for other domains?
You cannot set cookies for another domain. Allowing this would present an enormous security flaw.
You need to get b.com to set the cookie. If a.com redirect the user to b.com/setcookie.php?c=value
The setcookie script could contain the following to set the cookie and redirect to the correct page on b.com
<?php
setcookie('a', $_GET['c']);
header("Location: b.com/landingpage.php");
?>
Similar to the top answer, but instead of redirecting to the page and back again which will cause a bad user experience you can set an image on domain A.
<img src="http://www.example.com/cookie.php?val=123" style="display:none;">
And then on domain B that is example.com in cookie.php you'll have the following code:
<?php
setcookie('a', $_GET['val']);
?>
Hattip to Subin
Probaly you can use Iframe for this. Facebook probably uses this technique. You can read more on this here. Stackoverflow uses similar technique, but with HTML5 local storage, more on this on their blog
In case you have a.my-company.com and b.my-company.com instead of just a.com and b.com you can issue a cookie for .my-company.com domain - it will be accepted and sent to both of the domains.
You can't, at least not directly. That would be a nasty security risk.
While you can specify a Domain attribute, the specification says "The user agent will reject cookies unless the Domain attribute specifies a scope for the cookie that would include the origin server."
Since the origin server is a.com and that does not include b.com, it can't be set.
You would need to get b.com to set the cookie instead. You could do this via (for example) HTTP redirects to b.com and back.
Setting cookies for another domain is not possible.
If you want to pass data to another domain, you can encode this into the url.
a.com -> b.com/redirect?info=some+info (and set cookie) -> b.com/other+page
see RFC6265:
The user agent will reject cookies unless the Domain attribute
specifies a scope for the cookie that would include the origin
server. For example, the user agent will accept a cookie with a
Domain attribute of "example.com" or of "foo.example.com" from
foo.example.com, but the user agent will not accept a cookie with a
Domain attribute of "bar.example.com" or of "baz.foo.example.com".
NOTE: For security reasons, many user agents are configured to reject
Domain attributes that correspond to "public suffixes". For example,
some user agents will reject Domain attributes of "com" or "co.uk".
(See Section 5.3 for more information.)
But the above mentioned workaround with image/iframe works, though it's not recommended due to its insecurity.
You can't, but... If you own both pages then...
1) You can send the data via query params (http://siteB.com/?key=value)
2) You can create an iframe of Site B inside site A and you can send post messages from one place to the other. As Site B is the owner of site B cookies it will be able to set whatever value you need by processing the correct post message. (You should prevent other unwanted senders to send messages to you! that is up to you and the mechanism you decide to use to prevent that from happening)
Send a POST request from A. Post requests are on the serverside only and can't be accessed by the client.
You can send a POST request from a.com to b.com using CURL (recommended, serverside) or a hidden method="POST" form (clientside). If you go for the latter, you might want to obfuscate your JavaScript so that the user won't be able to understand the algorithm and interfere with it.
Make a gateway on b.com to set cookies:
<?php
if (isset($_POST['data']) {
setcookie('a', $_POST['data']);
header("Location: b.com/landingpage");
}
?>
If you want to bring security a step further, implement a function on both sides (a.com and b.com) to encrypt (on a.com) and decrypt (on b.com) data using a cryptographic cypher.
If you're trying to do something that must be absolutely secure (e.g. transfer a login session) try oAuth or take some inspiration from https://api.cloudianos.com/docs#v2/auth
Here is what I've used. Note, this cookie is passed in the open (http) and is therefore insecure. I don't use it for anything which requires security.
Site A generates a token and passes as a URL parameter to site B.
Site B takes the token and sets it as a session cookie.
You could probably add encryption/signatures to make this secure. Do your research on how to do that correctly.
In this link, we will find the solution Link.
setcookie("TestCookie", "", time() - 3600, "/~rasmus/", "b.com", 1);

Categories

Resources