Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
client-sessions
Advanced tools
client-sessions is connect middleware that implements sessions in encrypted tamper-free cookies. For a complete introduction to encrypted client side sessions, refer to Francois Marier's blog post on the subject;
NOTE: It is not recommended using both this middleware and connect's built-in session middleware.
npm install client-sessions
Basic usage:
var sessions = require("client-sessions");
app.use(sessions({
cookieName: 'mySession', // cookie name dictates the key name added to the request object
secret: 'blargadeeblargblarg', // should be a large unguessable string
duration: 24 * 60 * 60 * 1000, // how long the session will stay valid in ms
activeDuration: 1000 * 60 * 5 // if expiresIn < activeDuration, the session will be extended by activeDuration milliseconds
}));
app.use(function(req, res, next) {
if (req.mySession.seenyou) {
res.setHeader('X-Seen-You', 'true');
} else {
// setting a property will automatically cause a Set-Cookie response
// to be sent
req.mySession.seenyou = true;
res.setHeader('X-Seen-You', 'false');
}
});
You can control more specific cookie behavior during setup:
app.use(sessions({
cookieName: 'mySession', // cookie name dictates the key name added to the request object
secret: 'blargadeeblargblarg', // should be a large unguessable string
duration: 24 * 60 * 60 * 1000, // how long the session will stay valid in ms
cookie: {
path: '/api', // cookie will only be sent to requests under '/api'
maxAge: 60000, // duration of the cookie in milliseconds, defaults to duration above
ephemeral: false, // when true, cookie expires when the browser closes
httpOnly: true, // when true, cookie is not accessible from javascript
secure: false // when true, cookie will only be sent over SSL. use key 'secureProxy' instead if you handle SSL not in your node process
}
}));
You can have multiple cookies:
// a 1 week session
app.use(sessions({
cookieName: 'shopping_cart',
secret: 'first secret',
duration: 7 * 24 * 60 * 60 * 1000
}));
// a 2 hour encrypted session
app.use(sessions({
cookieName: 'authenticated',
secret: 'first secret',
duration: 2 * 60 * 60 * 1000
}));
In this example, there's a 2 hour authentication session, but shopping carts persist for a week.
Finally, you can use requestKey to force the name where information can be accessed on the request object.
var sessions = require("client-sessions");
app.use(sessions({
cookieName: 'mySession',
requestKey: 'forcedSessionKey', // requestKey overrides cookieName for the key name added to the request object.
secret: 'blargadeeblargblarg', // should be a large unguessable string or Buffer
duration: 24 * 60 * 60 * 1000, // how long the session will stay valid in ms
}));
app.use(function(req, res, next) {
// requestKey forces the session information to be
// accessed via forcedSessionKey
if (req.forcedSessionKey.seenyou) {
res.setHeader('X-Seen-You', 'true');
}
next();
});
A pair of encryption and signature keys are derived from the secret
option
via HMAC-SHA-256; the secret
isn't used directly to encrypt or compute the
MAC.
The key-derivation function, in pseudocode:
encKey := HMAC-SHA-256(secret, 'cookiesession-encryption');
sigKey := HMAC-SHA-256(secret, 'cookiesession-signature');
The AES-256-CBC cipher is used to encrypt the session contents, with an HMAC-SHA-256 authentication tag (via Encrypt-then-Mac composition). A random 128-bit Initialization Vector (IV) is generated for each encryption operation (this is the AES block size regardless of the key size). The CBC-mode input is padded with the usual PKCS#5 scheme.
In pseudocode, the encryption looks like the following, with ||
denoting
concatenation. The createdAt
and duration
parameters are decimal strings.
sessionText := cookieName || '=' || sessionJson
iv := secureRandom(16 bytes)
ciphertext := AES-256-CBC(encKey, iv, sessionText)
payload := iv || '.' || ciphertext || '.' || createdAt || '.' || duration
hmac := HMAC-SHA-256(sigKey, payload)
cookie := base64url(iv) || '.' ||
base64url(ciphertext) || '.' ||
createdAt || '.' ||
duration || '.' ||
base64url(hmac)
For decryption, a constant-time equality operation is used to verify the HMAC output to avoid the plausible timing attack.
The defaults are secure, but may not suit your requirements. Some example scenarios:
If the defaults don't suit your needs, you can customize client-sessions. Beware: Changing keys and/or algorithms will make previously-generated Cookies invalid!
To configure independent encryption and signature (HMAC) keys:
app.use(sessions({
encryptionKey: loadFromKeyStore('session-encryption-key'),
signatureKey: loadFromKeyStore('session-signature-key'),
// ... other options discussed above ...
}));
To specify custom algorithms and keys:
app.use(sessions({
// use WEAKER-than-default encryption:
encryptionAlgorithm: 'aes128',
encryptionKey: loadFromKeyStore('session-encryption-key'),
// use a SHORTER-than-default MAC:
signatureAlgorithm: 'sha256-drop128',
signatureKey: loadFromKeyStore('session-signature-key'),
// ... other options discussed above ...
}));
Supported CBC-mode encryptionAlgorithm
s (and key length requirements):
Cipher | Key length |
---|---|
aes128 | 16 bytes |
aes192 | 24 bytes |
aes256 | 32 bytes |
These key lengths are exactly as required by the Advanced Encryption Standard.
Supported HMAC signatureAlgorithm
s (and key length requirements):
HMAC | Minimum Key Length | Maximum Key Length |
---|---|---|
sha256 | 32 bytes | 64 bytes |
sha256-drop128 | 32 bytes | 64 bytes |
sha384 | 48 bytes | 128 bytes |
sha384-drop192 | 48 bytes | 128 bytes |
sha512 | 64 bytes | 128 bytes |
sha512-drop256 | 64 bytes | 128 bytes |
The HMAC key length requirements are derived from RFC 2104 section 3. The maximum key length can be exceeded, but it doesn't increase the security of the signature.
The -dropN
algorithms discard the latter half of the HMAC output, which
provides some additional protection against SHA2 length-extension attacks on
top of HMAC. The same technique is used in the upcoming JSON Web Algorithms
AES_CBC_HMAC_SHA2
authenticated
cipher.
One can easily generate both AES and HMAC-SHA2 keys via command line: openssl rand -base64 32
for a 32-byte (256-bit) key. It's easy to then parse that
output into a Buffer
:
function loadKeyFromStore(name) {
var text = myConfig.keys[name];
return new Buffer(text, 'base64');
}
If you specify encryptionKey
or signatureKey
, you must supply the other as
well.
The following constraints must be met or an Error
will be thrown:
Buffer
s.Based on the above, please note that if you specify a secret
and a
signatureAlgorithm
, you need to use sha256
or sha256-drop128
.
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
FAQs
secure sessions stored in cookies
We found that client-sessions demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.