Skip to main content

Search...

Stop Inventing Your Own Encryption

Security built in from the start beats security bolted on at the end. Here is why threat modeling, defense in depth, and secure defaults change everything.

10 min read
Cover for Stop Inventing Your Own Encryption

Security by design means treating security as a core requirement throughout the software development lifecycle, not something addressed at the end of a project. Key principles include defense in depth (layering independent security mechanisms), using established libraries instead of building custom security technology, validating all incoming data at interfaces, applying secure defaults, and failing securely when components malfunction.

Key Takeaways

  • Security addressed in the last ten percent of a project produces ten percent security, making early involvement the only reliable path to a secure system.
  • Threat modeling is not a specialist-only exercise: any team can sit down, identify what is valuable in their system, and work out systematically who would want it and how they would attack.
  • Building custom security mechanisms, including encryption or authentication flows, introduces vulnerabilities that even expert-produced security technology routinely contains and that most teams lack the resources to find.
  • Failing to a secure state is a design obligation: when a component such as an audit trail or authentication service fails, the system must refuse to continue processing rather than default to an open position.
  • Testers are a natural forcing function for security awareness because questioning what can go wrong is their core skill, and involving them early surfaces bypassed security controls before they reach production.

Security is what you build first, not what you bolt on last

If you start thinking about security in the last ten percent of a project, you get ten percent security. That single line sums up why so many systems ship with weak protection: the work was deferred until almost nothing could change.

Security shares this fate with performance, resilience, and availability. Everyone agrees these qualities matter. Almost no one gives them attention early, because the plan is to handle them “eventually”. Eventually rarely arrives in good shape.

Making security a first-class concern means treating it as a design input, not a closing task. You decide how a system defends itself while you are still shaping it, when changing the structure is cheap.

Why teams keep treating security as an afterthought

Most software engineers carry only abstract security knowledge, and that gap pushes the topic to the end. They know the words: authentication, authorization, auditing. What to actually do with them on a real system is far less clear.

There is a real shift worth naming. Ten years ago, a talk on security drew five people, and all five already worked in security. Today the room fills with engineers who want to learn. The interest is there. The practical know-how often is not.

The other obstacle is a culture clash. When a team finally reaches out to security specialists, they meet very clever people who do nothing but security. Many came from an infrastructure background and know little about software delivery. They speak a different language, and they tend to bury a team under a list of mandatory, urgent, disaster-or-else demands.

That framing is misleading, because nothing in security is absolute. Everything is a balance. A team that hears only “do all of this now or fail” has no way to weigh effort against risk.

Design principles give engineers a usable starting point

Principles work because they are short enough to remember and broad enough to guide real decisions. Eoin Woods built a set of ten after noticing that existing collections were either too thin or impossibly large, ranging from eight or nine entries up to several hundred. All valid, none usable at the extremes.

The point of a compact set is to get people thinking about security earlier. You do not need to master cryptography to ask whether your design leans on a single point of failure. You need a handful of prompts that fit in your head while you sketch the architecture.

What follows are the principles that carry the most weight in everyday design work.

Defense in depth: never rely on one mechanism

Assume any single security mechanism can be defeated. Sophisticated attackers will break one piece of encryption or slip past one authentication system, and if that is all you had, they now have everything.

Nearly every mechanism has flaws, or gets applied with mistakes. So the goal is layered, independent protection. In the systems that governments defend, every layer has its own controls, and they interlock. Breaking into one level just hands the attacker the same problem again.

The aim is to make intrusion expensive. Each independent layer raises the cost, which is often the most realistic form of defense you can build.

Don’t invent your own security technology

Building your own security technology is far harder than it looks, and the trap catches experienced engineers. Plenty of capable developers read a book on encryption and decide to build their own password vault. The honest advice is: don’t.

The same caution applies to authentication and authorization. Integrating with something like OAuth looks straightforward, yet the right move is to find a library that already does it.

Here is the reason in plain terms. Security technology built by professionals still ships with flaws. The moment it is released, expert testers start beating on it, and they always find problems. The odds that your homegrown version has none are slim.

The first thing they do when they produce it, they start beating it with all those expert security testers start testing it immediately, and they always find problems with it. What are the chances that you’re going to have no problems with yours? It’s pretty slight. — Eoin Woods

