Nodejs secure cook and HTTPOnly behind proxy server - node.js

In Nodejs with secure cookie and HTTPOnly behind a proxy server.
How does HttpOnly flag and Cookie with Secure flag send it headers to proxy server?
I have been reading and assume I need to enable X-Forward-Proto on my proxy server?
process.env.NODE_ENV = 'production';
if (app.get('env') === 'production') {
app.set('trust proxy', 1) // trust first proxy
}
app.use(session({
store: new RedisStore({host: '127.0.0.1', port: 6379, client: client, ttl: 3600}),
key: 'sid',
secret: 'abcde',
resave: false,
saveUninitialized: false,
// proxy: true,
cookie: {
secure: true,
httpOnly: true,
maxAge: 3600000
}
}));

I had a similar problem and solved it with the help of this tutorial.
You also need to set the proxy option to true in your session config.
I would suggest doing this with an environment variable expression process.env.NODE_ENV === "production".
app.use(session({
store: new RedisStore({host: '127.0.0.1', port: 6379, client: client, ttl: 3600}),
key: 'sid',
secret: 'abcde',
resave: false,
saveUninitialized: false,
proxy: process.env.NODE_ENV === "production",
cookie: {
secure: process.env.NODE_ENV === "production",
httpOnly: true,
maxAge: 3600000
}
}));
From the express-session docs:
proxy
Trust the reverse proxy when setting secure cookies (via the "X-Forwarded-Proto" header).
The default value is undefined.
true The "X-Forwarded-Proto" header will be used.

