Chapter 11 of 15

Security

People are jerks, and also monkeys

Security is one of the things that senior developers mention most often as something junior developers know nothing about. People walk in the door and do something dangerous, and the senior devs are like “don’t do that, for the love of God — we worked out twenty years ago that that’s dangerous.” And the new developer is like: “What? Nobody told me. There was no course on this.” It’s also the number one thing people are worried that agents will be bad at, because if you don’t tell your agent to be secure, it will build things that are easy but insecure.

Security is a process, not a phase

The first principle: you can’t add security later. Security is not a phase in your development process. It’s not “we spent six weeks building the product and then a week securing it.”

Security is a process where every time you make a decision, you consider how that decision could go wrong. You imagine what would happen if somebody decided to abuse the thing you just created. There’s no way to bolt it on afterward, because after six weeks of building you’ve made a thousand decisions. You won’t remember them all, and if the very first decision was insecure, the other 999 built on top of it are probably broken too.

Security will slow you down. That’s the cost. But it’s absolutely necessary.

Never trust the user

The second principle: never, ever trust the user. The user is frequently malicious and always capable of making mistakes.

One of the most common ways people talk themselves out of security is: “Oh, this is just an internal application. Nobody outside the company will ever use it.” There are many ways for an attacker to hijack the browser of someone inside your network, and as soon as they have access to that browser, they have access to all the internal applications. There is no such thing as an app with only friendly users.

Even if you’ve authenticated the user and you’re sure they are who they say they are — they might be a botnet that stole that user’s credentials. Even if the real user is logged in, you should think about what happens if they do something weird. If your financial application doesn’t consider what happens when someone tries to send all their money to an unexpected destination with no warning, that’s a problem waiting to happen.

Defense in depth

The third principle: there is no such thing as perfect security. No single mechanism will protect you from everything. The only strategy that works is to add layers.

Layer after layer after layer of security, such that it takes so long to get through them all that the attacker either gets bored, decides it isn’t worth it, or — critically — you notice it happening while they’re still working through the layers. If you have eight layers of security and someone has breached three of them, you have a chance to respond. If you had only one layer, your database was probably already stolen by the time you noticed.

Protect your network. Protect your database. Hash your passwords with proper salting.

Input validation

If there’s one concrete takeaway from the security section, it’s this: validate your inputs. Input validation is about codifying your distrust of users into your code.

Define what you’re expecting to receive, and reject anything that doesn’t match. If you have a username field, check that the thing someone typed looks like a username and not like a credit card number. This is not a hypothetical example — it’s a regular problem on websites that accept payment. Someone types their credit card into the username field, and then files a bug: “Everyone can see my credit card number!” That’s your fault, because you should have checked.

Validate on the client side in JavaScript before they send it to you — for a good user experience. Then validate again on the server — because you can’t trust that the thing contacting your API is actually your web app. Someone will sniff your packets, find your API endpoint, and send whatever they want directly. If your API accepts unsafe input but your web app doesn’t, your API is unsafe.

Always try the “evil paste” test. There’s so many things people can paste in! My favorites to check are:

  • Nothing: if the field is empty, does your app crash? You won’t believe how often the answer is yes.
  • O’Reilly: if somebody pastes in a quote or (god help you) a fancy curly quote, does your app still work?
  • Emoji: what happens if it’s an emoji, especially an emoji with a modifier like skin color? This is how you learn about unicode boundaries.
  • Moby Dick: what happens if it’s the entire text of Moby Dick? You need to have a size limit somewhere.