{"_id":"encrypted-attr","_rev":"207202","name":"encrypted-attr","description":"Encrypted model attributes in your favourite ORM.","dist-tags":{"latest":"1.1.0"},"maintainers":[{"name":"simonratner","email":"simon+github@probablyprime.net"}],"time":{"modified":"2021-06-03T13:39:34.000Z","created":"2017-08-16T09:00:38.143Z","1.1.0":"2017-10-06T05:56:45.702Z","1.0.6":"2017-10-03T18:05:29.247Z","1.0.5":"2017-08-17T01:22:54.298Z","1.0.4":"2017-08-16T19:37:02.153Z","1.0.3":"2017-08-16T18:18:21.836Z","1.0.2":"2017-08-16T09:03:43.211Z","1.0.1":"2017-08-16T09:00:38.143Z"},"users":{},"author":{"name":"Simon Ratner","url":"https://github.com/simonratner"},"repository":{"type":"git","url":"git+https://github.com/simonratner/node-encrypted-attr.git"},"versions":{"1.1.0":{"name":"encrypted-attr","version":"1.1.0","description":"Encrypted model attributes in your favourite ORM.","author":{"name":"Simon Ratner","url":"https://github.com/simonratner"},"engines":{"node":">=4.0"},"keywords":["attr_encrypted","encrypted","attribute","property","model","orm"],"dependencies":{"lodash":"^4.17.4","safe-buffer":"^5.1.1"},"devDependencies":{"mocha":"^3.5.0","mocha-standard":"^1.0.0","standard":"^10.0.3","unexpected":"^10.33.2"},"optionalDependencies":{},"scripts":{"test":"mocha -R spec --recursive"},"homepage":"https://github.com/simonratner/node-encrypted-attr","repository":{"type":"git","url":"git+https://github.com/simonratner/node-encrypted-attr.git"},"bugs":{"url":"https://github.com/simonratner/node-encrypted-attr/issues"},"license":"MIT","gitHead":"32c2e13e9fb7fca20b774808360c5c79c2ea1bd7","_id":"encrypted-attr@1.1.0","_npmVersion":"5.3.0","_nodeVersion":"8.1.4","_npmUser":{"name":"simonratner","email":"simon+github@probablyprime.net"},"dist":{"shasum":"c0f7950c43fe9c958aee3e844400624291dde79e","size":7268,"noattachment":false,"key":"/encrypted-attr/-/encrypted-attr-1.1.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/encrypted-attr/download/encrypted-attr-1.1.0.tgz"},"maintainers":[{"name":"simonratner","email":"simon+github@probablyprime.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/encrypted-attr-1.1.0.tgz_1507269405637_0.45153329032473266"},"directories":{},"publish_time":1507269405702,"_hasShrinkwrap":false,"_cnpm_publish_time":1507269405702},"1.0.6":{"name":"encrypted-attr","version":"1.0.6","description":"Encrypted model attributes in your favourite ORM.","author":{"name":"Simon Ratner","url":"https://github.com/simonratner"},"engines":{"node":">=6.0"},"keywords":["attr_encrypted","encrypted","attribute","property","model","orm"],"dependencies":{"lodash":"^4.17.4"},"optionalDependencies":{},"devDependencies":{"mocha":"^3.5.0","mocha-standard":"^1.0.0","standard":"^10.0.3","unexpected":"^10.33.2"},"scripts":{"test":"mocha -R spec --recursive"},"homepage":"https://github.com/simonratner/node-encrypted-attr","repository":{"type":"git","url":"git+https://github.com/simonratner/node-encrypted-attr.git"},"bugs":{"url":"https://github.com/simonratner/node-encrypted-attr/issues"},"license":"MIT","gitHead":"b16d0d6dd4f4542e2c9dc121764c4ca066a45c54","_id":"encrypted-attr@1.0.6","_npmVersion":"5.3.0","_nodeVersion":"8.1.4","_npmUser":{"name":"simonratner","email":"simon+github@probablyprime.net"},"dist":{"shasum":"5b9f81a9308ebe99dbe9cdd84523c8fdabb6f95e","size":6985,"noattachment":false,"key":"/encrypted-attr/-/encrypted-attr-1.0.6.tgz","tarball":"http://registry.cnpm.dingdandao.com/encrypted-attr/download/encrypted-attr-1.0.6.tgz"},"maintainers":[{"name":"simonratner","email":"simon+github@probablyprime.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/encrypted-attr-1.0.6.tgz_1507053929187_0.172123649623245"},"directories":{},"publish_time":1507053929247,"_hasShrinkwrap":false,"_cnpm_publish_time":1507053929247},"1.0.5":{"name":"encrypted-attr","version":"1.0.5","description":"Encrypted model attributes in your favourite ORM.","author":{"name":"Simon Ratner","url":"https://github.com/simonratner"},"engines":{"node":">=6.0"},"keywords":["attr_encrypted","encrypted","attribute","property","model","orm"],"dependencies":{"lodash":"^4.17.4","node-aes-gcm":"^0.2.2"},"optionalDependencies":{},"devDependencies":{"mocha":"^3.5.0","mocha-standard":"^1.0.0","standard":"^10.0.3","unexpected":"^10.33.2"},"scripts":{"test":"mocha -R spec --recursive"},"homepage":"https://github.com/simonratner/node-encrypted-attr","repository":{"type":"git","url":"git+https://github.com/simonratner/node-encrypted-attr.git"},"bugs":{"url":"https://github.com/simonratner/node-encrypted-attr/issues"},"license":"MIT","gitHead":"28d9c38a210f85707caa1c5b5ae87a14df536934","_id":"encrypted-attr@1.0.5","_npmVersion":"5.3.0","_nodeVersion":"8.1.4","_npmUser":{"name":"simonratner","email":"simon+github@probablyprime.net"},"dist":{"shasum":"86eab92085d743183cbe9700895ea1cb887d6588","size":5389,"noattachment":false,"key":"/encrypted-attr/-/encrypted-attr-1.0.5.tgz","tarball":"http://registry.cnpm.dingdandao.com/encrypted-attr/download/encrypted-attr-1.0.5.tgz"},"maintainers":[{"name":"simonratner","email":"simon+github@probablyprime.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/encrypted-attr-1.0.5.tgz_1502932974166_0.8616908984258771"},"directories":{},"publish_time":1502932974298,"_hasShrinkwrap":false,"_cnpm_publish_time":1502932974298},"1.0.4":{"name":"encrypted-attr","version":"1.0.4","description":"Encrypted model attributes in your favourite ORM.","author":{"name":"Simon Ratner","url":"https://github.com/simonratner"},"engines":{"node":">=6.0"},"keywords":["attr_encrypted","encrypted","attribute","property","model","orm"],"dependencies":{"lodash":"^4.17.4","node-aes-gcm":"^0.2.2"},"optionalDependencies":{},"devDependencies":{"mocha":"^3.5.0","mocha-standard":"^1.0.0","standard":"^10.0.3","unexpected":"^10.33.2"},"scripts":{"test":"mocha -R spec --recursive"},"homepage":"https://github.com/simonratner/node-encrypted-attr","repository":{"type":"git","url":"git+https://github.com/simonratner/node-encrypted-attr.git"},"bugs":{"url":"https://github.com/simonratner/node-encrypted-attr/issues"},"license":"MIT","gitHead":"7be3a0b90c042382a73f3d624b35ead6efd64e4e","_id":"encrypted-attr@1.0.4","_npmVersion":"5.3.0","_nodeVersion":"8.1.4","_npmUser":{"name":"simonratner","email":"simon+github@probablyprime.net"},"dist":{"shasum":"91b1f8bbb68894094ff1e4e55f5884480d052415","size":5343,"noattachment":false,"key":"/encrypted-attr/-/encrypted-attr-1.0.4.tgz","tarball":"http://registry.cnpm.dingdandao.com/encrypted-attr/download/encrypted-attr-1.0.4.tgz"},"maintainers":[{"name":"simonratner","email":"simon+github@probablyprime.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/encrypted-attr-1.0.4.tgz_1502912221113_0.2996102957986295"},"directories":{},"publish_time":1502912222153,"_cnpm_publish_time":1502912222153,"_hasShrinkwrap":false},"1.0.3":{"name":"encrypted-attr","version":"1.0.3","description":"Encrypted model attributes in your favourite ORM.","author":{"name":"Simon Ratner","url":"https://github.com/simonratner"},"keywords":["attr_encrypted","encrypted","attribute","property","model","orm"],"dependencies":{"lodash":"^4.17.4","node-aes-gcm":"^0.2.2"},"optionalDependencies":{},"devDependencies":{"mocha":"^3.5.0","mocha-standard":"^1.0.0","standard":"^10.0.3","unexpected":"^10.33.2"},"scripts":{"test":"mocha -R spec --recursive"},"homepage":"https://github.com/simonratner/node-encrypted-attr","repository":{"type":"git","url":"git+https://github.com/simonratner/node-encrypted-attr.git"},"bugs":{"url":"https://github.com/simonratner/node-encrypted-attr/issues"},"license":"MIT","gitHead":"cd217c1f5067e3151cdbd8dc7ab94458e2d3b106","_id":"encrypted-attr@1.0.3","_npmVersion":"5.3.0","_nodeVersion":"8.1.4","_npmUser":{"name":"simonratner","email":"simon+github@probablyprime.net"},"dist":{"shasum":"3265a5747e4d29ccc55e84c7755d16232c8b9d58","size":5021,"noattachment":false,"key":"/encrypted-attr/-/encrypted-attr-1.0.3.tgz","tarball":"http://registry.cnpm.dingdandao.com/encrypted-attr/download/encrypted-attr-1.0.3.tgz"},"maintainers":[{"name":"simonratner","email":"simon+github@probablyprime.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/encrypted-attr-1.0.3.tgz_1502907501752_0.10656847013160586"},"directories":{},"publish_time":1502907501836,"_hasShrinkwrap":false,"_cnpm_publish_time":1502907501836},"1.0.2":{"name":"encrypted-attr","version":"1.0.2","description":"Encrypted model attributes in your favourite ORM.","author":{"name":"Simon Ratner","url":"https://github.com/simonratner"},"keywords":["attr_encrypted","encrypted","attribute","property","model","orm"],"dependencies":{"lodash":"^4.17.4","node-aes-gcm":"^0.2.2"},"optionalDependencies":{},"devDependencies":{"mocha":"^3.5.0","mocha-standard":"^1.0.0","standard":"^10.0.3","unexpected":"^10.33.2"},"scripts":{"test":"mocha -R spec --recursive"},"homepage":"https://github.com/simonratner/node-encrypted-attr","repository":{"type":"git","url":"git+https://github.com/simonratner/node-encrypted-attr.git"},"bugs":{"url":"https://github.com/simonratner/node-encrypted-attr/issues"},"license":"MIT","gitHead":"a752eb0480f6de263e48d265845fdd8631c36561","_id":"encrypted-attr@1.0.2","_npmVersion":"5.3.0","_nodeVersion":"8.1.4","_npmUser":{"name":"simonratner","email":"simon+github@probablyprime.net"},"dist":{"shasum":"0fd9fa516621595982048f22b0cb38745ddf531a","size":5017,"noattachment":false,"key":"/encrypted-attr/-/encrypted-attr-1.0.2.tgz","tarball":"http://registry.cnpm.dingdandao.com/encrypted-attr/download/encrypted-attr-1.0.2.tgz"},"maintainers":[{"name":"simonratner","email":"simon+github@probablyprime.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/encrypted-attr-1.0.2.tgz_1502874223068_0.24271863093599677"},"directories":{},"publish_time":1502874223211,"_hasShrinkwrap":false,"_cnpm_publish_time":1502874223211},"1.0.1":{"name":"encrypted-attr","version":"1.0.1","description":"Encrypted model attributes in your favourite ORM.","author":{"name":"Simon Ratner","url":"https://github.com/simonratner"},"keywords":["attr_encrypted","encrypted","attribute","property","model","orm"],"dependencies":{"es6-error":"^4.0.2","lodash":"^4.17.4","node-aes-gcm":"^0.2.2"},"optionalDependencies":{},"devDependencies":{"mocha":"^3.5.0","mocha-standard":"^1.0.0","standard":"^10.0.3","unexpected":"^10.33.2"},"scripts":{"test":"mocha -R spec --recursive"},"homepage":"https://github.com/simonratner/node-encrypted-attr","repository":{"type":"git","url":"git+https://github.com/simonratner/node-encrypted-attr.git"},"bugs":{"url":"https://github.com/simonratner/node-encrypted-attr/issues"},"license":"MIT","gitHead":"13cb4f50143f2ed4a202e1a01a80fb32a6335bc6","_id":"encrypted-attr@1.0.1","_npmVersion":"5.3.0","_nodeVersion":"8.1.4","_npmUser":{"name":"simonratner","email":"simon+github@probablyprime.net"},"dist":{"shasum":"0b148cd89cc7f32e96442ab51eef4d61ec0d1057","size":5025,"noattachment":false,"key":"/encrypted-attr/-/encrypted-attr-1.0.1.tgz","tarball":"http://registry.cnpm.dingdandao.com/encrypted-attr/download/encrypted-attr-1.0.1.tgz"},"maintainers":[{"name":"simonratner","email":"simon+github@probablyprime.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/encrypted-attr-1.0.1.tgz_1502874038041_0.29193784832023084"},"directories":{},"publish_time":1502874038143,"_cnpm_publish_time":1502874038143,"_hasShrinkwrap":false}},"readme":"# encrypted-attr\n\n[![travis](http://img.shields.io/travis/simonratner/node-encrypted-attr/master.svg?style=flat-square)](https://travis-ci.org/simonratner/node-encrypted-attr) &nbsp;\n[![npm](http://img.shields.io/npm/v/encrypted-attr.svg?style=flat-square)](https://www.npmjs.org/package/encrypted-attr) &nbsp;\n[![license](https://img.shields.io/github/license/simonratner/node-encrypted-attr.svg?style=flat-square)](LICENSE)\n\nEncrypted model attributes in your favourite ORM.\n\n# Security model\n\n* AES-256-GCM:\n    * 96-bit random nonce\n    * 128-bit authentication tag\n* Additional authenticated data:\n    * Key id: use different keys for different attributes (or different users),\n      rotate keys for new data over time without re-encrypting old data\n    * Object id: prevent substitution of encrypted values\n\nAll keys should be 32 bytes long, and cryptographically random. Manage these\nkeys as you would any other sensitive credentials (environment config, vault,\nkeychain). You can generate random keys with this snippet:\n```\nnode -p \"require('crypto').randomBytes(32).toString('base64')\"\n```\n\nRefer to [NIST Special Publication 800-38D](http://doi.org/10.6028/NIST.SP.800-38D)\nfor additional recommendations. In particular, you should pay attention to\nuniqueness requirements for keys and IVs, and constraints on the number of\ninvocations with a given key (Section 8). These should inform key rotation\npolicies.\n\n# Threat model\n\nThis is designed to protect you from leaking sensitive user data under very\nspecific scenarios:\n\n* Full data dump\n    * Misplaced unencrypted backups\n    * Compromised database host\n* Partial data dump\n    * Query injection via unsanitized input\n\nSpecifically, this does *not* provide any protection in cases of a compromised\napp host, app-level vulnerabilities, or accidentally leaking sensitive data\ninto logs. It is also not a substitute for actually encrypting your backups,\nsanitizing your input, et cetera.\n\n# Install\n\nThis module requires at least node v4.0.\n\n```\nnpm install encrypted-attr\n```\n\n# Use\n\nThis module can be used stand-alone to encrypt individual values, or wrapped\ninto a plugin or hook to your favourite ORM.\n\nHere is a quick example to get started:\n\n```js\nconst EncryptedAttributes = require('encrypted-attr')\n\nlet encryptedAttributes = EncryptedAttributes(null, {\n  keys: {\n    k1: crypto.randomBytes(32).toString('base64') // use a persistent key\n  },\n  keyId: 'k1'\n})\n\nlet secretNumber = '555-55-5555'\nlet encryptedNumber = encryptedAttributes.encryptAttribute(null, secretNumber)\n// YWVzLTI1Ni1nY20kJGsx$r2JF/XHvpsgNwJDs$c/P6GwnUZGokEjQ=$sEMv0cyKPBL90mo2zZ1MpQ\n```\n\n### EncryptedAttributes(attributes, options)\n\nConstruct an instance to handle encryption and decryption. You should construct\nfor each unique set of attributes and keys that you want to encrypt.\n\n| Parameter    | Type             | Required?  | Details                                                                                                                                                                                                                                                    |\n| :----------- | :--------------- | :--------- | :----------------------------                                                                                                                                                                                                                              |\n| `attributes` | array of strings | _Optional_ | List attributes which should be encrypted.  These can be specified as top-level string keys, or nested paths using dot-separated notation.  This list is used by `encryptAll`/`decryptAll`, and is also useful for creating helper methods on your models. |\n| `options`    | dictionary       | _Optional_ | See below.                                                                                                                                                                                                                                                 |\n\nThese options are supported:\n\n| Option       | Type             | Required?  | Details                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n| :----------- | :--------------- | :--------- | :----------------------------                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |\n| `keys`       | dictionary       | Required   | Dictionary of all relevant data encryption keys, as `base64` strings.  Since encrypted strings _embed the key id that was used to encrypt them_, it's important that `keys` contains the appropriate key for any previously encrypted data you might run across.                                                                                                                                                                                                                                                                                           |\n| `keyId`      | string           | Required   | The id of the key to use for all _new encryptions_.  This is not necessarily the only key that will be used for decryptions, because the key id gets embedded into the encrypted string.  When that string is decrypted, this module unpacks that key id and uses it to determine the appropriate decryption key.  This approach allows multiple keys to be used for the same attribute.  (Note that this option is only _technically_ required if you need to encrypt new data. If you are only decrypting existing data, you do not need to provide it.) |\n| `verifyId`   | string           | _Optional_ | The property name to use as the primary id for objects. If not set, object id will not be included during encryption, nor verified during decryption. If set to a truthy value that isn't a string, `\"id\"` will be used instead.                                                                                                                                                                                                                                                                                                                           |\n\nIf the `verifyId` option is specified, the value of that property on the source\nobject passed during encryption will be included as part of the authenticated\nmetadata; during decryption, this value is expected to match the value of the\nsame property on the object passed during decryption, otherwise an exception\nis thrown.\n\n### encryptAttribute(sourceObject, plaintextString)\n\nEncrypt a value using one of your keys (specifically, the key indicated by the\n`keyId` option specified in the constructor).\n\nReturns the encrypted representation of the value. Does nothing if the provided\nvalue is already encrypted using this module (so this method is idempotent).\n\n```js\nlet encryptedString = encryptAttributes.encryptAttribute(sourceObject, plaintextString)\n```\n\n> `sourceObject` is optional, and only relevant if the `verifyId` option is used;\notherwise you may pass `null` or `undefined`.\n\n\n### encryptAll(sourceObject)\n\nEncrypt a subset of fields in the provided plain object. The set of fields to\nbe encrypted is specified by the array of attribute paths supplied to the\n`EncryptedAttributes` constructor.\n\nReturns the source object with any to-by-encrypted fields replaced by their\nencrypted representation. Note that this method modifies the provided object.\n\n```js\nlet partiallyEncryptedObject = encryptedAttributes.encryptAll(sourceObject)\n```\n\n\n### decryptAttribute(sourceObject, encryptedString)\n\nDecrypt a value.\n\nReturns the plaintext string. Does nothing if the provided value does not look\nlike it was encrypted using this module (so this method is idempotent).\n\n```js\nlet plaintextString = encryptedAttributes.decryptAttribute(sourceObject, encryptedString)\n```\n\n> `sourceObject` is optional, and only relevant if the `verifyId` option is used;\notherwise you may pass `null` or `undefined`.\n\n\n### decryptAll(sourceObject)\n\nDecrypt a subset of fields in the provided plain object. The set of fields to\nbe decrypted is specified by the array of attribute paths supplied to the\n\nReturns the source object with any encrypted fields replaced by their plaintext\nvalue. Note that this method modifies the provided object.\n\n```js\nlet decryptedObject = encryptedAttributes.decryptAll(partiallyEncryptedObject)\n```\n\n\n# Use with an ORM\n\nWhile this module can be used stand-alone, it is designed to be wrapped into\na plugin or hook to your favourite ORM. Eventually, this package may include\nsuch plugins for common ORMs, but for now, here's an example using\n[thinky](https://github.com/neumino/thinky):\n\n```js\nconst EncryptedAttributes = require('encrypted-attr')\nconst thinky = require('thinky')()\nconst _ = require('lodash')\n\nlet Model = thinky.createModel('Model', {})\n\nlet encryptedAttributes = EncryptedAttributes(['secret', 'nested.secret'], {\n  keys: {\n    k1: crypto.randomBytes(32).toString('base64') // use an actual key here\n  },\n  keyId: 'k1',\n  verifyId: 'id'\n})\n\n// Pre-save hook: encrypt model attributes that need to be encrypted.\nModel.docOn('saving', function (doc) {\n  encryptedAttributes.encryptAll(doc)\n})\n\n// Post-save hook: decrypt model attributes that need to be decrypted.\nModel.docOn('saved', function (doc) {\n  encryptedAttributes.decryptAll(doc)\n})\n\n// Post-retrieve hook: ditto.\nModel.on('retrieved', function (doc) {\n  encryptedAttributes.decryptAll(doc)\n})\n\n// Optionally, add some helpers in case we need to set or read the value\n// directly (such as an update query), without going through model hooks.\nfor (let attr of encryptedAttributes.attributes) {\n  Model.define(_.camelCase(`encrypt ${attr}`), function (val) {\n    return encryptedAttributes.encryptAttribute(this, val)\n  })\n  Model.define(_.camelCase(`decrypt ${attr}`), function (val) {\n    return encryptedAttributes.decryptAttribute(this, val)\n  })\n}\n```\n\nAnd a usage example:\n\n```js\nasync function storeSomeSecrets (doc) {\n  await doc.merge({\n    secret: 'red',\n    nested: {\n      hint: 'colour',\n      secret: 'yellow'\n    }\n  }).save()\n\n  console.log(await Model.get(doc.id))\n  // {\n  //   id: '543bed92-e241-4151-9d8f-1aa942c36d24',\n  //   nested: {\n  //     hint: 'colour',\n  //     secret: 'yellow'\n  //   },\n  //   secret: 'red'\n  // }\n\n  console.log(await Model.get(doc.id).execute())\n  // {\n  //   id: '543bed92-e241-4151-9d8f-1aa942c36d24',\n  //   nested: {\n  //     hint: 'colour',\n  //     secret: 'YWVzLTI1Ni1nY20k...$NFvcFAaPTDay3uWH$t3G9Jrpy$g+BJT/UvfuboMB3ARiDRIQ'\n  //   },\n  //   secret: 'YWVzLTI1Ni1nY20k...$6UdqWqv9ox305Wmt$zyNF$S5BOgZSvMG3H24foFaTQjg'\n  // }\n}\n```\n\n\n# License\n\n[MIT](LICENSE)\n","_attachments":{},"homepage":"https://github.com/simonratner/node-encrypted-attr","bugs":{"url":"https://github.com/simonratner/node-encrypted-attr/issues"},"license":"MIT"}