Here’s an update on our site and app security. I feel it’s appropriate for a preview-stage startup with the kind of business data we hold. Security is always important, and we are already using better security than I’ve enforced in the past for fully-released consumer entertainment apps. This covers:
First of all, our site is now securely TLS-enabled at https://preview.cathylabs.com. I had never gotten a cert myself from LetsEncrypt before and found that process very easy. I’ve made a note in Asana to renew it in 3 months before it expires.
While doing that, we’ve made sure that our site cannot be accessed via HTTP over an insecure connection. There’s a feature in Django which I’ve enabled to make sure that we always redirect insecure requests to a secure connection. We also enabled HSTS, which asks users’ browsers to confirm that all future traffic from this site is HTTPS. Finally in a belt-and-suspenders-and-staplegun third check, Google Auth won’t accept authentication requests except from the secure URL and won’t redirect successful authentications except to the secure URL.
Besides HTTPS requests and responses, email notifications are the only other communications sent by our service. Those email notifications do not include any customer data, whether they’re to customers or to ourselves to notify us of admin action needed.
Django comes with built-in CSRF (Cross Site Request Forgery) protection enabled by default in forms. I love it when the secure thing is part of the platform I use. I’ve used this consistently throughout the product, of course. We also use Host header validation to make various Host spoofing attacks impossible.
I’ve followed all the advice in Django’s security recommendations and followed all the steps in their deploy checklist, including running manage.py check --deploy, and there are no issues identified by this tool in our production Django settings.
In short, huge props to the Django community for helping Web services to be secure by default.
Our customer authentication is via Google. Because we use a 3rd party OAUTH service, we have no passwords to store for customers.
We do have one account that is password-protected, and that is an administrator account. I randomly generated a long password for that and will tell you if you need to know it (but then I’ll have to kill you — and where would our startup be then?). That account login works with Django’s built-in authentication module, which again is secure by default. It hashes the password in storage so that it would be pretty hard for an attacker to get.
Once we have customer data, we store it in only two places: in our structured database and in S3. Database access is restricted to our app. See Heroku hosting below because that’s relevant: it’s set up by Heroku to link to our app only. I’ve done nothing to break or compromise that.
For Amazon S3, we had to setup and configure it to be secure. Our business account with AWS is only accessible by you and me and the app. We have properly set up IAM accounts for each of those so we could always revoke one agent’s access. Our S3 bucket permissions are configured conservatively so that only our service and our accounts can list files. Of course, a customer sometimes needs to download a file, which we only allow for completed compensation reports. We do this by first having the customer log in securely to our site, then get a temporary key to get the file directly from S3.
One always has to think this through: even if database access is configured to be secure, what could somebody do to change that configuration? Could somebody change our code itself or our security settings? Could somebody authorized then do something unauthorized? That’s why this last section is a little meta, a little one-step-removed.
Our code is in GitHub. If GitHub were compromised that would be bad — for a lot more people than us. Our repository is private, and it doesn’t contain our most important keys anyway. If somebody were to try to alter our master codebase to do bad things, we’d see those changes.
Our hosting service is Heroku (Salesforce). Again, if Heroku were compromised that would be bad for a lot more people than us. Heroku has many, many customers who are far more “interesting” than we are, and they have to host those customers securely too. I have more confidence in a hosting service managing my site and db than doing it myself at our tiny startup scale. For example, it’s their job to keep up with security alerts with the core Web, storage, and communication software they use. And they do.
Our Heroku passwords could in theory be used to compromise the whole service. If that happens that would be bad. Don’t tell anybody your Heroku password, and make sure it’s not easy to guess. I know you’ve done that already, because you use long generated passwords and a password manager. But even if the worst were to happen, if somebody were to deploy compromised code to Heroku, they wouldn’t be able to do so without our knowing — Heroku logs this kind of access all the time.
As we’ve discussed, you and I are doing all our customer service work for now. Because of that decision, we punted on having monitored access for customer service. When we have customer service employees, we’ll need a restricted and logged access interface. That interface needs to keep our own employees from getting data they don’t need to use, and log all data access. But since it’s only you and I so far, and we both have the keys to the entire site, that’s moot right now.
As we keep developing site features, I’ll keep reminding you of the work we need to do to secure our customer data. That work takes time, so we’ll need to talk through each feature to ensure that we do it right.
It’s pretty hard to go in depth and custom handle all these threats. That takes somebody who’s dedicated some serious career time to security only. But that in itself is why I’ve made standard choices, used standard tools, and followed standard, community-reviewed advice.
Thanks for walking me through all this. We’re both focused on keeping our customer data safe, and I ❤ your approach re: standard choices. A few thoughts: