Hello all,
I got an assignment for my graduation project(the college system chose the project) to create a secure web-based system for hospitals that has some security features such as access control, role-based, secure APIs, …and so on.
I started working as soon I got home for COVID-19 quarantine. And I want to leave my touch in the project so I created a new feature that may help to keep the data at a third untrusted party and allow to recover the data in case of a Ransomeware attack with a masterkey or with multiple Subkeys. Now lets get started!
Technical Details
Now let’s assume the following:
You have a records of 10k patients in a mongo database with the a structure that looks like this
patientRecord = new Schema( {
unique_id: Number,
caseType: String,
assignedTo: Number,
fullInfo: String,
phoneNumber: String,
priority: Number,
drugs: String
},{ collection: 'records' });
Record = mongoose.model('Record', patientRecord);
What we need to do is to backup this collection and send it to a third party server that is full secure, but we don’t trust the owners on the collection content. So, how can we make this possible?
We are going to use symtric key encryption in order to keep the content safe. As we need a masterkey to do this we are going to explain this later in this article.
System Implementation
We need to create two procedures in our system: one to create an encrypted backup and another to decrypt the data.
Create backup:
- Export data from MongoDB as a CSV file.
- Generate master key/sub keys.
- Encrypt the exported CSV file with AES-256 algorithm.
- Send the encrypted file to a remote server with an HTTPS request.
Recover backup:
- Generate masterkey from subkeys(in case of the masterkey isn’t avaliable).
- Upload the encrypted CSV file to the server.
- Use AES-256 decryption function with the generated masterkey.
- Import the decrypted data as collection to MongoDB.
Coding the Procedures
1- Export data from MongoDB as a CSV file:
You can use mongoexport command to do this just like this:
/*
DB: eHospital
Collection: records
Type: CSV
output file: /opt/backups/records.csv
*/
var spawn = require('child_process').spawn,
ls = spawn('mongoexport', ['--db', 'eHospital', '--collection', 'records', '--type', 'csv', '--out', '/opt/backups/records.csv']);
DON’T ALLOW USER TO SET THESE FROM IT’S SIDE FOR SECURITY REASONS
2- Generate master key/sub keys:
I wrote a JS script that do this task. You can find it here
What I did here is to generate 4 random values(sub keys) and XORing them in order to get 2 sub keys and 1 master key. We should distrubute the keys before next step. Giving the manager the master key, 2 trusted employees the 2 first sub keys, and the less-trusted employees the next 4 keys.
Here is the internal logic of my script:
3- Encrypt the exported CSV file with AES-256 algorithm:
We will use Crypto to do this task.
var crypto = require('crypto');
var fs = require('fs');
var masterKey = ''; // Put your masterkey here
try {
var data = fs.readFileSync('/opt/backups/records.csv', 'utf8');
var encryptedText = encrypt(masterKey, data);
fs.writeFile('/opt/backups/records.csv', encryptedText, function (err) {
if (err) throw err;
console.log('Done!');
});
} catch (e) {
console.log('Error:', e.stack);
}
// Using AES as encryption algorithm
function encrypt(key, text) {
var cipher = crypto.createCipher('aes-256-cbc', key);
var crypted = cipher.update(text, 'utf8', 'hex');
crypted += cipher.final('hex');
return crypted;
}
4- Send the encrypted file to a remote server with an HTTPS request:
In this task we will use Request to send our encrypted file to a remote server.
var request = require('request');
DB:const options = {
method: "POST",
url: "https://REMOTE-SERVER/file",
port: 443,
headers: {
"Content-Type": "multipart/form-data"
},
formData : {
"image" : fs.createReadStream("/opt/backups/records.csv")
}
};
request(options, function (err, res, body) {
if(err) console.log(err);
console.log(body);
});
Now we finished the first part let’s explain the 2nd part.
4- Generate master key from subkeys(in case of the master key isn’t available):
I wrote a JS script that does this task. You can find it here
The logic of the script is to get the 4 or 2 subkeys and XORing them to get the master key. BTW, the order of the keys is not important since we are XORing.
5- Upload the encrypted CSV file to the server:
I used HTML form to do that and express as a backend framework
var express = require('express');
var fileUpload = require('express-fileupload');
var app = express();
app.use(fileUpload());
app.post('/upload', function(req, res) {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
let sampleFile = req.files.mybackup; // file name in html form is mybackup
// Use the mv() method to place the file somewhere on your server
sampleFile.mv('/somewhere/on/your/server/records.csv', function(err) {
if (err)
return res.status(500).send(err);
res.send('File uploaded!');
});
});
My project page is a little different since I am calculating the key in the server.
6- Use AES-256 decryption function with the generated master key:
Same as step 3
var crypto = require('crypto');
var fs = require('fs');
var masterKey = ''; // Put your masterkey here
try {
var data = fs.readFileSync('/opt/backups/records.csv', 'utf8');
var decryptedText = decrypt(masterKey, data);
fs.writeFile('/opt/backups/records.csv', decryptedText, function (err) {
if (err) throw err;
console.log('Done!');
});
} catch (e) {
console.log('Error:', e.stack);
}
// Using AES as decryption algorithm
function decrypt(key, text){
var decipher = crypto.createDecipher('aes-256-cbc',key)
var dec = decipher.update(text,'hex','utf8')
dec += decipher.final('utf8');
return dec;
}
7- Import the decrypted data as a collection to MongoDB:
You can use mongoimport command to do this just like this:
/*
DB: eHospital
Collection: records
Type: CSV
File: /opt/somewhere/on/your/server/records.csv
*/
var spawn = require('child_process').spawn;
ls = spawn('mongoimport', ['--db', 'eHospital', '--collection', 'records', '--type', 'csv', '--file', '/somewhere/on/your/server/records.csv']);
Notes
- Don’t forget to download modules from NPM before using the codes.
- This code may not work just by throwing them into
.js
file, you have to modify them. - The system can be much harder to break, but we need to do more coding, I just wanted to explain the approach that I followed.
Conclusion
We can share a master key with multiple users in case of master key gets lost we can generate it from multiple subkeys.