|
1 |
| -# node-encrypted-attr |
2 |
| -Encrypted model attribute implementation that can be easily plugged into your favourite ORM. |
| 1 | +# Encrypted Attributes |
| 2 | + |
| 3 | +Encrypted model attribute implementation that can be easily plugged into your |
| 4 | +favourite ORM. |
| 5 | + |
| 6 | +# Security model |
| 7 | + |
| 8 | +* AES-256-GCM |
| 9 | +* 96-bit random nonce |
| 10 | +* 128-bit authentication tag |
| 11 | +* Additional authenticated data: |
| 12 | + * Key id, allowing use of different keys for different attributes, or |
| 13 | + rotating keys over time without re-encrypting all data |
| 14 | + * [*Optional*] Object id, allowing to detect substitutions of encrypted |
| 15 | + values |
| 16 | + |
| 17 | +All keys should be 32 bytes long, and cryptographically random. Manage these |
| 18 | +keys as you would any other credentials (environment config, keychain, vault). |
| 19 | + |
| 20 | +Best way to generate keys: |
| 21 | +``` |
| 22 | +node -p "require('crypto').randomBytes(32).toString('base64')" |
| 23 | +``` |
| 24 | + |
| 25 | +# Threat model |
| 26 | + |
| 27 | +This is designed to protect you from leaking sensitive user data under very |
| 28 | +specific scenarios: |
| 29 | + |
| 30 | +* Full database dump |
| 31 | + * Misplaced unencrypted backups |
| 32 | + * Compromised database host |
| 33 | +* Partial database dump |
| 34 | + * Query injection via unsanitized input |
| 35 | + |
| 36 | +Specifically, this does *not* provide any protection in cases of a compromised |
| 37 | +web app host, app-level vulnerabilities, or accidental leaks into persistent |
| 38 | +logs. It is also in no way a substitute for actually encrypting your backups, |
| 39 | +sanitizing all you input, et cetera. |
| 40 | + |
| 41 | +# Install |
| 42 | + |
| 43 | +``` |
| 44 | +npm install node-encrypted-attr |
| 45 | +``` |
| 46 | + |
| 47 | +# Use |
| 48 | + |
| 49 | +While this module can be used stand-alone to encrypt individual values (see |
| 50 | +[tests](/test/)), it is designed to be wrapped in a plugin or hook for your |
| 51 | +favourite ORM. Eventually, this package may include such plugins for common |
| 52 | +ORMs, but for now, here's an example of integrating with [thinky](https://github.com/neumino/thinky): |
| 53 | + |
| 54 | +## Thinky |
| 55 | + |
| 56 | +``` |
| 57 | +const EncryptedAttributes = require('node-encrypted-attr') |
| 58 | +const thinky = require('thinky')() |
| 59 | +const _ = require('lodash') |
| 60 | +
|
| 61 | +let Model = thinky.createModel('Model', {}) |
| 62 | +
|
| 63 | +Model.encryptedAttributes = EncryptedAttributes(['secret'], { |
| 64 | + keys: { |
| 65 | + k1: 'bocZRaBnmtHb2pXGTGixiQb9W2MmOtRBpbJn3ADX0cU=' |
| 66 | + }, |
| 67 | + keyId: 'k1' |
| 68 | +}) |
| 69 | +
|
| 70 | +// Pre-save hook: encrypt any model attributes that need to be encrypted. |
| 71 | +Model.pre('save', function (next) { |
| 72 | + try { |
| 73 | + this.encryptedAttributes.encryptAll(obj) |
| 74 | + process.nextTick(next) |
| 75 | + } catch (err) { |
| 76 | + process.nextTick(next, err) |
| 77 | + } |
| 78 | +}) |
| 79 | +
|
| 80 | +// Post-save hook: decrypt any model attributes that need to be decrypted. |
| 81 | +Model.post('save', function (next) { |
| 82 | + try { |
| 83 | + this.encryptedAttributes.decryptAll(obj) |
| 84 | + process.nextTick(next) |
| 85 | + } catch (err) { |
| 86 | + process.nextTick(next, err) |
| 87 | + } |
| 88 | +}) |
| 89 | +
|
| 90 | +// Post-retrieve hook: ditto. |
| 91 | +Model.post('retrieve', function (next) { |
| 92 | + try { |
| 93 | + this.encryptedAttributes.decryptAll(obj) |
| 94 | + process.nextTick(next) |
| 95 | + } catch (err) { |
| 96 | + process.nextTick(next, err) |
| 97 | + } |
| 98 | +}) |
| 99 | +
|
| 100 | +// Optionally, add some helper methods in case you need to set or read a value |
| 101 | +// directly, without going through model parser. |
| 102 | +for (let attr of Model.encryptedAttributes.attributes) { |
| 103 | + Mode.define(_.camelCase(`encrypted ${attr}`), function (val) { |
| 104 | + return Model.encryptedAttributes.encryptAttribute(this, val) |
| 105 | + } |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +# License |
| 110 | + |
| 111 | +[MIT](LICENSE) |
0 commit comments