Last active
February 15, 2021 18:15
-
-
Save kennwhite/de7892fdb71ee3112f8370200e87859b to your computer and use it in GitHub Desktop.
Basic FLE hello world with shell, demonstrating per-user keys (json pointers)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Simple demonstration using MongoDB Client-Side Field Level Encryption (local key version) | |
using json pointer for per-user/per-document dynamic key selection | |
This pattern might be useful for Right to Be Forgotten GDPR use case. | |
Note: FLE schemas with json pointer dynamic key IDs require randomized mode and automatic encryption | |
Decryption -- whether randomized or deterministic -- is always automatic, assuming the data key is available/cached. | |
If deterministic (searchable) mode is required, consider dynamic user key selection | |
at the app level via explicit encryption methods (versus automatic), e.g.: | |
db.people.insert({ | |
firstName: 'Alan', | |
... | |
ssn: db.getMongo().encrypt( $userIdKey, "901-01-0002" , ENC_DETERM ), | |
dob: db.getMongo().encrypt( $userIdKey, new Date('1912-06-23'), ENC_DETERM )) | |
}) | |
Requires Enterprise 4.2+ shell (comes with built-in mongocryptd) and a MongoDB 4.2+ database | |
Local, stand-alone, replica set cluster, or Atlas MongoDB will all work. | |
To use this, just open Mongo shell, with this file, e.g.: | |
mongo --nodb --shell hello_world_shell_local_per_user.js | |
*/ | |
var demoDB = "demoFLE" | |
var keyVaultColl = "__keystore" // nothing special about this key vault collection name, but make it stand out | |
const ENC_DETERM = 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' | |
const ENC_RANDOM = 'AEAD_AES_256_CBC_HMAC_SHA_512-Random' | |
print("\n\nBeginning FLE demo... \n") | |
var env = {} | |
env.connStr = "localhost" | |
// Don't use this keyString to protect anything that matters and DEFINITELY NOT IN PRODUCTION | |
// Generate value with command line: echo $(head -c 96 /dev/urandom | base64 | tr -d '\n') | |
env.keyString = "Bd3ab6v14VHyMXKTUSKB8QxDL2Wb70NcCEL3cmbfs28jY7wqZ2BEHKdBZnz64NTCYJboqmZpf+2cmXf9X7A7oTRNk25MTZq5/pZHc7bsM5TsdyH+fvzd826VQXLVKwsU"; | |
var localDevMasterKey = { key: BinData( 0, env.keyString ) } | |
var clientSideFLEOptions = { | |
kmsProviders : { local : localDevMasterKey } , | |
schemaMap: {}, // on first invocation prior to field key generation, this should be empty | |
keyVaultNamespace: demoDB + "." + keyVaultColl | |
} | |
print("Setting up an FLE Mongo client session... \n") | |
var encryptedSession = new Mongo(env.connStr, clientSideFLEOptions); | |
// Non-interactive mongo shell script equivalent of "use demoFLE" | |
db = encryptedSession.getDB( demoDB ) | |
// Wipe sandbox. Approximate Atlas equivalent of: db.dropDatabase() | |
print("Wiping FLE demo sandbox... \n\n") | |
db.getCollectionNames().forEach(function(c){db.getCollection(c).drop()}); | |
var keyVault = encryptedSession.getKeyVault(); | |
print("Creating user-specific data keys... \n\n") | |
printjson( keyVault.createKey("local", "", ["u0001"]) ) | |
printjson( keyVault.createKey("local", "", ["u0002"]) ) | |
printjson( keyVault.createKey("local", "", ["u0003"]) ) | |
print("\nAttempting to retrieve field keys... \n") | |
var records = db.getCollection( keyVaultColl ).find().pretty() | |
while (records.hasNext()) { | |
printjson(records.next()); | |
} | |
print("\nSetting server-side json schema for automatic encryption on `people` collection... \n\n") | |
db.createCollection("people") | |
db.runCommand({ | |
collMod: "people", | |
validator: { | |
$jsonSchema: { | |
"bsonType": "object", | |
"properties": { | |
"ssn": { | |
"encrypt": { | |
"bsonType": "string", | |
"algorithm": ENC_RANDOM, | |
"keyId": "/userid" | |
} | |
}, | |
"dob": { | |
"encrypt": { | |
"bsonType": "date", | |
"algorithm": ENC_RANDOM, | |
"keyId": "/userid" | |
} | |
}, | |
} | |
} | |
} | |
}) | |
print("Attempting to insert sample documents with automatic encryption using per-user keys... \n") | |
try { | |
var res = null | |
res = db.people.insert({ | |
firstName: 'Grace', | |
lastName: 'Hopper', | |
userid: 'u0001', | |
ssn: '901-01-0001', | |
dob: new Date('1989-12-13'), | |
address: { | |
street: '123 Main Street', | |
city: 'Omaha', | |
state: 'Nebraska', | |
zip: '90210' | |
}, | |
contact: { | |
mobile: '202-555-1234', | |
email: 'grace@example.com', | |
} | |
}) | |
} catch (err) { | |
res = err | |
} | |
print("Result: " + res) | |
try { | |
var res = null | |
res = db.people.insert({ | |
firstName: 'Alan', | |
lastName: 'Turing', | |
userid: 'u0002', | |
ssn: '901-01-0002', | |
dob: new Date('1912-06-23'), | |
address: { | |
street: '456 Oak Lane', | |
city: 'Cleveland', | |
state: 'Ohio', | |
zip: '23210' | |
}, | |
contact: { | |
mobile: '202-123-1234', | |
email: 'alan@example.net', | |
} | |
}) | |
} catch (err) { | |
res = err | |
} | |
print("Result: " + res + "\n\n") | |
print("Dumping decrypted documents from `people`...") | |
var records = db.people.find().pretty() | |
while (records.hasNext()) { | |
printjson(records.next()); | |
} | |
print("\nDeleting user data key for userid: `u0002`... \n\n") | |
res = db.getCollection('__keystore').deleteOne({"keyAltNames": ["u0002"]}) | |
printjson(res) | |
print("\nEstablishing a new Mongo client session (to flush cached keys)... \n\n") | |
encryptedSession = new Mongo(env.connStr, clientSideFLEOptions); | |
db = encryptedSession.getDB( demoDB ) | |
print("Dumping updated key vault...") | |
try { | |
var r = db.getCollection( keyVaultColl ).find() | |
while (r.hasNext()) { | |
printjson(r.next()); | |
} | |
} catch (err) { | |
print(err) | |
} | |
print("\nAttempting to fetch & decrypt document with userid key u0001...") | |
try { | |
var r = db.people.find({"userid":"u0001"}).pretty() | |
while (r.hasNext()) { | |
printjson(r.next()); | |
} | |
} catch (err) { | |
print(err) | |
} | |
print("\nAttempting to fetch & decrypt document with deleted userid key u0002 (should fail)...") | |
try { | |
var r = db.people.find({"userid":"u0002"}).pretty() | |
while (r.hasNext()) { | |
printjson(r.next()); | |
} | |
} catch (err) { | |
r = err | |
} | |
print(r) | |
print("\nDemo complete.\n\n") | |
quit() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ mongo --nodb --shell hello.js | |
MongoDB shell version v4.4.2 | |
type "help" for help | |
Beginning FLE demo... | |
Setting up an FLE Mongo client session... | |
Wiping FLE demo sandbox... | |
Creating user-specific data keys... | |
UUID("cd570387-d0dd-4816-a957-578eee7fab0d") | |
UUID("8734d8dd-cd82-4e19-9ba5-b189d9a168f9") | |
UUID("ff7ebf5b-6774-4ff7-acc6-c79aa6532349") | |
Attempting to retrieve field keys... | |
{ | |
"_id" : UUID("cd570387-d0dd-4816-a957-578eee7fab0d"), | |
"keyMaterial" : BinData(0,"uEG8D6EGfBCpWrix/wtdaOpoDEqJY0Hm3pwlZmza4Wgv4CMSbCTB6cTkTrdEGjd8Gl/FzVzlzQ2RlW3G/Pn7cLkX06gB1uwlemZ8hIAtfM5jQ7YhWaYvtvw3Hn9EuOAoI2thjuocm5Er5Do2wasc2OK8gFCM9cqrEh5hUQ+nRBWrSZTlfo+BdZ9JhzW8HVQVSiLjkPHhm1iVDdRe6l2gOw=="), | |
"creationDate" : ISODate("2021-02-11T02:59:45.536Z"), | |
"updateDate" : ISODate("2021-02-11T02:59:45.536Z"), | |
"status" : 0, | |
"version" : NumberLong(0), | |
"masterKey" : { | |
"provider" : "local" | |
}, | |
"keyAltNames" : [ | |
"u0001" | |
] | |
} | |
{ | |
"_id" : UUID("8734d8dd-cd82-4e19-9ba5-b189d9a168f9"), | |
"keyMaterial" : BinData(0,"mB20SIAlOUcqwXVN06o0qV2KzofM2HZk5WftF+KB/YQPiLhuckd6VTo4m6/NE1Z9dLIVBxnftnqA9KD4O/4vfvEJ9ByXPiqo0BHX42BG2ODKtZJ+EKEVR72DG1ZjtHjChVF2febUC+uTwpBO7o14XYRihMgcCCt8Akc+UdBxWHzFO0jHcR9hdE07UTaDDoDBeR5JgMDnuRaXA09yTpT1Sg=="), | |
"creationDate" : ISODate("2021-02-11T02:59:45.543Z"), | |
"updateDate" : ISODate("2021-02-11T02:59:45.543Z"), | |
"status" : 0, | |
"version" : NumberLong(0), | |
"masterKey" : { | |
"provider" : "local" | |
}, | |
"keyAltNames" : [ | |
"u0002" | |
] | |
} | |
{ | |
"_id" : UUID("ff7ebf5b-6774-4ff7-acc6-c79aa6532349"), | |
"keyMaterial" : BinData(0,"d8tJ7FFihiPqoGRg8hPYeifWpB3iI8rLBZ4uUyjDM1J0dk4CrVwa8PcqvzrDYUkZHw7+8ggxINJxLFO6KCH52afxOJ0rS3KqQs2HIkGSc6ywDqndJeGd2n/md6RBcG1LCR50imSX+ZWwBhyJeGtRJ91bBGeLxmL9tlPgmdTj59es7M2oOaE7xrbmvFfpdb1mEehhsNOcROvqWl1XvCs8zA=="), | |
"creationDate" : ISODate("2021-02-11T02:59:45.544Z"), | |
"updateDate" : ISODate("2021-02-11T02:59:45.544Z"), | |
"status" : 0, | |
"version" : NumberLong(0), | |
"masterKey" : { | |
"provider" : "local" | |
}, | |
"keyAltNames" : [ | |
"u0003" | |
] | |
} | |
Setting server-side json schema for automatic encryption on `people` collection... | |
Attempting to insert sample documents with automatic encryption using per-user keys... | |
Result: WriteResult({ "nInserted" : 1 }) | |
Result: WriteResult({ "nInserted" : 1 }) | |
Dumping decrypted documents from `people`... | |
{ | |
"_id" : ObjectId("60249da12559abae59dfca34"), | |
"firstName" : "Grace", | |
"lastName" : "Hopper", | |
"userid" : "u0001", | |
"ssn" : "901-01-0001", | |
"dob" : ISODate("1989-12-13T00:00:00Z"), | |
"address" : { | |
"street" : "123 Main Street", | |
"city" : "Omaha", | |
"state" : "Nebraska", | |
"zip" : "90210" | |
}, | |
"contact" : { | |
"mobile" : "202-555-1234", | |
"email" : "grace@example.com" | |
} | |
} | |
{ | |
"_id" : ObjectId("60249da12559abae59dfca35"), | |
"firstName" : "Alan", | |
"lastName" : "Turing", | |
"userid" : "u0002", | |
"ssn" : "901-01-0002", | |
"dob" : ISODate("1912-06-23T00:00:00Z"), | |
"address" : { | |
"street" : "456 Oak Lane", | |
"city" : "Cleveland", | |
"state" : "Ohio", | |
"zip" : "23210" | |
}, | |
"contact" : { | |
"mobile" : "202-123-1234", | |
"email" : "alan@example.net" | |
} | |
} | |
Deleting user data key for userid: `u0002`... | |
{ "acknowledged" : true, "deletedCount" : 1 } | |
Establishing a new Mongo client session (to flush cached keys)... | |
Dumping updated key vault... | |
{ | |
"_id" : UUID("cd570387-d0dd-4816-a957-578eee7fab0d"), | |
"keyMaterial" : BinData(0,"uEG8D6EGfBCpWrix/wtdaOpoDEqJY0Hm3pwlZmza4Wgv4CMSbCTB6cTkTrdEGjd8Gl/FzVzlzQ2RlW3G/Pn7cLkX06gB1uwlemZ8hIAtfM5jQ7YhWaYvtvw3Hn9EuOAoI2thjuocm5Er5Do2wasc2OK8gFCM9cqrEh5hUQ+nRBWrSZTlfo+BdZ9JhzW8HVQVSiLjkPHhm1iVDdRe6l2gOw=="), | |
"creationDate" : ISODate("2021-02-11T02:59:45.536Z"), | |
"updateDate" : ISODate("2021-02-11T02:59:45.536Z"), | |
"status" : 0, | |
"version" : NumberLong(0), | |
"masterKey" : { | |
"provider" : "local" | |
}, | |
"keyAltNames" : [ | |
"u0001" | |
] | |
} | |
{ | |
"_id" : UUID("ff7ebf5b-6774-4ff7-acc6-c79aa6532349"), | |
"keyMaterial" : BinData(0,"d8tJ7FFihiPqoGRg8hPYeifWpB3iI8rLBZ4uUyjDM1J0dk4CrVwa8PcqvzrDYUkZHw7+8ggxINJxLFO6KCH52afxOJ0rS3KqQs2HIkGSc6ywDqndJeGd2n/md6RBcG1LCR50imSX+ZWwBhyJeGtRJ91bBGeLxmL9tlPgmdTj59es7M2oOaE7xrbmvFfpdb1mEehhsNOcROvqWl1XvCs8zA=="), | |
"creationDate" : ISODate("2021-02-11T02:59:45.544Z"), | |
"updateDate" : ISODate("2021-02-11T02:59:45.544Z"), | |
"status" : 0, | |
"version" : NumberLong(0), | |
"masterKey" : { | |
"provider" : "local" | |
}, | |
"keyAltNames" : [ | |
"u0003" | |
] | |
} | |
Attempting to fetch & decrypt document with userid key u0001... | |
{ | |
"_id" : ObjectId("60249da12559abae59dfca34"), | |
"firstName" : "Grace", | |
"lastName" : "Hopper", | |
"userid" : "u0001", | |
"ssn" : "901-01-0001", | |
"dob" : ISODate("1989-12-13T00:00:00Z"), | |
"address" : { | |
"street" : "123 Main Street", | |
"city" : "Omaha", | |
"state" : "Nebraska", | |
"zip" : "90210" | |
}, | |
"contact" : { | |
"mobile" : "202-555-1234", | |
"email" : "grace@example.com" | |
} | |
} | |
Attempting to fetch & decrypt document with deleted userid key u0002 (should fail)... | |
Error: Invalid keyID. | |
Demo complete. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment