Day 44: Finally Getting Comfortable

Continued my web security studies. I did some review before moving on with the rest of the PassportJS playlist. It has taken a few days but I am finally moving away from the exposure phase and more towards the comprehension phase of learning. I just have to keep going and eventually this will just be like everything I have learned before.

Not much else to report. A lot of refining with the express-session package notes. The documentation for the package is actually pretty messy. For a package that has over a million weekly downloads it is pretty crazy. I spent a lot of time adding overviews to each section of the API so that I can see all the options at a glance and click a link to the actual details that are further down on the page. The connect-mongo package’s docs were much more organized which I appreciated.

TLDR;

Okay, so here are the highlights of what I did:

  • Backend -> Continued studying the concepts mentioned in the web security section. Reviewed my notes so far on Authentication, Authorization, HTTP cookies, Sessions, and the related Node.js packages.

Rough Notes – express-session Package

I tried to provide some overviews on all the properties and methods available with the package. I also reorganized it to present examples first. Maybe it is just my preference but I prefer seeing code snippets in how some code is used before seeing an explanation of it’s use. I like how Mozilla Docs does it.

Usage

Installation

This is a Node.js module available through the npm registry. Installation is done using the npm install command:

$ npm install express-session

Examples

View Counter

A simple example using express-session to store page views for a user.

var express = require("express");
var parseurl = require("parseurl");
var session = require("express-session");

var app = express();

app.use(
  session({
    secret: "keyboard cat",
    resave: false,
    saveUninitialized: true,
  })
);

app.use(function (req, res, next) {
  if (!req.session.views) {
    req.session.views = {};
  }

  // get the url pathname
  var pathname = parseurl(req).pathname;

  // count the views
  req.session.views[pathname] = (req.session.views[pathname] || 0) + 1;

  next();
});

app.get("/foo", function (req, res, next) {
  res.send("you viewed this page " + req.session.views["/foo"] + " times");
});

app.get("/bar", function (req, res, next) {
  res.send("you viewed this page " + req.session.views["/bar"] + " times");
});

app.listen(3000);

User login

A simple example using express-session to keep a user log in session.

var escapeHtml = require("escape-html");
var express = require("express");
var session = require("express-session");

var app = express();

app.use(
  session({
    secret: "keyboard cat",
    resave: false,
    saveUninitialized: true,
  })
);

// middleware to test if authenticated
function isAuthenticated(req, res, next) {
  if (req.session.user) next();
  else next("route");
}

app.get("/", isAuthenticated, function (req, res) {
  // this is only called when there is an authentication user due to isAuthenticated
  res.send(
    "hello, " +
      escapeHtml(req.session.user) +
      "!" +
      ' <a href="/logout">Logout</a>'
  );
});

app.get("/", function (req, res) {
  res.send(
    '<form action="/login" method="post">' +
      'Username: <input name="user"><br>' +
      'Password: <input name="pass" type="password"><br>' +
      '<input type="submit" text="Login"></form>'
  );
});

app.post(
  "/login",
  express.urlencoded({ extended: false }),
  function (req, res) {
    // login logic to validate req.body.user and req.body.pass
    // would be implemented here. for this example any combo works

    // regenerate the session, which is good practice to help
    // guard against forms of session fixation
    req.session.regenerate(function (err) {
      if (err) next(err);

      // store user information in session, typically a user id
      req.session.user = req.body.user;

      // save the session before redirection to ensure page
      // load does not happen before session is saved
      req.session.save(function (err) {
        if (err) return next(err);
        res.redirect("/");
      });
    });
  }
);

app.get("/logout", function (req, res, next) {
  // logout logic

  // clear the user from the session object and save.
  // this will ensure that re-using the old session id
  // does not have a logged in user
  req.session.user = null;
  req.session.save(function (err) {
    if (err) next(err);

    // regenerate the session, which is good practice to help
    // guard against forms of session fixation
    req.session.regenerate(function (err) {
      if (err) next(err);
      res.redirect("/");
    });
  });
});

app.listen(3000);

Syntax / API

Example:

