#178: Feature request to encrypt session data stored in cookies
This references issue: #178.
When session data is stored in the cookie, it isn't cryptographically encrypted, and is therefore unsecure. The existing encode and decode functions simply pass the JSON object through to be stringified and converted to base64, and back again.
This pull request adds functionality to use crypto-supported algorithms to encrypt their session data, to safely store in a cookie.
The IV length and key length checks are handled by crypto, while validation exists within the feature to verify correctly formatted encryption options, encrypted string, and data.
A user can extend the session.opts object and define what algorithm they want to use, the initialization vector length, and the key, and set the flag useCrypto to true, and the library will handle the rest.
eg.
const encrypt = require('./lib/encrypt');
const crypto = require('crypto');
const options = {
useCrypt: true,
ivlen: 16,
algo: 'aes-256-gcm',
secret: crypto.randomBytes(32);
};
const data = {
message: 'hello, world!',
};
const encrypted = encrypt.encryptData(data, options);
console.log(encrypted);
console.log(encrypt.decryptData(encrypted, options));
A user can also use these same settings when initialising the session instance in App:
const crypto = require('crypto');
const Koa = require('koa');
const app = new Koa();
const session = require('./index');
const options = {
useCrypt: true,
ivlen: 16,
algo: 'aes-256-gcm',
secret: crypto.randomBytes(32),
};
let views = 0;
app.keys = [ 'a', 'b' ];
app.use(session(options, app));
app.use(async ctx => {
views++;
ctx.session.views = views;
ctx.session.message = 'hello, world! Views: ' + ctx.session.views;
ctx.body = ctx.session.message;
});
app.listen(3000);
Output:
curl -v http://localhost:3000/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Content-Length: 22
< Set-Cookie: koa:sess=MjNhMDQ2N2VlMmVjOTc1ZjAwNzUyNjUyYTFjMTBjNDUuNjIwNGFlMjMyNTk2NDVlYjdhZjAzOTIzYjc5NGU3ZGIuMmQ1ZmI3MjMyMzk1MTA4ODg2MzQyNzdiZDNhNThhZGRhYzQzYTVlZjBkYmQ2Nzg0Njg0ZmNhNjllYWVlMTMxYjEyZjdmYzUxYjE3NzY2ZDU3MmIxMTE1MmU1NmNkYjI4MDE4NGE3NWJiZTFiYTYwY2Q1NTM4ZDBlM2RlYzMwMjFmZTdmMGI2NDBkZjA2MjI4MTY1NjViZTJkZTI1ZDU3ZmJmMmIyNjhhM2NlYmYwNjliZA==; path=/; httponly
< Set-Cookie: koa:sess.sig=zGGX31V-b5u_ioX0EP_YsfVARzk; path=/; httponly
< Date: Mon, 19 Aug 2019 07:27:51 GMT
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
hello, world! Views: 1
I have also updated index.js to include default values for the accepted options. This ensures that values are always set, and that it provides a degree of information to the developer about what options there are.
const defaults = {
key: 'koa:sess',
overwrite: true,
httpOnly: true,
signed: true,
autoCommit: true,
maxAge: null,
useCrypt: false,
};
...
function formatOpts(opts) {
opts = Object.assign({}, defaults, opts || {});
...
}
TODO Add the following test cases
- [ ] formatOpts
- [ ] defining the
encodeanddecodefunctions - [ ] the
encryptfile - [ ] cookies with crypto encoded data Also:
- [ ] Add performance stats
Coverage decreased (-61.3%) to 38.71% when pulling e2d86a75c58a29b69405ad98cead4c256bc7f31a on jmitchell38488:gh-178-encrypt-cookie into 10bb12246699101a0c87a2f3e2e09b1a79e10e33 on koajs:master.
Coverage decreased (-61.3%) to 38.71% when pulling e2d86a75c58a29b69405ad98cead4c256bc7f31a on jmitchell38488:gh-178-encrypt-cookie into 10bb12246699101a0c87a2f3e2e09b1a79e10e33 on koajs:master.
I'd recommend https://github.com/nicokaiser/koa-encrypted-session for now, or you can write your own wrapper to use specific encrypt/decrypt methods.
@dead-horse that one prescribes the algo, rather than letting you set it. I've set this one up with more flex around the algo you can pick, and whether you want to enable it.
@dead-horse is this something that would be merged into the code base?
It looks like this would be one way to resolve #181, which may otherwise be a critical security vulnerability.