The HttpOnly flag is something that is only considered by a HTTP client. A HTTP proxy will just pass on the flag and ignore it.
As long as the client itself connects to the server (or the proxy, if it's a reverse proxy) with HTTPS, it will work.

Related

Express doesn't set-cookie when SameSite is none

I have read a lot of other similar questions, but I couldn't solve the issue.
My setup is Node + Express + PassportJs and everything works in development, but I have problems on production.
With the following code, I see that the session cookie is sent back in the response, but I also get a message saying that it won't be applied as SameSite is lax (the default) and the response comes from another site (frontend and backend do not have the same origin).
app.use(
session({
secret: "foo",
resave: false,
saveUninitialized: false,
store: MongoStore.create({ mongoUrl: process.env.MONGO_DB_CONN_STRING! }),
cookie: { httpOnly: true }
})
);
So I changed it to this, so to specify SameSite and Secure in production, but at this point, no cookie is set anymore!
app.use(
session({
secret: "foo",
resave: false,
saveUninitialized: false,
store: MongoStore.create({ mongoUrl: process.env.MONGO_DB_CONN_STRING! }),
cookie: isProduction ? { httpOnly: true, sameSite: "none", secure: true } : {} // <-- only change
})
);
What could be the cause? I've tried to fix it by playing with CORS (no success) and other 100 things. Yet it seems some quirk I am missing.
depending on what service you use to deploy your API(netlify, render.com, heroku other...) you have to enable proxy
this.app.enable('trust proxy');
it fixed my issue

Why can't I set cookies over HTTPS?

I have a server that has its own domain and is using HTTPS. I have a website that has its own domain and is using HTTPS as well.
On the home page of the website, there is a login button and a sign up button. Both buttons lead to forms to execute their respective tasks, and both forms send requests to the server that respond with cookies, or at least that's what they are supposed to do.
I am using express-session in combination with Redis to store the session ids. Here is the config for that (connectToRedis is simply a function that returns a RedisClient):
const RedisStore = connectRedis(session);
const redisClient = await connectToRedis();
app.use(
session({
store: new RedisStore({
client: redisClient,
}),
cookie: {
httpOnly: true,
secure: true,
sameSite: "lax",
maxAge: TEN_YEARS,
},
resave: false,
saveUninitialized: false,
secret: SECRET,
name: AUTH_COOKIE,
})
);
For some reason the cookies are not being sent in the requests. The Set-Cookie header isn't even showing up! I tried changing SameSite to none (article), but that didn't work.
This is my first time deploying a production website, so all this HTTPS stuff is kind of new to me.
Thanks for your help, and if you need any extra information, please let me know.
IMPORTANT UPDATE:
Finally, I have made some progress (all it took was a pizza break).
I added the path key to my cookie config and gave it the value of /. I also set proxy to true.
So now it looks like this:
const RedisStore = connectRedis(session);
const redisClient = await connectToRedis();
app.use(
session({
store: new RedisStore({
client: redisClient,
}),
cookie: {
httpOnly: true,
secure: true,
sameSite: "none",
maxAge: TEN_YEARS,
path: "/",
},
resave: false,
saveUninitialized: false,
secret: SECRET,
name: AUTH_COOKIE,
proxy: true,
})
);
With this, the cookie is finally appearing in the requests, but it isn't being set in the browser...

Read express session cookie from React js app

I'm building a project with authentication. I'm using Node+React. I set an express session cookie on the back-end and I want a component in react to read that cookie to see if the user is authenticated or not. For some reason I can not access that cookie from the react(client-side)... Maybe someone could help out?
BACK:
app.use(session({
name: process.env.SESS_NAME,
resave: false,
saveUninitialized: false,
secret: process.env.SESS_SECRET,
cookie: {
maxAge: parseInt(process.env.SESS_LIFETIME),
sameSite: true, //strict,
secure: process.env.NODE_ENV === "production"
}
}))
FRONT:
import Cookies from "js-cookie";
...
console.log("cookie", Cookies.get("sid"));
I have a cookie named "sid" in this case and I can see it in my console in the browser... but when I try to access it its undefiend
thanks!
Your issue is that you have not set the httpOnly property on the cookie when configuring session. The default value is true which will prevent client browsers from reading the cookie.
Note be careful when setting this to true, as compliant clients will not allow client-side JavaScript to see the cookie in document.cookie.
app.use(session({
name: process.env.SESS_NAME,
resave: false,
saveUninitialized: false,
secret: process.env.SESS_SECRET,
cookie: {
maxAge: parseInt(process.env.SESS_LIFETIME),
sameSite: false, // this may need to be false is you are accessing from another React app
httpOnly: false, // this must be false if you want to access the cookie
secure: process.env.NODE_ENV === "production"
}
}))
See the cookie options in docs

How to set cookie from express js

I am using express.js as my api faremwork.trying to set browser cookie from expressjs with response.cookie but it is not saving any cookie on my browser.
No Error in console
Already tried httpOnly false/true,secure false/true
res.cookie('nodeSessID', req.sessionID, {maxAge: 24*60*60*1000,httpOnly:false,secure:false,domain:'cz-tuts.com',path:'/'}).json({status:1,message:"success"});
app.use(cookieParser());
app.use('/*',session({
secret: 'ThisisTestSecretForCookie',
// create new redis store.
name: 'nodeSessID',
store: new redisStore({host: 'localhost', port: 6379, client: redisClient,ttl : 260}),
saveUninitialized: false,
resave: true,
cookie: { secure: false,domain:'cz-tuts.com',httpOnly:false,path: '/',maxAge:360000 },
}));
cookie should saved in browser

How to configure application to be available subdomain cookie?

I need to share session cookie between main domain and all subdomains. I have two nodejs services based on expressjs framework:
// example.local
...
app.use(session({
cookie: {
domain: "example.local"
}
, key: 'sid'
, secret: '[my secret]'
, saveUninitialized: true
, resave: true
, store: new RedisStore({
host: 'localhost',
port: 6379
})
}));
// blog.example.local
...
app.use(session({
// what should I write here? <---------
}));
So my question is what should I write in session configuration of blog.example.local to get access to existing cookie of example.local?
EDIT: as #yeiniel suggest, I should just use the same config for blog.example.local like the following:
// blog.example.local
...
app.use(session({
cookie: {
domain: "example.local"
}
, key: 'sid'
, secret: '[my secret]'
, saveUninitialized: true
, resave: true
, store: new RedisStore({
host: 'localhost',
port: 6379
})
}));
Is it enough or I may optimize it?
Basically you need two things: Use the same settings on all servers (not just cookie settings but all the session settings included the store) and ensure cookie domain configuration point to the common domain between the sites.
i think your cookie attribute in middleware should be like this,
cookie: {
domain: ".example.local",
path:'/'
}
for blog.example.local and
cookie: {
domain: "example.local",
path:'/'
}
for example.local
Hope this work you.
I am currently managing a similar setup
All apps have the same settings for session
app.use(session({
store: redisStore,
secret: config.secret,
resave: true,
rolling: true,
saveUninitialized: false,
name: config.cookie_name,
cookie: {
domain: config.cookie_domain_name, \\ .website.tld
secure: false
}
You will not be able to use localhost to keep your session data, specially if apps are on different servers. YOu will need a central storage for session data, which all apps can access.

Resources