This article: Life in a post-database world: using crypto to avoid DB writes
Prompted me to write this url-crypt Node.js module to convert a Secret into secure urlsafe base64 strings.
Think of an email verification link that doesn't need to store it's data in the database. Or, a JWT token whose claims are secret instead of base64.
The idea is to encrypt and store a reasonable amount of data (<512 chars) in < 2k chars of base64 (a URL) so it can be decrypted later.
I published this on npm and then thought. Is it secure? Insecure would be unhelpful to others :-/
How it works:
var urlCrypt = require('url-crypt')('super-secret-key');
var data = { hello: 'world', this: 'is a test', of: 'url-crypt' };
var base64 = urlCrypt.cryptObj(data);
var backAgain = urlCrypt.decryptObj(base64);
expect(backAgain).to.eql(data);
What it does: urlCrypt.cryptObj(obj)
:
- Converts obj to JSON
- Gzips the JSON
- Creates 30 bytes of salt
- Encrypts [salt][gzip] with aes-256-cbc and a pbkdf2 of key
- Converts to base64
And back again.
The code is 2 functions all one file.
Some specific questions:
Here it adds some jitter as a protection against dictionary attacks. Does this make it stronger?
Here it uses pbkdf2 to make the aes key. The salt is constant. Does pbkdf2 this way make it stronger? Adding a salt configuration parameter seems like just making a longer key.
Here it puts some random data in front of the real data. If I knew the real data ({email:"[email protected]"}) I thought it would be easier to guess the secret. This random data is to protect against that. Does this salt help?
Here it repeats the aes-256-cbs. Is this helpful? Repetition makes the ending token longer (which makes for less data in the URL), so is this extraneous?
What's the best way to make computationally harder, w/o making the token longer?
Is there a way to make any of this better? Or anything complexity that could be removed.
Thanks! It's my first security focused module, so I wanted to get it sanity checked.