Skip to main content

Command Palette

Search for a command to run...

Should You Trust Your Developers?

Why "Secure by Default" needs to be the new normal.

Updated
9 min read
Should You Trust Your Developers?
A

I am a Lead Consultant at Cognizant Servian, with a diverse industry background ranging from startup scale embedded IOT and Industry 4.0 through to enterprise scale Edutech companies and utility providers. My expertise lies in cloud native, secure software development, with a keen interest in exploring emerging technologies.

In today’s digital landscape, security remains "job zero" of every team. However, ensuring security effectiveness requires us to enable teams to integrate secure infrastructure and code practices seamlessly into their development processes. This pivotal balance safeguards our systems and allows teams to amplify their focus on value-driven tasks instead of grappling with compliance intricacies.

Let us explore the landscape of security enablement and see how methodologies such as automation, push-down, and platform engineering can securely accelerate delivery velocity.

Can’t We Just Shift-Left?

There’s been a trend of shifting the responsibility of site reliability engineering, DevSecOps, and security onto development teams. I agree with this trend! Teams should be fully accountable for the changes they produce and own the process from end to end. However, I am concerned about the shift of responsibility onto the development team without the enablement of the development team.

To illustrate this point, we will use three scenarios: a siloed delivery model, a pure shift-left approach, and a platform-enabled shift-left team. Let’s start with the siloed delivery model and explore the amount of time spent by each team:

In this model, our developers can spend a lot of time on development efforts, as they can offload some of the cognitive load of ensuring that concerns such as security, SRE, and DevSecOps are considered. This might seem all well and good, but it raises some interesting questions:

  • Who is responsible for getting the code into production?

  • Who is ultimately responsible for these initiatives? Is it:

    • The team that implemented it.

    • The team that checked it.

    • An overarching managerial function.

  • If less consideration is given at the implementation phase, is our response mainly proactive? Or are we more likely to see reactive changes from the check phase?

  • Are these teams going to get along? These gatekeeper functions prevent the development team from meeting its goals by causing rework. By offloading responsibility, the development teams create more work for the gatekeeper teams. This can lead to an adversarial relationship.

This model causes issues with delivery, so let’s shift the responsibility for all of these functions to the development team. We’ll take a look at our time split in development teams now:

The development team gets to own all of its outcomes but has significantly increased cognitive load. The context they need to consider has expanded, and the expertise that used to be provided by the dedicated team now needs to be encapsulated in the development team. This is where the title of this post comes from. Should you trust your developers to do this? For example, the security team’s primary remit was to ensure secure artifacts. We are now trying to fulfill that same function with a fraction of the development team's attention. It doesn’t come down to a matter of trust. We cannot expect the same performance from a team that does not provide the same level of care. In trying to simplify our delivery, we have only accomplished two outcomes:

  • Our artifacts are less likely to be secure.

  • Our development team’s internal delivery velocity has been reduced.

Neither of these were the outcomes we were hoping for.

We’ve made two assumptions in the above model:

  • Compliance requires the correct, active decisions. Non-compliance is the result of inaction.

  • The development team is fully responsible for providing compliance.

Secure by Default

We often hear the term “Secure by Design” used as the pinnacle of security systems thinking. But let’s break down the 7-stage Software Delivery Life Cycle (SDLC):

Security only included at the design phase of the SDLC.

When we look at Secure by Design practices, these are typically the phases I see organizations targeting. The argument could be made that this also extends to implementing the design. However, security beyond the scope of design is rarely considered at this point.

Let’s look at our assumption from earlier:

  • Compliance requires the correct, active decisions. Non-compliance is the result of inaction.

What if we invert this dependency:

  • Non-compliance requires active decisions. Compliance is the result of inaction.

The more active decisions we can remove from the path to compliance, the less cognitive load our developers need to carry. We do this through the production of enabling artifacts. If we look at our first model again, let’s move our specialist teams upwards in the process:

The development team still has responsibility for the artifacts' compliance, but we use upstream teams to help reduce the cognitive load. We can undertake many processes to make this happen, but let's look at one example: an express application.

import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import { loadOrigins } from './loadOrigins';
import { authMiddleware } from './authMiddleware';

const app = express();
app.use(helmet())
app.disable('x-powered-by')

const secureRouter = express.Router();

const insecureRouter = express.Router();

var corsOptions = {
    origin: function (_, callback) {
        loadOrigins(function (error, origins) {
            callback(error, origins)
        })
    }
}

secureRouter.use(cors(corsOptions));
secureRouter.use(authMiddleware({}));

insecureRouter.use(cors(corsOptions));

app.use('/secure', secureRouter);
app.use('/open', insecureRouter);

// ... Routes go here

app.use((_req, res, _next) => {
    res.status(404).send("Sorry can't find that!")
})

