Upload Files Securely to Google Drive

Trevor Foskett
Virtru Technology Blog
4 min readNov 25, 2019

Encrypt and Upload to Drive with the Virtru SDK.

Photo by Gareth Davies on Unsplash

My colleague Chad Sigler recently wrote an article about integrating the Virtru SDK with Boto3 to upload encrypted files to AWS S3. Chad’s an actual, real developer — the type of guy who works with S3 buckets a lot — so this was an ideal project for him. I, on the other hand, spend most of my time in the Google ecosystem, as do many of the organizations I work with. These orgs frequently use Google Drive as their main cloud storage service, so with that in mind,I set out to see if I could build some similar encrypted upload functionality for Google Drive. Chad’s already covered Python encrypted uploads, so we’ll build this Drive tool with Node.js.

Getting Started

Google provides an API that we can use to upload content to Google Drive, either one file at a time or in bulk. To get plugged into this API, we can copy directly from Drive’s Node.js Quickstart. In addition to the Drive API, this Quickstart also takes care of Google authentication into your organization’s Drive environment. You’ll need to run this quickstart inside your working directory first in order to generate your Google credential files. These will be stored in the /.google/ directory.

This quickstart is used to simply list existing files in Drive, so we’ll need to add some functionality to upload new content into that drive. Luckily, there’s some sample code we can copy for that too:

Creating a simple upload to Drive.

All we need to do is remove the code in the “listFiles()” function and replace it with the code block above, then remove “.metadata.readonly” from the value of the SCOPES variable, and we have a working upload client:

Change the Quickstart to upload rather than list files.

Adding Encryption

Now we need to add the Virtru SDK to our code to encrypt the files before we upload them to Drive. Knowing we’ll likely want to upload multiple files at once, we can borrow from Virtru’s How to Encrypt All Files in a Directory guide:

Virtru SDK: How to Encrypt All Files in a Directory

Connecting the Pieces — Encrypt and Upload

Now we have all the functionality we need; we just have to tie the two pieces together. Since the Google auth code I copied used “listFiles()” as a callback function, we can just create a new function to call instead and insert it in listFiles()’s place. I created “virtruStart()” which begins the process of reading files in the input directory, then subsequently calls the main “encrypt()” function on each.

Load Google credentials, then call virtruStart to begin the encryption process.
The encryption function called by virtruStart.

With the file encrypted, we can now upload to Drive. To do so, we can simply add the upload code to the main encrypt function and pass the encrypted data directly into the drive.file.create() operation:

Adding upload functionality to the encrypt function.

…and that’s it! The full script (which you can view on GitHub) now parses the input directory, encrypts each file, and uploads to Google Drive as a .tdf3.html file.

But — assuming we may at some point want to retrieve these files — we need to write a corresponding decrypt & download function.

Get Your Stuff Back — Decrypt and Download

For the most part, we’ll just need to run these operations in reverse, with a few key differences:

First, Drive only allows download by file ID rather than filename. To get the ID, we need to list the all the encrypted files in Drive by name and ID, then create the download request for each ID:

Search for encrypted files in Drive, then download by ID.

We also can’t download encrypted content directly into the Virtru decryption client; we need to store these in /tmp where we can access them later for decryption:

Set the download location for the stream data to /tmp.

Finally, there are a few considerations we need to make in case there are multiple files of the same name. Specifically:

  1. Two encrypted files of the same name will not have the same set of encryption keys. We need to ensure that the Virtru decryption client is using the correct keys for that specific file, or else the decryption will fail.
  2. If a filename already exists in the ./decrypt-out directory, we don’t want the decryption client to overwrite that file as it decrypts the newest version and writes it to disk. Instead, we should have additional versions added to the directory with a count in the filename.

To solve the first issue, we can add a unique identifier to each downloaded — but still encrypted — file before storing it the /tmp directory. I just used Math.random() to add a random string to the end of each filename, but you can use whatever mechanism you like.

Assign unique filename and store to /tmp.

To prevent overwriting of preexisting files, we need to include some logic in the decrypt function to first check if a file of that name exists, and if so, add a count to the filename before writing. So if “filename.docx” already exists, we’ll decrypt the new file as “filename (1).docx”, etc.

Check if filename exists and if so, count up for files of same name.

And now we’re ready to download and decrypt files! You can check out the full script here.

Make It Your Own

For this project, I only used one input folder. However, you could certainly expand this to iterate through a series of subfolders and upload to a similar directory structure in Drive. You’re also not limited to Drive — you can insert the Virtru SDK into the upload process to any cloud storage service. If you have a service you’d like to see a reference implementation for, let us know in the comments!

About Me

I’m a Solutions Engineer at Virtru, where I work with our customers to identify areas of their environments where additional data protections are needed, and develop solutions using Virtru’s Data Protection Platform & SDKs to meet those needs. Our developer platform is built on our SaaS Key and Policy Infrastructure to support data security, audit, and control using the open standard TDF3.

--

--