Security
Lesson 7 of 8About 4 min readSuggest an edit

Security in the development lifecycle

Security added at the end of a project arrives as a list of findings nobody has time to fix. Built into each stage of normal work, it becomes a set of small, cheap habits. Aim to catch common flaws automatically and leave humans free for the subtle ones.

Secure defaults and requirements

The cheapest vulnerability is one the platform makes hard to write. Give teams secure defaults:

  • a web framework configured with output escaping, CSRF protection and secure cookie flags;
  • a database layer that only offers parameterised queries;
  • infrastructure modules where storage buckets are private unless someone opts out.

Write security needs into requirements like any other behaviour: “only the account owner can export data”, “tokens expire after 15 minutes”. A requirement can be tested; a vague wish to “be secure” cannot.

Code review for security

Reviewers catch logic flaws that tools miss. Ask them to look for:

  • a new endpoint or action without an authorisation check;
  • user input reaching SQL, shell commands, file paths, templates or redirects;
  • secrets, tokens or personal data in code or logs.

Use code owners rules so changes to authentication, crypto, permissions or CI configuration always get a security-aware reviewer.

Automated scanning

Tool Looks at Good at
SAST source code injection patterns, unsafe APIs
DAST a running app headers, misconfiguration
Dependency scanning lockfiles known vulnerable versions
Secret scanning commits and history leaked keys and tokens

Run SAST (static analysis, such as Semgrep or CodeQL) on every pull request. DAST (dynamic testing, such as ZAP) probes a deployed test environment from outside, so it usually runs on a schedule. Secret scanning works best before the push, as a pre-commit hook or push protection, because a secret that reaches the remote must be rotated.

Start by blocking merges only on high-confidence rules. A scanner that raises hundreds of false positives teaches people to ignore it.

Fuzzing

Fuzzing feeds large numbers of generated inputs to a function and watches for crashes, hangs or broken invariants. It pays off most for parsers and decoders of untrusted input. Go supports it natively:

func FuzzParseAmount(f *testing.F) {
	f.Add("12.50")
	f.Fuzz(func(t *testing.T, s string) {
		v, err := ParseAmount(s)
		if err == nil && v < 0 {
			t.Errorf("negative amount from %q", s)
		}
	})
}

Run it with go test -fuzz=FuzzParseAmount; Go saves any failing input under testdata, where it becomes a regression case.

Penetration tests and bug bounties

A penetration test is a time-boxed assessment by skilled testers, useful before a major launch or after large architectural changes. Give testers a clear scope, test accounts and documentation.

A bug bounty invites outside researchers to report issues for rewards. It only helps if you can triage and fix reports quickly. Start with a vulnerability disclosure policy; add a paid programme once your scanners already catch the basics.

Fixing by severity and exploitability

Not every finding is urgent. Prioritise with two questions: how bad is it if exploited, and how likely is exploitation here?

  • Severity scores such as CVSS describe the flaw in general, not in your system.
  • Signals such as a public exploit, inclusion in CISA’s Known Exploited Vulnerabilities catalogue, or a high EPSS score raise the urgency.
  • Reachability matters: a vulnerable function your code never calls is lower priority than one on a public endpoint.

Agree target fix times per severity in advance, track them, and record any accepted risk with an owner and an expiry date.

Security champions

A central security team cannot review everything. Security champions are engineers in each team who take a modest share of the work: they know the local codebase, answer first questions, lead threat models and triage scanner results. Give them time, training and a direct line to the security team.

Habits

  • Secure defaults in templates, so the easy path is the safe one.
  • SAST and secret scanning on every pull request, tuned to low noise.
  • Fixes prioritised by exploitability and reachability, not score alone.

Next: Responding to security incidents

Preparing for, detecting, containing and recovering from security incidents, then disclosing and learning from them.