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.
First: this library only makes sense if your clients sit behind a firewall, or if you control who can get an account and anonymous users don't need to access encrypted data. Otherwise, the encryption key will be public defeating the whole point.
This library monkey-patches either the Firebase Admin SDK or the Firebase JavaScript Client SDK to automatically encrypt and decrypt keys and values of your choosing using AES-SIV. Almost everything just works, except that startAt
and endAt
queries on encrypted data would produce randomly ordered results and so are forbidden. equalTo
queries will work fine, however, since a given plaintext value will always encrypt to the same ciphertext — but it will also let an attacker know if any two values are equal, even if they don't know what they are.
The library works both in Node (4.x+) and in the browser. In the browser, you need to also load crypto-js
(the following modules are sufficient: core.js
, enc-base64.js
, md5.js
, evpkdf.js
, cipher-core.js
, aes.js
, mode-ctr.js
) and cryptojs-extension
(only build/siv.js
is required). If you want to enable caching to enhance performance, then in the browser you'll also want to load node-lru-cache
. All these libraries are automatically included in the Node distribution.
Upon requiring this library, the admin.database()
or firebase.database()
method as well as the
app.App.database()
methods are monkey-patched to return a custom FireCrypt
instance in place of
standard admin.database.Database
or firebase.database.Database
instances. The FireCrypt
instance generally has the same API, although you must first configure the encryption before it
can be used:
Configure FireCrypt With Firebase Admin SDK:
const admin = require('firebase-admin');
const FireCrypt = require('firecrypt');
const app = admin.initializeApp({
// ...
})
const db = admin.database(); // OR const db = app.database();
const encryptionKeyCheckValue = db.configureEncryption(options, specification);
Configure FireCrypt With Firebase Client SDK:
const firebase = require('firebase');
const FireCrypt = require('firecrypt');
const app = firebase.initializeApp({
// ...
})
const db = firebase.database(); // OR const db = app.database();
const encryptionKeyCheckValue = db.configureEncryption(options, specification);
The options
are as follows:
algorithm
: the crypto algorithm to use. Currently supported values are:
aes-siv
: actual encryption using AES-SIV.passthrough
: fake encryption using an identity transform, useful for debugging.none
: no encryption, will throw an error with firecrypt === 'NO_KEY'
if you attempt to read or write any encrypted path.key
: the required key for algorithm aes-siv
. Must be 32, 48, or 64 bytes, encoded in base 64. You can generate such a key using openssl rand -base64 64
. If you attempt to decrypt a value with the wrong key then an error with firecrypt === 'WRONG_KEY'
will be thrown.encryptionKeyCheckValue
: a value generated by a previous call to configureEncryption()
used to verify that the aes-siv
key
used in both calls is the same. If a different key was used to generate the encryptionKeyCheckValue
then an error with firecrypt === 'WRONG_KEY'
will be thrown.
encryptionKeyCheckValue
is also available at any time via
admin.database().encryptionKeyCheckValue
or firebase.database().encryptionKeyCheckValue
.cacheSize
: the maximum size in bytes of the encryption and decryption caches, used to improve performance. In the browser, the caches will only be activated if LRUCache
is defined; it should conform to the API of node-lru-cache
. You can also specify encryptionCacheSize
and decryptionCacheSize
separately.If the algorithm
is aes-siv
then configureEncryption()
will return a value that can be used to synchronously verify whether another key matches by passing it via encryptionKeyCheckValue
. This can be useful if the key is distributed as part of a session, and you want to check if you need to invalidate the session because the key has been rotated. Also, if the key doesn't match then decrypting will throw an exception later.
The specification
is a JSON structure similar to Firebase security rules but specifying which keys and values need to be encrypted instead. The structure mimics that of your datastore and uses $wildcards
in the same manner as security rules.
{
"rules": {
"foo": {
".encrypt": {"value": "#"}
},
"bar": {
"$baz": {
".encrypt": {"key": "#-#-."}
}
}
}
}
Each .encrypt
directive can require the key or value (or both) at that path to be encrypted. The parameter is an encryption pattern, where #
are placeholders for chunks to be encrypted, .
for chunks that should not be encrypted, and everything else is matched verbatim to the plaintext data. Normally, you'll just use a single #
to encrypt the entire key or value, but sometimes it can be useful to encrypt only specific parts of a composite key. You can also specify an empty pattern to explicitly indicate that something should not be encrypted, which is only useful if you're encrypting a sibling wildcard key but don't want some specific instances to be encrypted.
You must specify value encryption at the atomic data leaves only — it's not valid to encrypt an object and trying to do so will throw an exception at runtime. There's currently no way to require encryption for an entire subtree.
For bulk encryption/decryption (including key rotation), you can also specify ".encrypt": {"few": true}
on wildcard keys (whether encrypted or not) where the number of children is expected to be low enough that it's reasonable to read or write them all at once.
You may want to check out fireplan
for a convenient way to generate the encryption specification from your security rules schema. See also firecrypt-tools
for related utilities.
Run the following commands from the command line to get your local environment set up:
$ git clone git@github.com:Reviewable/firecrypt.git
$ cd firecrypt # go to the firecrypt directory
$ npm install # install local npm dependencies
Run the following command to build the distribution files for the library:
$ npm run build
This will generate the following distribution files, along with accompanying source maps:
dist/node/firecrypt.js
- A non-minified CommonJS build of the library for use in Node.js.dist/browser/firecrypt.js
- A non-minified IIFE build of the library for use in the browser.dist/browser/firecrypt.min.js
- A minified IIFE build of the library for use in the browser.FAQs
Transparent encryption for Firebase
The npm package firecrypt receives a total of 15 weekly downloads. As such, firecrypt popularity was classified as not popular.
We found that firecrypt demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 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.