I'm trying to run Electron in headless mode to fetch content on remote server which requires cookies with prefix __Host-. However, the old code used to run
var cookie = {
url: cookieurl,
name: cookiename,
value: cookievalue
};
win.webContents.session.cookies.set(cookie)
.then(function(result)
{
loadUrl(win, indexUrl, output);
})
.catch(function(e)
{
throw Error("Failed to load cookie, e="+e);
});
and this seems to work just fine as long as cookiename does not start with __Host-. When I try to set cookie with prefix __Host- I get following exception instead:
Error: Failed to parse cookie
However, this limitation is not documented at https://www.electronjs.org/docs/latest/api/cookies
This detail is not documented in the official documentation at https://www.electronjs.org/docs/latest/api/cookies but it's a logical result of other rules. Specifically the Set-Cookie HTTP header is defined to follow these rules:
<cookie-name>=<cookie-value>
...
Note: Some <cookie-name> have a
specific semantic:
__Host- prefix:
Cookies with
names starting with __Host- must be set with the secure flag, must
be from a secure page (HTTPS), must not have a domain specified (and
therefore, are not sent to subdomains), and the path must be /.
...
Attributes
...
Secure Optional
Indicates that the cookie is sent to the server only
when a request is made with the https: scheme (except on localhost),
and therefore, is more resistant to man-in-the-middle attacks.
Note: Do not assume that Secure prevents all access to sensitive
information in cookies (session keys, login details, etc.). Cookies
with this attribute can still be read/modified either with access to
the client's hard disk or from JavaScript if the HttpOnly cookie
attribute is not set.
Insecure sites (http:) cannot set cookies with the Secure attribute
(since Chrome 52 and Firefox 52). For Firefox, the https: requirements
are ignored when the Secure attribute is set by localhost (since
Firefox 75).
Specifically, you cannot set cookie with name starting with __Host- prefix without also specifying secure. As a result, setting cookie as described in the question fails. Unfortunately, the exception is just Error: Failed to parse cookie instead of Error: cannot set cookie with "__Host-" prefix without also setting "secure" attribute.
Following should work as expected:
var cookie = {
url: cookieurl,
name: cookiename,
value: cookievalue,
secure: true,
// httpOnly: true,
// sameSite: "lax",
};
win.webContents.session.cookies.set(cookie)
.then(function(result)
{
loadUrl(win, indexUrl, output);
})
.catch(function(e)
{
throw Error("Failed to load cookie, e="+e);
});
The above example also has httpOnly and sameSite attributes in comments to work as a reminder that you probably want to consider these attributes, too.
Related
I have a frontend web app made in ReactJS, and a backend made in HapiJS. Backend is running on http://localhost:3000 and frontend in http://localhost:1234.
I'm trying to implement authentication based on cookies. In my frontend, I'm doing the request for login (where I expect to get the cookies back) using Axios.
In my backend, I already have the code that set the cookies like this (I was setting a second cookie just for testing purposes):
//Config for user session cookie
server.auth.strategy('session', 'cookie', {
cookie: {
name: 'sid-example',
isSameSite: 'None',
path: '/',
password: 'password-should-be-32-characters',
isSecure: false,
isHttpOnly: false
},
...
}
//Config for a normal cookie
server.state('data', {
ttl: null,
isSecure: false,
isHttpOnly: false,
encoding: 'base64json',
clearInvalid: true,
strictHeader: true,
isSameSite: 'None',
path: '/'
});
//In my route's code where authentication takes place
//Set user session cookie
request.cookieAuth.set({ uuid });
//Set normal cookie
h.state('data', { foo: 'bar' });
When I do the request (login attempt), I can see in Chrome dev tools -> Network tab that the cookies are set and come as part of the response. In development, I'm not setting the cookies as httpOnly true nor secure, and this can be seen in the request details.
However, when I check the application cookies, I cannot see them. In the application tab in dev tools, I can see noo cookies (I already refreshed the tab).
It detects there are 2 cookies but they are not shown:
Also, if I attempt to make requests using Axios, expecting the cookie to be sent back to the server (to authorize the request), they are not sent (I'm using the flag withCredentials in Axios (as explained HERE). This can be seen in the Network tab as well (in the column cookies shows the count of cookies sent, while in Set Cookies the ones that are set either by the server or from the web app).
Also I already disabled (for testing purposes) the browser flags #same-site-by-default-cookies and #cookies-without-same-site-must-be-secure, as I'm not running it via HTTPS. Is there anything I'm missing that is preventing the browser to accept these cookies?
I had to use this setting in the backend:
SameSite = 'None'
Secure = True
(for localhost, Secure=True works, I guess)
On the client side, apart from including withCredentials: true in the subsequent requests, I had to include that in the initial login request as well, otherwise the browser doesn't consider the set-cookie headers in the response.
Recently due to Chrome 80, it has been noted that cookies without the SameSite=None and Secure attributes will not get set in Chrome browsers.
Currently, I use the Flask-JWT-Extended library to generate my cookies for my backend, but even though it has the samesite=None in the set_cookies function the cookies still do not get set in the browser.
I sent the request with Postman and viewed my cookie and got the below cookie:
access_token_cookie=my_token; Path=/; Domain=127.0.0.1; Secure; HttpOnly;
I have tried manually setting the headers with:
resp.headers.add('Set-Cookie', 'access_token_cookie=bar; SameSite=None; Secure')
But even after setting the cookie manually, I still get the following cookie with no SameSite attribute:
access_token_cookie=bar; Path=/user; Domain=127.0.0.1; Secure;
I'm wondering if there is a way to set the SameSite attribute within the cookies right now.
Edit
This is the code that I have for the site.
List item
access_token = create_access_token(identity=user.username)
resp = jsonify({"username": user.username,
"user_type": user.roles
})
resp.headers.add('Set-Cookie', 'access_token_cookie=' + access_token + '; SameSite=None; Secure')
return resp
Chrome ignores cookies marked as Secure that was received via insecure channel.
So, you can either test this via https or remove the Secure attribute
In order to do this, I use make_response without any Flask plugins:
from flask import make_response, render_template
resp = make_response(render_template("index.html"))
resp.set_cookie('pwd', pwd, samesite="Lax")
The important part is resp.set_cookie('pwd', pwd, samesite="Lax"). The samesite argument lets you set the SameSite of the cookie.
You're correct in thinking that Chrome now requires cookies marked SameSite=None to also be marked Secure:
Any cookie that requests SameSite=None but is not marked Secure will be rejected.
However, the Domain you specify for your cookie (127.0.0.1) indicates that the request's server origin is an insecure one (i.e. using the http scheme), and you should be aware that, due to a feature known as Strict Secure Cookies, attempts to set a Secure cookie from an insecure origin fail in Chrome 58+:
This adds restrictions on cookies marked with the 'Secure' attribute. Currently, Secure cookies cannot be accessed by insecure (e.g. HTTP) origins. However, insecure origins can still add Secure cookies, delete them, or indirectly evict them. This feature modifies the cookie jar so that insecure origins cannot in any way touch Secure cookies.
Therefore, if you want to set a cookie marked SameSite=None in modern Chrome, the origin needs to be secure (i.e. use the https scheme).
I can't access any cookie from JavaScript. I need to read some value and send them via JSON for my custom checks.
I've tried to access cookies from JS, like it was described at:
http://www.w3schools.com/js/js_cookies.asp
Get cookie by name
As you can see at the code, it's seen as clear as a crystal the next:
var c_value = document.cookie;
When I'm trying to access the document.cookie value from the Chrome's web-debugger, I see only the empty string at the Watch expressions:
So I can't read cookies value, which I need.
I've checked the cookie name, which I'm sending to get an associated value IS correct.
Also, I'm using the W3Schools source code for getting cookies, if you're interested (but from the 2nd link, the technique is similar).
How can I fix my issue?
You are most likely dealing with httponly cookies. httponly is a flag you can set on cookies meaning they can not be accessed by JavaScript. This is to prevent malicious scripts stealing cookies with sensitive data or even entire sessions.
So you either have to disable the httponly flag or you need to find another way to get the data to your javascript.
By looking at your code it should be easy to disable the http only flag:
Response.AddHeader("Set-Cookie", "CookieName=CookieValue; path=/;");
Response.SetCookie(new HttpCookie("session-id") { Value = Guid.NewGuid().ToString(), HttpOnly = false });
Response.SetCookie(new HttpCookie("user-name") { Value = data.Login, HttpOnly = false });
Now you should be able to access the cookie information from JavaScript. However I don't know exactly what kind of data you are trying to get so maybe you can go for another approach instead and for example render some data attribute on the page with the information you need instead of trying to read the cookie:
<div id="example" data-info="whatever data you are trying to retrieve"></div>
console.log(document.getElementById('example').getAttribute('data-info'));
keep an eye also to the cookie's Path attribute, as the cookie is only visible to subdirectories under Path. I had your issue and I solved setting Path "/"
I would say http only is your first culprit but this can also occur by not setting the scope of your cookie.
If the site has been redirected from another domain, you will need to look into setting the scope of the cookie. Domain and Path defines the scope of the cookie, which URLs the cookie should be sent to. Depending on this, you might not see the cookie in your response.
I ran across this issue when setting a cookie on a successful SAML SSO login and couldn't retrieve the cookie from the Document because it was never send as part of the request.
I had the same problem several times. And every time, it was for a different reason.
Different reasons:
problem of httpOnly field. It was set to false and I was trying to access it from the console. Setting it to true or accessing it from the source code did the trick.
problem of secure field. It was true and I was using only http.
problem of Expires / Max-Age. The cookie was outdated and it was not visible in document.cookie.
If your cookie is set as Set-Cookie or Set-Cookie2 it's not part of the response headers collection: http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders%28%29-method
Returns all headers from the response, with the exception of those whose field name is Set-Cookie or Set-Cookie2.
If you are using some secure authentication then that case you could not access cookies directly because of secure. you have to change some response attribute in server side using below code .
Response.AddHeader("Set-Cookie", "CookieName=CookieValue; path=/;");
Response.SetCookie(new HttpCookie("session-id") { Value = Guid.NewGuid().ToString(), HttpOnly = false });
Response.SetCookie(new HttpCookie("user-name") { Value = data.Login, HttpOnly = false });
But you should not because it may change secure to un-secure, so you have to find out solution that be done in server side to delete cookies and allow to you do some operations.
Its possible to do changes in server side.
Below is code i used to set cookie and redirect link at a single response but only either will work. If statusCode is 301/302 the redirection is happening but cookie is not set. If statusCode is 200 cookie is set but redirect is not working. Does anyone know how to use both in a single request ? Should i change the StatusCode some status code dont allow setting cookie ?
const response = {
statusCode: 302,
headers: {
"Access-Control-Allow-Origin" : "*",
"Access-Control-Allow-Credentials" : true,
"Set-Cookie": 'data='+data,
"Location":"http://localhost:8040/#/dashboard",
},
body: JSON.stringify({
message: data,
}),
};
callback(null, response);
I am using serverless framework of nodejs.
Here is a screenshot of the response
But in cookie noting is der
Browsers won't accept a cookie for a completely different domain than the request was sent to. So, if you're redirecting to a new domain, you can't set a cookie for that new domain.
The domain part of the cookie works only for domains that have the same root domain, but differ only in subdomain.
This is taken from RFC 6265:
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".
So, to recap. You can't set a cookie from your server for a completely different domain and the domain attribute in the cookie won't save you either. Under specialized circumstances, you can set a cookie for a different domain that is different only in sub-domain (e.g. shares the same root domain).
FYI, if your cookie every did get set appropriately, you'd have to change this:
"Set-Cookie": 'data='+data,
to this:
"Set-Cookie": 'data=' + JSON.stringify(data),
to properly serialize your object.
You're setting your cookie equal to data=[Object object] not the actual information.
You need to serialize your data object in that string.
I have tried to set a cookie using document.cookie = "tagname = test; secure" but this does not set the secure flag. Am I setting it wrong? Can you only set it from a server response? I am also wondering that, because I have had a difficult time finding an example of its use, that it probably is not commonly used?
Thanks a bunch!
TL:DR
document.cookie = "tagname = test;secure";
You have to use HTTPS to set a secure attribute
The normal (or formal, maybe) name is attribute. Since the flag refers to other things.
More Info
Cookie attributes:
Secure - Cookie will be sent in HTTPS transmission only.
HttpOnly- Don't allow scripts to access cookie. You can set both of the Secure and HttpOnly.
Domain- specify the hosts to which the cookie will be sent.
Path - create scopes, cookie will be sent only if the path matches.
Expires - indicates the maximum lifetime of the cookie.
More details and practical usages. Check Testing_for_cookies_attributes_(OTG-SESS-002)
UPDATES
The following contents expire in June 2, 2016.
Cookie Flags
Cookie flags are prefixes. At the moment, they are described in the RFC draft as a update to the RFC6265
These flags are used with the 'secure' attribute.
__Secure-
The dash is a part of the prefix. This flag tells the browser, the cookie should only be included in 'https'.
__Host-
A cookie with this flag
must not have 'domain' attribute, it will be only sent to the host which set it.
Must have a 'path' attribute, that is set to '/', because it will be sent to the host in every request from the host.
because the flag is called secure, not security:
document.cookie = "tagname = test;secure";
This cookie package is easy to use # https://www.npmjs.com/package/js-cookie
//to set cookie use
Cookies.set('name', 'value', { expires: 7, path: '' });
//to read the cookie, use
Cookies.get('name'); // => 'value'
//to delete cookie this
Cookies.remove('name')
//to set secure cookie this
Cookies.set('name', 'value', { secure: true });
Although the server responded with Upper case, and separate with space:
set-cookie: x = y; Secure
The client javascript needs to lowercase the secure and delete the whitespace after ;, like so:
document.cookie = `x=y;secure`;
Otherwise, it will be no effect.
This is an example for ExpressJs users:
Set secure cookie
res.cookie("name", "value", { secure: true });
Read this cookie
req.cookies["name"];
When the Secure attribute is set on a cookie, the browser will include it in
the request only when the request is made through HTTPS and not through
HTTP .
It's a best practice to use this attribute for sensitive cookies as it will
protect them from being sent over insecure connection.