DevSecOps is often described as a pipeline: static analysis on every commit, dependency and container scanning, infrastructure checks, signed artifacts. Those gates matter, and an earlier post covers them. But a scanner can only find problems in code that already exists. The most expensive security issues we see in reviews are not coding mistakes; they are design decisions: an export that includes every customer's data, a password reset flow that leaks whether an account exists, an admin action without audit.
Those are cheapest to fix while a feature is still a ticket. This post describes the front half of a secure software development lifecycle (SDLC) as we run it with product teams: what happens before the first commit, and how to keep it light enough that teams actually do it.
The SDLC stages and where security fits
| Stage | Security activity | Output |
|---|---|---|
| Plan | Security requirements, data classification | Requirements on the ticket |
| Design | Abuse cases, design review, threat model for risky features | Notes, decisions, tickets |
| Build | Secure coding guidelines, review checklist | Code |
| Test | Security acceptance tests, scanners | Passing tests and gates |
| Release | Pipeline gates, sign-off for high-risk changes | Deployed artifact |
| Operate | Monitoring, incident response, vulnerability management | Alerts, fixes |
This post is about the first two rows.
Plan: security requirements on every relevant ticket
Most features need no special security work. Some do, and the trick is noticing which. We use a short set of trigger questions on the ticket template:
- Does this feature create, read or export personal or payment data?
- Does it add a new way to authenticate or change account recovery?
- Does it add roles, permissions or admin actions?
- Does it accept files, URLs or rich text from users?
- Does it integrate a new third party that receives our data?
- Does it change who can see what across tenants or customers?
If any answer is yes, the ticket gets a "security" label and security requirements written into it. They are concrete, testable statements, not principles:
Security requirements
- Only users with the "billing_admin" role can trigger the export.
- The export contains only the requester's tenant data.
- The download link expires after 24 hours and works once.
- The export action is recorded in the audit log with user, tenant and row count.
A requirement like "the export must be secure" is useless; nobody can test it.
Design: abuse cases
User stories describe what a legitimate user wants. Abuse cases describe what a malicious or careless user might try with the same feature. Writing three to five of them for a labelled ticket takes ten minutes:
| Abuse case | Expected behaviour |
|---|---|
| A support agent changes the tenant id in the export request | Request rejected, attempt logged |
| A user shares the download link publicly | Link expires, works once, requires login |
| An attacker triggers thousands of exports | Rate limited per user and tenant |
| A removed employee uses an old session to export | Session invalidated at offboarding |
| Export includes columns added later, such as tax ids | Export uses an explicit column allow-list |
Abuse cases turn into security acceptance tests later, and they often reveal requirements nobody had written down, like the last row.
Design: a lightweight review
For labelled features, the developer writes a short design note, one page at most, and a security champion or the security team reviews it. The note covers:
- data flows: where data comes from, where it is stored, where it goes;
- trust boundaries: browser, API, background jobs, third parties;
- authentication and authorization decisions;
- how the abuse cases are handled;
- logging and monitoring.
The review is a 30-minute conversation, not an approval board. Its output is a list of decisions and tickets. For high-risk features (payment flows, authentication, cross-tenant data), we do a full threat model with STRIDE, which a follow-up post in this series walks through.
Security champions
The security team cannot review every feature. Each product team has a security champion: a developer with some extra training and a direct line to the security team. Champions review most labelled tickets themselves and escalate the risky ones. This scales, and it spreads security knowledge into teams.
Build and test: security acceptance criteria
Security requirements and abuse cases become acceptance criteria on the ticket and tests in the code:
def test_export_rejects_other_tenant(client, billing_admin_tenant_a):
resp = client.post("/exports", json={"tenant_id": TENANT_B}, user=billing_admin_tenant_a)
assert resp.status_code == 403
assert AuditLog.objects.filter(action="export_denied", user=billing_admin_tenant_a).exists()
def test_export_link_works_once(client, billing_admin_tenant_a):
link = create_export(client, billing_admin_tenant_a)
assert client.get(link).status_code == 200
assert client.get(link).status_code == 410
These tests are as important as functional ones. They are the evidence that the requirement holds, and they keep holding after refactoring.
Data classification as the starting point
Most trigger questions depend on knowing what data a feature touches. We keep a simple classification on our data model: each table and important field is tagged public, internal, confidential or restricted, in code next to the schema. Tickets that touch confidential or restricted fields are labelled automatically when a developer links the affected models. The classification also drives logging redaction and export rules, so it pays for itself several times.
Keeping it light
Teams resist processes that feel like bureaucracy. What made this work:
- Only for labelled tickets. Most tickets need nothing extra.
- Templates, not documents. Requirements, abuse cases and the design note are sections in the ticket.
- Timeboxed reviews. Thirty minutes, scheduled within two days of request.
- Visible value. We share examples where a design review prevented a real problem, anonymised, in the engineering newsletter.
Training
Champions get a half-day workshop on abuse cases and design review, and everyone else a short session on the trigger questions. Most of the skill is learned by doing: the first few reviews are run together with the security team, then champions run them alone.
Measuring
Two numbers tell us whether the front half of the SDLC works:
- share of security findings from penetration tests and bug reports that a design review could have caught, which should fall over time;
- median time from "security" label to completed review, which should stay under a few days.
In the first year, design-level findings in our annual penetration test dropped from seven to two.
Common mistakes
| Mistake | Effect | Do this instead |
|---|---|---|
| Security only in the pipeline | Design flaws reach production | Requirements and abuse cases at planning |
| Vague requirements ("secure", "compliant") | Nothing to test | Concrete, testable statements |
| Every feature reviewed by security | Bottleneck, rubber stamps | Trigger questions plus champions |
| Reviews as approval gates | Teams route around them | Short conversations producing tickets |
| Abuse cases not turned into tests | Protections regress silently | Security acceptance tests |
Key takeaways
- The cheapest security fixes happen while a feature is a ticket.
- Use trigger questions to find tickets that need security requirements.
- Write abuse cases and turn them into security acceptance tests.
- Keep design reviews short, frequent and handled mostly by security champions.
- Save full threat models for the riskiest features.
References
- NIST SP 800-218, Secure Software Development Framework (SSDF): https://csrc.nist.gov/pubs/sp/800/218/final
- OWASP SAMM, Software Assurance Maturity Model: https://owaspsamm.org/
- OWASP Application Security Verification Standard: https://owasp.org/www-project-application-security-verification-standard/
- OWASP Abuse Case Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Abuse_Case_Cheat_Sheet.html
Comments
0No comments yet. Be the first to comment!
Sign In to post a comment.