Open source or closed source is context-dependent, and neither holds a monopoly on good security. Open source brings transparency and a crowd of independent researchers. For some markets, a closed-source product is genuinely the stronger offering. Ask the vendor the right questions and match the choice to what you are building.

Trust cautiously, especially what enters your system

Treat every input and every component as something to verify rather than accept. Network connections should not be made or accepted without authorization and authentication. That is the obvious half of the principle.

The subtler half is what you bring into the system. Data deserves suspicion, because many exploits work by feeding malicious data into a system that processes it without checking. The same goes for what you build from: open source libraries, commercial libraries, commercial platforms. How secure are they, and how would you know if one carried a zero-day?

Use secure defaults and fail securely

Default credentials are a long-running source of breaches. For years, Oracle installations shipped with powerful accounts and standard passwords, and the Scott/Tiger demo login could get you into almost any system. Oracle fixed that, but a great deal of cloud demo software and enterprise network hardware still arrives with default users and passwords unlocked. Convenient, and dangerous.

Failing securely is the other side of the same idea: when something breaks, the system must not drop into an open state. One database vendor, focused on performance, built a tamper-resistant audit trail and then had engineers disable auditing and keep processing when it filled up. A customer caught it in beta. The trade-off felt reasonable to whoever wrote it, and it was exactly wrong.

Modern versions of this problem are quieter. In a message-driven system recovering from a multi-component failure, does every part re-authenticate and refuse to process until end-to-end security is restored? Or does it start processing opportunistically because the security services were not ready yet?

How to push security into the design phase

Make security part of how the team works, not a note left on a whiteboard that nobody reads during daily delivery. Design principles guide individual decisions. They do not, on their own, make a team build securely.

For that, you need a secure software development lifecycle, which simply means doing the security work all the way through, not at the end. You do not have to invent one. OWASP offers a well-developed model, and several government bodies publish their own. Start from an established one and tailor it, the way every team tailors its lifecycle.

Why testers are the natural allies here

Testers already think about what can go wrong, which is exactly the mindset security needs. Architects try to think this way too, but they are human and often pinned under feature pressure or a single quality target like throughput for the next few sprints.

That is where a tester earns their keep. Noticing that a new feature appears to bypass a security control, and saying so, is precisely what testers should be paid to do. The team’s job is to support that question, not to make the tester feel unpopular for raising it. The right response is gratitude, because the alternative was shipping the mistake.

Getting testers into the team early changes this further. When the tester was the last person to see the software, the reaction was “oh my God, what have you done?” Involved early, they help prevent the problem instead of discovering it.

Abuser stories and threat modeling, kept simple

Bring security into the backlog as abuser stories: how would someone attack this system? That is a simplified entry into threat modeling, a technique that sounds complicated and is not.

Threat modeling is a structured conversation. The team sits down and asks what they have that is valuable, who would want it, and how they would attack to get it. The valuable thing might be data, a financial transaction, or an operation an attacker wants to enable or disable. Work through it methodically and don’t overcomplicate it, the way the real experts advise.

It mirrors how you reason about high availability. You list what could go wrong and what happens when each piece fails. Once you have done that, you know how robust the system really is.

Zero-days and dependencies need automation, not heroics

Staying current with components is a process problem you cannot solve by hand. In ecosystems with fine-grained dependencies, such as Node.js, the only realistic way to stay on top of known vulnerabilities is automated support.

Defense in depth matters here too. If you lean heavily on one component to keep you safe and that component has a zero-day, someone will exploit it. Spread the reliance so a single flaw does not open everything.

Zero-days are an intractable problem in part because there is a black market in them, which makes even knowing one exists difficult. You cannot close that gap entirely. You can know what is in your system and automate the watch for vulnerabilities in your open source components, which is where most of the exposure accumulates.

Loose interfaces are a security decision in disguise

Permissive APIs that let everything through are a security risk, not just a convenience. A landscape of applications wired together with lazy contracts lets an infection travel from system to system until it lands somewhere that matters.

The easy path is the lowest common denominator: make everything a string, reject nothing, translate later. The cost shows up when attackers craft malformed strings that the string processor accepts as fine, then cause a failure when something else interprets them. That can be a denial-of-service, or something more deliberate that ends in remote command execution.

Strongly typed interfaces are the trade-off. They take longer to build, flex less, and are harder to evolve, which is why people reach for loose ones. The warning still holds: the moment you accept large amounts of unvalidated data, you have a potential security problem, so validate carefully.

Share this page

Related Posts