Installation steps
Project setup
- Create a google spreadsheet where the encrypted submissions will go.
- Rename the first tab in the spreadsheet Sheet1 => "Responses"
- From the Spreadsheet url in your browser copy the identifier between
/spreadsheets/d/
and/edit
(note: without the '/'s). It will be ~44 characters including numbers, letters and underscores. This is theSPREADSHEET_ID
configuration variable, we'll need it later. - Choose menu: Tools -> ScriptEditor. Name the script (anything)
- Choose menu: Publish -> Deploy as webapp. Set Execute As -> spreadsheet owner and Who has access -> "Anyone, even anonymous"
- Press Deploy. Copy the url for the app. It will look like: 'https://script.google.com/macros/s/....../exec'. This is the
SUBMIT_URL
config variable, we'll need it later. - Generate a Private/Public key pair as described in the jsencrypt project. 2048-bit keys are presumed in the example in this README.
- Note: Generating keys on an air-gapped laptop is recommended, along with building and running the decrypt section of it project. You could use an online laptop to build the wider project (see step #8) and run the encrypt section of the project.
openssl genrsa -out rsa_2048_priv.pem 2048
openssl rsa -pubout -in rsa_2048_priv.pem -out rsa_2048_pub.pem
Building the encrypt tool
- Run
npm install
if you haven't done so to load the node/webpack modules. - We'll assume you generated/copied the public key to the root of the project to a file
rsa_2048_pub.pem
. This isPUBLIC_KEY_FILE
in the config. We now have three variables to input to our build command. Run the following command all on one line:
SPREADSHEET_ID=<YOUR SPREADSHEET_ID> SUBMIT_URL=<YOUR SUBMIT_URL> PUBLIC_KEY_FILE=<RELATIVE PATH TO PUBLIC_KEY> ./node_modules/.bin/webpack
ONLY if you are on the laptop with the private key, then you should also include PRIVATE_KEY_FILE=./rsa_2048_priv.pem
just after the public key file variable is set.
Example of what the full command might look like:
SPREADSHEET_ID=20L466aa6uYM_YSbezDI0lkrXduKtB6eaM4YXSGJ7w78 SUBMIT_URL=https://script.google.com/macros/s/AKfycaz7sP8Gkr7xD7AGZSJUnOtrVi2vwxbgUFl05RAX4WqFUhH9dVI/exec PUBLIC_KEY_FILE=./rsa_2048_pub.pem PRIVATE_KEY_FILE=./rsa_2048_priv.pem ./node_modules/.bin/webpack
- The built files will be in a directory called
dist/
- Copy the contents of the file
./dist/encrypt/Code.gs
to the Google Script Editor (Tools -> ScriptEditor)- Press Save (icon at the top)
- Choose menu: Publish -> Deploy as webapp. Choose 'New' in project version and click 'Update'
- On your first deploy, Google may prompt you to accept permissions for read/write access to spreadsheets -- follow the prompts.
- Copy
./dist/encrypt/index.html
to your user-facing website. If you are using a form generated from a CMS or platform like Squarespace, copy the javascript in./dist/encrypt/main.js
and paste it inline below the form contents - Time to test your encrypted form. In development you can use the quick-startup process in the Develpment section below and then visit http://localhost:8000/dist/encrypt/
Decrypting
- Visit the google spreadsheet and File->Download as CSV
- Copy that to the air-gapped laptop by USB or wherever you have the decrypt.html file
- In a web browser File->Open ./dist/decrypt/index.html wherever it lives on the computer
- Click upload, and choose the downloaded csv file -- the browser will display the decrypted contents
Development
-
For a quick startup,
python -m SimpleHTTPServer
from this directory.
-
When you have made changes, run
webpack
(possibly as./node_modules/.bin/webpack
) which deploys changes in ./src to ./dist however the project tracks the ./testdist directory There are some important environment variables that control webpack's build.
Browser compatibility
Webpacked config still uses some 'old-modern' javascript browser features. This project targets compatibility with Internet Explorer 10+
Features used for the encrypt part:
- btoa (Base64 encoding and decoding)
- xmlhttprequest2 fails for IE9, but IE10+ is ok. Note that Google Script Api did not always support x-domain requests, but they appear to now.
- cors xmlhttprequest requires polyfill for IE9, but IE10+ is ok
- querySelector We avoid jQuery as a dependency for universal deployment but thus need querySelector
- Array.forEach began support in IE10
Features avoided:
- Promises are not available in pre-Edge Trident Internet Explorers including 10-11
- TextEncoder is unavailable in IE 10-11, so we add a polyfill
- Arrow functions are unsupported in IE 10-11
- Const/Let declarations are unsupported in IE10
Multiple Key support
Both the PRIVATE_KEY_FILE and PUBLIC_KEY_FILE can support multiple keys.
For the PRIVATE_KEY_FILE, just append the keys together (leaving the begin/end ------BEGIN RSA.....----
lines in).
For the PUBLIC_KEY_FILE, the first key is the default. After that, subsequent keys should be prefixed with a line like
OPTION=State:CA
Which means if someone fills in field with name="State" and the value is "CA" then we should use this public key.