app.use((err, _req, res, _next) => {
    console.error(err.stack)
    res.status(500).send('Something broke!')
})

Here is everything our dev team needs to remember to do to be secure, and this is a simple, generic example! Not to mention, we still need to create the loadOrigins function and the authMiddleware function. Different teams will likely store the data for the loadOrigins function in multiple places, including env files, DBs, or even hardcoded. Throughout our codebase, we will likely have multiple implementations of this.

If we packaged all this up into a library alongside the security team to ensure that we create some sensible defaults for all of these use cases, then it might look something like this:

import { makeRouters } from "@myorg/express"

let { secureRouter, insecureRouter } = makeRouters()

// ... Routes go here

app.use((_req, res, _next) => {
    res.status(404).send("Sorry can't find that!")
})

app.use((err, _req, res, _next) => {
    console.error(err.stack)
    res.status(500).send('Something broke!')
})

Note a few key elements in this example:

  • There is less chance of misconfiguration.

  • Baseline security defaults are set in a library.

  • Through object destructuring, each app uses the same terminology for the different levels of security guarantee.

We’ve given our developers the tools needed to succeed. But we can still push the concept further:

  • We could make calls to the underlying express app constructor a code smell in our static analysis tool of choice.

  • We can implement automatic dependency management in our repositories to ensure apps comply with the latest security library implementation.

  • We can package other common express extensions or canned configurations into our library.

The result of these improvements is that insecurity shifts from a failure to implement the controls correctly through passive ignorance to an active choice by the development team to be non-compliant. This is what we mean by building platforms that are secure by default. Only by ignoring or specifically circumventing controls can we have non-compliant systems.

Even though our example is simple, contrived, and maybe a little obvious, it sets the tone for what we should do at every stage of our SDLC. What zero overhead defaults can we introduce to improve compliance with security through passive actions on behalf of the development team?

Eventually, we want our SDLC to look like this:

Security included at every stage of the SDLC.

At each stage, we follow compliant security practices by creating sensible defaults rather than increasing the cognitive load on our developers.

Getting Buy In

Those responsible for a security budget face the problem of convincing others that security spend is worth it and that your new security initiatives won’t impact the business's operations while still magically making it more secure. Secure by default concepts unlock a compelling conversation regardless of your existing security process.

Let’s look at the two models we often see: security on the critical path and pure shift-left teams. We’ll frame the conversation without changing security processes, but remember that the most scalable goal is the transition to shift-left teams with enabling defaults!

Security on the critical path: The key metric we want to drive here comes from flow engineering; we want to optimise the percentage of work that arrives at the security review stage that is complete and accurate. Each iteration of the security process limits:

  • The rate at which we deploy.

  • Demands on constrained resources.

  • The cultural fit between security and development by creating an adverserial relationship.

We optimise the percentage complete and accurate metric through secure defaults provided by artifacts and automation. This reduces the cognitive load on both the developers and the security team, enabling higher throughput and security compliance. We can now ask ourselves the question, are our defaults and guardrails enough for us to fully enable our teams to own both feature and security outcomes?

Pure shift-left teams: There are two constraints on pure shift-left teams:

  • The requirement for enough security expertise in each team to understand the security controls needed.

  • The standardisation of security controls across teams.

This usually results in a large portion of the security budget spent on training, documentation, standardisation, and security reviews. When we reduce the control surface that developers need to interact with to be secure and provide automated guidance, we reduce our dependence on all of those manual factors by minimising the sources of those constraints. Once again, we achieve the same goal: by reducing the security cognitive load on our teams, we enable higher throughput and security compliance.

That final piece is the secret sauce to getting business buy-in: our security improvements do not come at the cost of other metrics. They even improve them!

Do Developers Need Security Training?

Short answer: yes. By creating all of these enablements, we might be tempted to rest on our laurels. A quote from Jackie Stewart can best provide the long answer to this question:

You don't have to be an engineer to be be a racing driver, but you do have to have Mechanical Sympathy.

It’s the same with security; everyone is working towards getting the racecar over the line or, in our world, delivering features quickly and securely. Developers don’t necessarily need to be intimately familiar with every control implemented in our defaults and guardrails. Still, they need mechanical sympathy for the underlying security of the system and the operation of our defaults and guardrails in general. Remember, we are not removing the responsibility for producing secure artifacts from our developers; it’s still their “job zero.” We are making it easier to implement the right decisions and harder to make the wrong ones.

Conclusion

Enabling security as a seamless part of the development process is key to balancing rapid delivery with robust protection. Organizations can empower teams to deliver secure and compliant systems without compromising velocity or innovation by adopting secure-by-default platforms and reducing cognitive load. Ultimately, this approach fosters collaboration, enhances security outcomes, and aligns business objectives with streamlined, developer-friendly practices.