On Tuesday evening I delivered a presentation to a fairly diverse group comprised of local IT business owners and staff – the largest of it’s kind in my city. The subject of it was incident response: hiring the right staff and educating existing staff, designing networks that reduce the impact of breaches, log correlation and malware analysis, etc. One point that I made, which visibly provoked deep thought throughout much of the audience, was that shifting infrastructure into the cloud moves our data further out of the reach of security controls and into the hands of potentially untrustworthy and incompetent 3rd parties. You may say: “well, duh”. Trust me, it too came as a surprise to me that this would cause distress for people, as in my mind it’s absolute common sense – but obviously not. The concern of outsourcing security was, however, one of the reasons that I chose to introduce a policy within my workplace that prohibits the transmission of confidential data (e.g. credentials) via email or SMS, as data retention and the security of cloud and telco services is at times somewhat questionable. So, you want to eliminate the storage of confidential information in any such outsourced services. Faced with having to devise a solution that is usable by even the most technically inept, I decided to build upon a concept already used by some online services: self-destructing, encrypted messages.
why reinvent existing services?
My main issue with them stems from my belief that any application that handles any form of a secret needs to be open source in order to be trusted as you must be able to ascertain:
- The code is free from vulnerabilities.
- The cryptography standards are suitable for the purpose.
- The application is not siphoning out secrets.
- The design is very basic and clean.
- The application is very usable in desktop, tablet and mobile browsers.
- Users can create notes that require only a link, require an additional 6-character key and/or perform all cryptography in the browser to ensure the message, link and key are never sent to the server in plaintext.
how does the service work?
Let’s create a case scenario with Alice and Bob. Alice wants to send a secret to Bob, so she visits DeadDrop:
Alice enters a message into the form:
If she forgets to enter some text into one of the fields, doesn’t complete the reCAPTCHA challenge or enters an invalid email address she get’s a pop-up (toast) on her screen informing her of this:
Once her entry is complete, she clicks on submit and see’s a nice animation as the request is processed:
If she checked “Require TFA” she’ll receive both a link and 6-character key to send to Bob:
Otherwise she’ll only need to send him a link:
In any case, the link contains two query strings:
- id: used to fetch the correct record from the database.
- key: used to decrypt the message.
The manually-entered TFA key is an extension of the key used to encrypt the message.
In this case Bob is a bit paranoid that his employer might be snooping in on his emails, so as the link is being sent to him via email, Alice sends him the TFA key via an out-of-band method: SMS.
When Bob visits the link he is requested to enter the TFA key:
Upon successfully entering it, he’s shown the secret message:
However, if he were to try and visit that link again, or if he were to enter the incorrect TFA key – he’d be shown an error message as the message either would not exist or would fail to decrypt:
Alice is also sent an email confirming to her that Bob has successfully opened the message and that it has been deleted from the database:
DeadDrop is an ASP.NET application, using SignalR for real-time server-client communication. The database platform is PostgreSQL, however only uses a single table – as that’s all that is required. Providing the host is trusted, all cryptographic operations are performed by a separate WCF service (CryptoService) that has several roles:
- Key Generation: Leveraging the MS RNGCryptoServiceProvider class, cryptographically secure random strings can be generated of variable length.
- Encryption: Inferno is used to perform AES256 CTR + HMAC-SHA384 EtM encryption. Messages are encrypted using a random key, and all other data (note descriptions and email addresses) is encrypted using a key and payload that is defined in the web.config of the service. EtM ensures that encryption is authenticated.
- Hashing: CryptSharp is used to generate salts, and hash keys using scrypt (for verification purposes when requesting a note).
In the case that the user does not trust the host and opt’s for in-browser cryptography, the standards are much the same:
- Key Generation:RandomSource.getRandomValues() ensures that sufficient entropy is used to generate random strings – typically where browsers fall short.
- Hashing: Whilst not specifically hashed in the browser, keys are stretched prior to transmission to the server by 86,000 rounds of PBKDF2 also using Forge. On the server they’re hashed the same as other keys using scrypt.
Lately I’ve been inspired by Google’s Material Design, which led me to stumble upon Materialize – an awesome framework for developing interfaces that adhere to it’s principles. Using it is incredibly easy (I cannot stress this enough), it’s responsive and it offers some pretty advanced features that would be quite difficult to implement from scratch. It obviously ties in quite nicely with reCaptcha v2 too!
Developing this application hasn’t really stretched my development ability at all, however working alongside our UX team has given me a newfound appreciation for putting serious thought into application usability. The design aspect of DeadDrop isn’t quite finished so it hasn’t passed the eyes of any of our clients yet (the true test), however our helpdesk staff have begun using it internally and I’ve received nothing but positive feedback.
You can view the source code on GitHub.