HTTP is a stateless protocol.
That means HTTP requests don't know anything about each other. However, there are situations where we want to maintain state across HTTP requests. What are some of those situations? →
OK, fine - HTTP is stateless, but maintaining state can be useful.
So… how might we maintain state or share data between requests? →
Welp! That sounds terribly insecure. Why should that make you wince just a little bit. →
This means that:
A browser has to keep a session id, and send it over to the server on every request in order to maintain state. What are some potential mechanisms for doing this? →
Check out the Open Web Application Security Project for more details
Here's how state is maintained between requests using cookies. →
First… check out the documentation on cookies:
Cookies are stored by your browser, but how do they get there? →
Set-Cookie
(multiple Set-Cookie
's in a single http response are allowed)Set-Cookie
header values can specify (separated by ;
's):
name=value
pairCookie
header
An http response that sets a few cookie values. →
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: foo=bar
Set-Cookie: baz=qux
Set-Cookie: session_id=5d6d473c-a370-44c8-bff0-4f28efd7c92a; HttpOnly; Secure
An http request that sends back cookies. →
GET / HTTP/1.1
Cookie: foo=bar;baz=qux
Note that:
Set-Cookie
headers in one responseCookie
header in the request
We'll discuss HttpOnly
and Secure
in a moment
Cookies will be deleted when the client is shut down (for example, when you quit your browser). These are session cookies.
For cookies that last longer (or shorter!), you can create permanent cookies that expire at a certain date or after a certain length of time.
You can do this by adding options to the Set-Cookie
header →
Set-Cookie: foo=bar; Expires=Thu, 29 Oct 2016 07:28:00 GMT;
Set-Cookie: foo=bar; Max-Age=300;
Note that a server cannot force the browser to delete cookies! … but it can set an expiration date which will hopefully convince the client to eventually delete them.
In addition to name value pairs of arbitrary data, there are also options that can be set through the Set-Cookie
header (also separated by semicolons):
Domain
- cookies sent will only be valid for this domain (default is current domain)Path
- cookies sent will only be valid for this path (default is all paths)HttpOnly
- only allow reading of cookies via http, don't allow JavaScript!!!! …why do this? → 3rd party JavaScript included on your page is allowed to read cookies for that domain!?Secure
- cookies will only be sent if the request is encrypted (using SSL/HTTPS)
Out of these, definitely use HttpOnly
and Secure
… though for most of class, we'll be omitting Secure
until we get to SSL/HTTPS.
Ok, so back to this idea of sessions. Sessions allow you to: →
You can create your own session management by creating custom middleware that: →
Cookie
headerCookie
header that contains a session id, it'll generate a session id (using the crypto
module)
Set-Cookie
header with that idCookie
header with a session id, it'll:
You can try writing some session management middleware by using →
req.get
- to retrieve the Cookie
headercrypto
module to create a session idres.set
or res.append
- to set the Set-Cookie
header
Note that… res.set
doesn't allow you to set multiple Set-Cookie
headers (it only leaves the last cookie set). So res.append allows you to add values to an already set response field.
Of course, as you might expect, session middleware already exists… but we'll try a little bit of the above to see what's going on.
Stock Chrome always you to view cookies as well as delete them: →
Use res.set
or res.append
to set the Set-Cookie
header →
app.get('/make-a-cookie', function(req, res) {
// used append so that we can Set-Cookie more than once
res.append('Set-Cookie', 'MY_SESSION_ID=123; HttpOnly');
res.append('Set-Cookie', 'color=#00ff00');
res.send('made you a cookie');
});
Watch all following requests send those cookies back via the Cookie
header.!
Here's an example of displaying cookies using client side JavaScript, using document.cookie
. →
app.get('/peek', function(req, res) {
// uncomment this:
// const s = "alert(document.cookie);";
res.send('' + 'check out yr cookies!');
});
If you used the example in the previous slide, the HttpOnly
cookie, MY_SESSION_ID
, should not appear in the alert
.
The previous slides sketch out a way of implementing manual cookie management.
For "production", instead of using a custom solution , you can use the express-session
module! →
req.session.someData
How might you use this?
req.session.count += 1
req.session.favoriteColor = req.body.favoriteColor
Use npm to install as usual: npm install express-session
Boilerplate setup.
const express = require('express');
Include the express-session module…
const session = require('express-session');
const app = express();
app.set('view engine', 'hbs');
app.use(express.urlencoded({ extended: false }));
Set up some session options (the secret should really be externalized and not in version control, but we'll keep it here for convenience).
const sessionOptions = {
secret: 'secret for signing session id',
saveUninitialized: false,
resave: false
};
app.use(session(sessionOptions));
Check out the docs for details on all of the options. The ones that we set explicitly are:
Some others interesting ones that we don't explicitly set:
By default, express-session
uses an in memory session store, MemoryStore
→
MemoryStore
, is purposely not designed for a production environment. It will leak memory under most conditions, does not scale past a single process, and is meant for debugging and developing."So, for our purposes, it's ok to use the in-memory store, but any production application should use another compatible session store, such as memcache, mongodb, etc.
Once you have all of the setup finished, you'll have a new property available on your request object. It will allow you to use: →
req.session
- an object that you can read and write session data toreq.session.id
and req.sessionID
- the id for the session (both are the same and both are read only)Let's create a simple-form that: →
Our usual routes, but note the use of req.session.
app.get('/', function(req, res) {
const name = req.session.myName || '';
res.render('index', {'myName':name});
});
app.post('/', function(req, res) {
console.log(req.body);
req.session.myName = req.body.firstName;
res.redirect('/');
});
app.listen(3000);
myName: {{myName}}
What do you think will happen? →
Session data will be unique to each browser session (so you can have foo for one name and bar for another name if you're using two different browsers)
What do you think will happen if we request the page with curl? Will the name be there? →
curl localhost:3000
Nooope… no info to identify the session, so name isn't there.
We can actually use curl to send cookies by using the --cookie
flag. Let's copy over the cookie data…
curl localhost:3000 -v --cookie "connect.sid=..."
What will happen if we restart the server? Will the session data still be present? →
We're using an in-memory session store, so, the session data will not be persisted.