const express = require("express");
const session = require("express-session");

const app = express();

app.set("trust proxy", 1); // trust first proxy
app.use(
  session({
    secret: "keyboard cat",
    resave: false,
    saveUninitialized: true,
    cookie: { secure: true },
  })
);

session(options)

The session function takes one parameter which is an options object that is used to construct a session middleware. The options object has the following properties:

  • cookie
    • cookie.domain
    • cookie.expires
    • cookie.httpOnly
    • cookie.maxAge
    • cookie.path
    • cookie.sameSite
    • cookie.secure
  • genid
  • name
  • proxy
  • resave
  • rolling
  • saveUninitialized
  • secret (REQUIRED)
  • store
  • unset

Note Session data is not saved in the cookie itself, just the session ID. Session data is stored server-side.

Note: Since version 1.5.0, the cookie-parser middleware no longer needs to be used for this module to work. This module now directly reads and writes cookies on req/res. Using cookie-parser may result in issues if the secret is not the same between this module and cookie-parser.

Warning: The default server-side session storage (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. That is why we have to use an external database to store the session data in a production environment. There is a long list of compatible Session store packages below. This list contains a lot of packages that span across the majority if not all of the popular databases used today.

options.cookie

Settings object for the session ID cookie. The default value is:

{
  path: '/',
  httpOnly: true,
  secure: false,
  maxAge: null
}

req Added Properties

Once the session middleware is set in our express app from app.use(session(options)) we can access the session data on all incoming requests from within our callbacks using the http.ClientRequest object (Commonly named req). The req object has the session property added through the express-session middleware call. The req.session object has custom properties that we can add as well as default properties like:

  • req.session.regenerate(callback)
  • req.session.destroy(callback)
  • req.session.reload(callback)
  • req.session.save(callback)
  • req.session.touch()
  • req.session.id
  • req.session.cookie
    • req.session.cookie.maxAge
    • req.session.cookie.originalMaxAge
    • req.session.cookie.expires
  • req.sessionID

Session Store Implementation

Every session store must be an EventEmitter and implement specific methods. The following methods are the list of requiredrecommended, and optional.

  • Required methods are ones that this module will always call on the store.
  • Recommended methods are ones that this module will call on the store if available.
  • Optional methods are ones this module does not call at all, but helps present uniform stores to users.

For an example implementation view the connect-redis repo:

  • store.all(callback) = Optional
  • store.destroy(sid, callback) = Required
  • store.clear(callback) = Optional
  • store.length(callback) = Optional
  • store.get(sid, callback) = Required
  • store.set(sid, session, callback) = Required
  • store.touch(sid, session, callback) = Recommended

Compatible Session Stores

The following modules implement a session store that is compatible with this module.

  • aerospike-session-store A session store using Aerospike.
  • better-sqlite3-session-store A session store based on better-sqlite3.
  • cassandra-store An Apache Cassandra-based session store.
  • cluster-store A wrapper for using in-process / embedded stores – such as SQLite (via knex), leveldb, files, or memory – with node cluster (desirable for Raspberry Pi 2 and other multi-core embedded devices).
  • connect-arango An ArangoDB-based session store.
  • connect-azuretables An Azure Table Storage-based session store.
  • connect-cloudant-store An IBM Cloudant-based session store.
  • connect-couchbase A couchbase-based session store.
  • connect-datacache An IBM Bluemix Data Cache-based session store.
  • @google-cloud/connect-datastore A Google Cloud Datastore-based session store.
  • connect-db2 An IBM DB2-based session store built using ibm_db module.
  • connect-dynamodb A DynamoDB-based session store.
  • @google-cloud/connect-firestore A Google Cloud Firestore-based session store.
  • connect-hazelcast Hazelcast session store for Connect and Express.
  • connect-loki A Loki.js-based session store.
  • connect-lowdb A lowdb-based session store.
  • connect-memcached A memcached-based session store.
  • connect-memjs A memcached-based session store using memjs as the memcached client.
  • connect-ml A MarkLogic Server-based session store.
  • connect-monetdb A MonetDB-based session store.
  • connect-mongo A MongoDB-based session store.
  • connect-mongodb-session Lightweight MongoDB-based session store built and maintained by MongoDB.
  • connect-mssql-v2 A Microsoft SQL Server-based session store based on connect-mssql.
  • connect-neo4j A Neo4j-based session store.
  • connect-pg-simple A PostgreSQL-based session store.
  • connect-redis A Redis-based session store.
  • connect-session-firebase A session store based on the Firebase Realtime Database
  • connect-session-knex A session store using Knex.js, which is a SQL query builder for PostgreSQL, MySQL, MariaDB, SQLite3, and Oracle.
  • connect-session-sequelize A session store using Sequelize.js, which is a Node.js / io.js ORM for PostgreSQL, MySQL, SQLite and MSSQL.
  • connect-sqlite3 A SQLite3 session store modeled after the TJ’s connect-redis store.
  • connect-typeorm A TypeORM-based session store.
  • couchdb-expression A CouchDB-based session store.
  • dynamodb-store A DynamoDB-based session store.
  • express-etcd An etcd based session store.
  • express-mysql-session A session store using native MySQL via the node-mysql module.
  • express-nedb-session A NeDB-based session store.
  • express-oracle-session A session store using native oracle via the node-oracledb module.
  • express-session-cache-manager A store that implements cache-manager, which supports a variety of storage types.
  • express-session-etcd3 An etcd3 based session store.
  • express-session-level A LevelDB based session store.
  • express-session-rsdb Session store based on Rocket-Store: A very simple, super fast and yet powerfull, flat file database.
  • express-sessions A session store supporting both MongoDB and Redis.
  • firestore-store A Firestore-based session store.
  • fortune-session A Fortune.js based session store. Supports all backends supported by Fortune (MongoDB, Redis, Postgres, NeDB).
  • hazelcast-store A Hazelcast-based session store built on the Hazelcast Node Client.
  • level-session-store A LevelDB-based session store.
  • lowdb-session-store A lowdb-based session store.
  • medea-session-store A Medea-based session store.
  • memorystore A memory session store made for production.
  • mssql-session-store A SQL Server-based session store.
  • nedb-session-store An alternate NeDB-based (either in-memory or file-persisted) session store.
  • @quixo3/prisma-session-store A session store for the Prisma Framework.
  • restsession Store sessions utilizing a RESTful API
  • sequelstore-connect A session store using Sequelize.js.
  • session-file-store A file system-based session store.
  • session-pouchdb-store Session store for PouchDB / CouchDB. Accepts embedded, custom, or remote PouchDB instance and realtime synchronization.
  • session-rethinkdb A RethinkDB-based session store.
  • @databunker/session-store A Databunker-based encrypted session store.
  • sessionstore A session store that works with various databases.
  • tch-nedb-session A file system session store based on NeDB.

Goal For Round 8 of the #100DaysofCode Challenge

This is my eighth round of the “#100daysofcode” challenge. I will be continuing my work from round five, six, and seven into round eight. I was working through the book “Cracking the Coding Interview” by Gayle Laakmann McDowell. My goal was to become more familiar with algorithms and data structures. This goal was derived from my goal to better understand operating systems and key programs that I use in the terminal regularly e.g. Git. This goal was in turn derived from my desire to better understand the fundamental tools used for coding outside of popular GUIs. This in turn was derived from my desire to be a better back-end developer.

I am currently putting a pause on the algorithm work to build some backend/full stack projects. I primarily want to improve my skills with the back-end from an implementation perspective. I have improved tremendously in terminal and CLI skills but I lost focus due to how abstract the algorithm concepts got. I wanted to work on things that were more tangible until I can get to a position where I could directly benefit from improving my algorithm skills and theoretical knowledge. So that’s the focus right now. Build my backend skills and prove my full stack capabilities by building some dope projects.

Again, I still have no idea if my path is correct but I am walking down this road anyways. Worst case scenario I learn a whole bunch of stuff that will help me out on my own personal projects. Best case scenario I actually become one of those unicorn developers that go on to start a billion dollar company… You never know LOL.