DevSecOps in Depth · Security Chaos Engineering

Security Chaos Engineering

A SAST gate can be green, a purple-team session can score every technique tested green on the ATT&CK Navigator, and the on-call security engineer can still sleep through a real incident three weeks later — because nobody ever actually pulled the trigger on the exact control that was supposed to catch it, on the exact day it mattered. Security chaos engineering borrows a discipline built for a different problem — Netflix's chaos engineering, invented to prove a distributed system survives a server dying mid-request — and points it at a different question: not "does the system survive," but "does anyone notice." You revoke a real credential and watch whether anything pages. You plant a fake leaked secret somewhere a real one would leak and watch whether it's caught before it's ever used. You fire one small, deliberately reversible attack pattern against a live account and watch whether the detection you believe exists actually exists. This page covers the method borrowed from resilience chaos engineering, one fully worked credential-revocation experiment including the gotcha it was designed to surface, the tooling that runs these experiments safely, and what it takes to run one without the drill itself becoming the incident.

☺ Explain it like I'm 10

Imagine you install a home alarm system and then just trust the brochure forever. Security chaos engineering is asking a friend to actually jiggle a locked window at 2pm on a random Tuesday, while you watch to see whether the alarm truly goes off, whether it calls the right phone number, and how many minutes pass before someone shows up. If nothing happens, you didn't get unlucky — you found out the alarm was never really wired to anything, while it was still just a drill and not a real burglary.

🦝🦊Your hosts for this topic: Rocky the Raccoon & Foxy — Rocky already pries open threat models and pentests looking for the gap everyone missed; here he does it on a recurring schedule, against production, on purpose. Foxy is the one actually watching to see whether anything reacts — the same instinct that drives incident response & forensics, just run before an attacker forces the question instead of after.

What security chaos engineering actually tests

☺ Like you're 10: It's not enough to believe your smoke alarm has a battery in it. Make some smoke, on purpose, in a safe spot, and watch whether it actually goes off.

Chaos engineering as a discipline started at Netflix around 2010 with Chaos Monkey — a tool, later open-sourced as part of the "Simian Army," that randomly killed production instances to prove the streaming service could survive a server dying mid-request. Casey Rosenthal and collaborators later codified the practice into a shared set of principles, published at principlesofchaos.org, aimed squarely at resilience: does the system keep working when something breaks. Around the mid-to-late 2010s, Aaron Rinehart — then Chief Security Architect at UnitedHealth Group, later a founder of the chaos-engineering company Verica — and Kelly Shortridge argued the same method answers a security question just as well as a resilience one, work that became the O'Reilly book Security Chaos Engineering: Sustaining Resilience in Software and Systems (check the current edition for exact publication details — it went through early-release chapters before a full print run). The idea: instead of asking "will checkout survive a dead node," ask "will anyone notice a stolen credential."

It's worth being precise about how this differs from work this course already covers. Purple teaming is a scheduled, human-facilitated session — a red operator and a detection engineer sit down together, run a handful of techniques, and compare notes in real time. Atomic Red Team tests are one-off, run manually against a lab host to earn a single ATT&CK Navigator score. Security chaos engineering is the discipline that turns that kind of test into a routine: hypothesis-driven, run against production or as close to it as is responsible, and — the part most teams skip — automated to run again and again on a schedule, not once before an audit. The nearest commercial category is breach and attack simulation (BAS), a market of vendor platforms that continuously fire known attack techniques against your environment; the tools later on this page overlap with what BAS products do, but the discipline itself — a stated hypothesis, a controlled blast radius, a real production target — is what chaos engineering adds on top of "run more simulated attacks."

◆ Key idea

A purple-team session proves a technique can be detected, once, in a room full of people watching for it. Security chaos engineering proves it's still detected, unattended, on an ordinary Tuesday, months after the people who built the rule moved on to other work.

The method, borrowed: steady state, hypothesis, blast radius

☺ Like you're 10: Say out loud exactly what you expect to happen before you do the risky thing — otherwise you can't tell whether the result surprised you or you just weren't paying attention.

Netflix's chaos engineering principles translate onto a security experiment almost line for line, with one inversion that changes everything about how you read the result:

Here's the inversion. In a classic resilience experiment, success looks like nothing changing — you kill a node, and checkout keeps working exactly as it did before, which confirms the system is as resilient as you hoped. In a detection-focused security experiment, success looks like something changing — you revoke a credential, and the quiet steady state of "no open incident" should stop being true within your stated window. If you inject a real failure and the dashboard stays exactly as calm as it was five minutes earlier, that calm isn't good news. It's the blind spot the whole exercise exists to surface.

1 · Hypothesize a steady state "off-allow-list key use pages on-call within 5 minutes" 2 · Inject one reversible failure revoke a key, plant a fake secret, run one technique 3 · Observe against the stated SLA did it fire, and how long did it actually take? 4 · Learn, then automate the rerun fix the gap, reschedule the identical experiment continuous loop — tested once and never rechecked is a hope, not a control
◆ Key idea

A resilience chaos experiment succeeds when nothing changes. A security detection chaos experiment succeeds when something should change — and the whole point of running it is finding out whether it actually does.

A full worked experiment: revoking a production credential

☺ Like you're 10: Turning off a stolen key stops someone from using it again. It doesn't automatically kick out someone who's already inside, using a copy they made five minutes earlier.

Say a legacy deploy job — not everything has migrated to OIDC-federated workload identity yet; a third-party integration, a break-glass admin path, or a cron job nobody's touched in a year is a completely realistic gap even in a shop that's otherwise done the migration — still authenticates with a static IAM user access key, svc-deploy-legacy. The hypothesis: "if this key is compromised and someone deactivates it, (1) no further calls succeed using it, and (2) security on-call is paged within 5 minutes of the deactivation itself, because that's exactly the kind of event a real incident response would need to happen fast."

# 1) INJECT — deliberately reversible: Inactive, not Delete, so one command undoes it
aws iam update-access-key --user-name svc-deploy-legacy \
  --access-key-id AKIAEXAMPLE111 --status Inactive

# rollback ready before the drill starts:
#   aws iam update-access-key --user-name svc-deploy-legacy \
#     --access-key-id AKIAEXAMPLE111 --status Active

# 2) OBSERVE — did the pipeline this rule depends on even exist?
# an EventBridge rule that should be watching for exactly this:
{
  "source": ["aws.iam"],
  "detail-type": ["AWS API Call via CloudTrail"],
  "detail": { "eventName": ["CreateAccessKey", "DeleteAccessKey", "UpdateAccessKey"] }
}

# 3) THE SURPRISE — a session already assumed using this key keeps working:
aws sts get-caller-identity   # run from the CI job's already-cached session — still succeeds

# 4) THE ACTUAL FIX the drill was missing from the runbook —
# revoke the ROLE's active sessions, not just the key used to assume it
# (this is the same mechanism behind the IAM console's own "Revoke active sessions" button)
aws iam put-role-policy --role-name ci-deploy-prod \
  --policy-name RevokeSessionsBefore2026-08-16T14-00 \
  --policy-document file://deny-before-revocation-time.json

deny-before-revocation-time.json is a short inline policy keyed on aws:TokenIssueTime, denying every action for any credential minted before the moment you set:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Deny",
    "Action": "*",
    "Resource": "*",
    "Condition": { "DateLessThan": { "aws:TokenIssueTime": "2026-08-16T14:00:00Z" } }
  }]
}
Inject: IAM key → Inactive (reversible) CloudTrail UpdateAccessKey event EventBridge rule matches eventName Slack / PagerDuty fired within 5 min? that's the actual test deactivating the key does two different things at once: New AssumeRole / fresh API calls using the deactivated key DENIED immediately — expected ✓ Already-issued STS session token assumed before the key was deactivated STILL VALID until its own expiry ⚠ Runbook fix the drill surfaced Revoke active sessions on the assumed ROLE (an aws:TokenIssueTime deny policy) — deactivating the key alone isn't enough

Neither finding here is a hypothetical stretch — it's exactly how AWS session credentials work: an STS session issued via sts:AssumeRole is a self-contained, signed credential that doesn't re-check the originating key's status on every call, so it stays valid until its own expiration (up to the role's configured MaxSessionDuration) regardless of what happens to the key that was used to obtain it. A team that "knows" this in the abstract and a team that's actually watched a live session survive a deliberate key revocation are not in the same place operationally — one has a belief, the other has a corrected runbook and a rule that now watches UpdateAccessKey specifically, not just CreateAccessKey and DeleteAccessKey.

The experiment catalog: beyond one credential

☺ Like you're 10: Different drills catch different sleeping guards — a fire drill doesn't tell you whether the burglar alarm works, so you need more than one kind.

Credential revocation is one instance of a general pattern: pick something a real incident would involve, do the smallest reversible version of it against something real, and watch whether the control that's supposed to react actually does.

ExperimentWhat you inject"It worked" looks likeExample tooling
Credential / session revocationDeactivate or rotate a live key or role sessionAlert fires within SLA; new calls are denied and any already-issued session is confirmed dead tooCloud IAM/STS APIs + EventBridge / Cloud Audit Log alerting
Leaked-secret simulationA honeytoken planted where a real secret would realistically leakCaught by secret scanning before it's ever used, or an alert fires the moment it isThinkst Canarytokens; gitleaks / TruffleHog as the "should have caught it first" control
Benign attack patternOne isolated, reversible cloud attack techniqueThe mapped Sigma / GuardDuty / CloudTrail rule fires and is triaged within SLAStratus Red Team, MITRE Caldera
Cloud posture driftFlip one guardrailed setting — a bucket made public, a security group openedA CSPM/CNAPP flags it, and ideally auto-remediates, within minutesProwler, ScoutSuite, native CSPM
Policy / gate bypass attemptPush a manifest or plan that should fail an admission or IaC policyThe gate actually blocks it — not just "should," according to the docsKyverno, OPA Gatekeeper, Checkov in CI
Telemetry blackoutSilently stop one log shipper, agent, or scannerSomeone notices the missing data itself — not just a missed attack it would have caughtHeartbeat / dead-man's-switch monitoring on the pipeline
⚠ Watch out

A honeytoken is only safe if it's genuinely inert and nobody forgets where it's buried. Never plant a canary credential with real permissions attached "just in case it's needed" — that turns a detection experiment into an actual vulnerability. And keep a dated inventory of every token you've planted: a forgotten canary that trips during an unrelated compliance audit or third-party scan wastes a real investigation chasing a fire drill nobody remembered lighting.

Tooling for injection, and what counts as "observing"

☺ Like you're 10: The tool that pretends to be a burglar is only half the job — you also need someone actually watching the cameras while it happens.

Stratus Red Team, an open-source CLI, packages individual cloud attack techniques — organized by provider and MITRE ATT&CK tactic — as small, self-contained, reversible units, closely mirroring Atomic Red Team's approach but purpose-built for cloud control-plane actions instead of host-level ones:

# list techniques mapped to one ATT&CK tactic
stratus list --mitre-attack-tactic exfiltration

# warmup provisions any prerequisite scratch infrastructure the technique needs
stratus warmup aws.exfiltration.ec2-share-ebs-snapshot

# detonate performs the technique for real, against your real account
stratus detonate aws.exfiltration.ec2-share-ebs-snapshot
#   -> now go check: did a GuardDuty finding land? did the mapped Sigma/CloudTrail rule fire?

# revert undoes the technique's effect; cleanup tears the warmup infra back down
stratus revert aws.exfiltration.ec2-share-ebs-snapshot
stratus cleanup aws.exfiltration.ec2-share-ebs-snapshot

Thinkst Canarytokens (a free service at canarytokens.org, also self-hostable) generates a realistic-looking but entirely inert credential — an AWS key pair, an Azure app ID, a Kubernetes kubeconfig, a Word document, a DNS hostname — that does exactly one thing: it phones home the moment anyone tries to use it. Plant it somewhere a real leaked secret would realistically end up, not somewhere only your own team would ever look — a public GitHub gist named like a leftover debug dump, an internal wiki page (to test insider or compromised-account detection specifically, as a separate experiment from the public-leak case), a low-privilege EC2 instance's user-data script. The token is the detector:

// illustrative — Canarytokens' exact webhook payload shape can change; treat as representative
{
  "canarytoken": "a1b2c3d4e5f6...",
  "channel": "AWS API Key Token",
  "time": "2026-08-16T14:02:11Z",
  "src_ip": "203.0.113.44",
  "additional_data": { "event_name": "GetCallerIdentity", "user_agent": "aws-cli/2.x" }
}

Two more tools round out the toolbox. MITRE Caldera, a server-and-agent adversary emulation platform built by the same organization behind ATT&CK, runs multi-step "operations" autonomously against a fleet of agents rather than one command at a time — the closest open-source path to the "automate to run continuously" principle for host-level techniques. Infection Monkey (open-sourced by Guardicore, now maintained under Akamai after its 2021 acquisition) does something similar for internal network lateral-movement testing, deliberately built to be safe-by-design and to end in a report rather than real damage. And on the cheap-but-shallow end, AWS's own guardduty create-sample-findings API generates a sample finding of a chosen type with no real attack behind it at all — useful for proving your GuardDuty-to-Slack plumbing works, but it tests the pipe, not the detection logic, and shouldn't be mistaken for the real thing.

For orchestrating the whole loop as code rather than a checklist, Chaos Toolkit (an open-source, provider-agnostic experiment runner) expresses a security experiment in the same declarative shape a resilience one would use — a steady-state hypothesis with probes, a method of actions, and rollbacks:

{
  "version": "1.0.0",
  "title": "Revoking svc-deploy-legacy's key pages security on-call within 5 minutes",
  "steady-state-hypothesis": {
    "title": "No open security incident right now",
    "probes": [{
      "type": "probe", "name": "no-open-pagerduty-incident", "tolerance": 0,
      "provider": { "type": "python", "module": "chaos_pagerduty.probes",
                     "func": "count_open_incidents", "arguments": { "service": "security-oncall" } }
    }]
  },
  "method": [{
    "type": "action", "name": "deactivate-ci-access-key",
    "provider": { "type": "process", "path": "aws",
      "arguments": "iam update-access-key --user-name svc-deploy-legacy --access-key-id AKIAEXAMPLE111 --status Inactive" },
    "pauses": { "after": 300 }
  }],
  "rollbacks": [{
    "type": "action", "name": "reactivate-ci-access-key",
    "provider": { "type": "process", "path": "aws",
      "arguments": "iam update-access-key --user-name svc-deploy-legacy --access-key-id AKIAEXAMPLE111 --status Active" }
  }]
}

Notice the assertion this experiment is actually built to make: after the 5-minute pause, re-running the same probe should now find at least one open incident — the steady state was supposed to break. A framework built for resilience testing (where the probe should still pass afterward) needs that inversion spelled out explicitly, or the experiment silently tests the wrong thing.

Detection coverage decays — why a green cell needs a re-test date

☺ Like you're 10: A smoke alarm you tested once, two years ago, isn't the same as a smoke alarm you know works today — batteries die quietly, and so do detection rules.

The ATT&CK Navigator scoring this course already covers treats a score of 3 — "a rule exists and has been tested" — as an earned, trustworthy state. It is, on the day it's earned. What it isn't is permanent. Log pipelines get refactored. A service migrates to a new logging library and quietly renames a field a Sigma rule's logsource pipeline depended on. An EDR agent ships a version bump that reshapes its event schema. Someone "cleans up" an EventBridge rule during an unrelated audit and narrows its eventName filter without realizing a downstream detection depended on the wider match. None of these changes announce themselves, and the absence of an alert looks identical whether nothing bad happened or the rule quietly stopped working — that's detection drift, and it's invisible by construction until something tests it again.

The fix follows directly from the same "security as code" argument this course has made since What is DevSecOps?: don't just write and test a detection once — schedule the experiment that proved it works to run again, automatically, and treat a failure to re-fire as a regression, not a curiosity.

# .ci/chaos-regression.yml — re-run the SAME experiment that earned a Navigator score of 3,
# on a schedule, against a scoped account, and fail loudly if the paired rule stops firing
schedule:
  - cron: "0 3 * * *"        # nightly, off-peak, against a non-prod or tightly scoped account

steps:
  - name: detonate-known-technique
    run: stratus detonate aws.exfiltration.ec2-share-ebs-snapshot

  - name: wait-for-pipeline
    run: sleep 300            # give collection, normalization, and correlation time to catch up

  - name: assert-alert-fired
    run: python verify_alert_fired.py --rule-id T1537-ebs-share --window 10m
    # non-zero exit = a rule "tested and validated" months ago has silently regressed —
    # open a ticket against the rule owner; don't just log it and move on

  - name: revert
    if: always()
    run: stratus revert aws.exfiltration.ec2-share-ebs-snapshot
◆ Key idea

An ATT&CK Navigator layer is a photograph, not a live feed. Wiring the experiment that earned each green cell into a scheduled regression job is what turns the photograph into something you can actually trust six months later without retaking it by hand.

Running it safely: blast radius, deconfliction, and the game day

☺ Like you're 10: A fire drill only teaches anyone anything if the building doesn't actually burn down in the process — plan the exit before you pull the alarm.

Everything on this page targets production, or something close enough to it that the result means something — which raises the stakes past what a scoped penetration test against a signed rules-of-engagement document already requires, not below it. A few practices make that responsible rather than reckless:

How much the responding team knows in advance is a real spectrum, not a binary. At the fully announced end sits something close to Etsy's own GameDay tradition under John Allspaw — collaborative, blameless, everyone in the room learning together, closer to a purple-team session than a covert test. At the unannounced end sits something like Google's internally documented DiRT (Disaster Recovery Testing) program, which has publicly discussed running realistic, unannounced failure scenarios — including security ones — specifically because a defender who knows a drill is coming behaves differently than one who doesn't. Neither end is more "correct"; an announced game day teaches a team to build and tune a control together, while an unannounced one tells you what actually happens the one time nobody's braced for it. Mature programs run both, at different cadences, for different questions.

⚠ Watch out

An unannounced experiment that isn't properly deconflicted can become a real incident in its own right — a responder who doesn't know it's a drill escalates externally, pages a VP, or opens a customer-facing status page over a fire nobody actually lit. The fix isn't to stop running unannounced tests; it's to make sure at least one authorized, reachable person always knows, and that "check the experiment registry first" is the very first line of every on-call runbook.

Whichever end of the spectrum a given drill sits on, the debrief matters as much as the injection did: a blameless write-up of exactly what fired, what didn't, and why — fed into a backlog with an actual SLA, the same discipline best practices and the operating model argues for everywhere else in this course, and the opposite of the pattern DevSecOps anti-patterns calls out when a finding just becomes a slide nobody revisits.

🎬 At the Shift-Left Squad
🦝

Rocky the Raccoon: Pulled the CI deploy key's status to Inactive four minutes ago. Nobody's said a word yet.

🦊

Foxy: Four minutes and no page? Let me check whether the EventBridge rule even exists, or exists and just never fired.

🐢

Timmy the Turtle: Before either of you says another word — this was authorized, scoped, and reversible, right? I want the rollback command sitting right there before we find out what actually happens.

🦝

Rocky the Raccoon: Sitting right here. One command flips it back to Active. That's the whole point of choosing Inactive over Delete.

🦊

Foxy: Found it — the rule matches CreateAccessKey and DeleteAccessKey. Nobody ever added UpdateAccessKey. That's why nothing fired.

🐘

Ellie the Elephant: And the session it already handed out earlier today — is that dead too, or still walking around out there?

🦝

Rocky the Raccoon: Still walking around. Deactivating the key never touched a session already issued. That's the part nobody's runbook mentioned — until right now.

🦉

Professor Owl: Which is the entire exercise in one sentence — we didn't confirm what we believed, we found what we didn't know. Fix the rule, fix the runbook, and put this exact drill back on the calendar for next quarter.

✓ Checkpoint

1. What does security chaos engineering test that a purple-team session or a one-time Atomic Red Team run does not, and what does it borrow methodologically from Netflix's original chaos engineering discipline? 2. Explain the inversion between a resilience chaos experiment's steady-state hypothesis and a detection-focused security chaos experiment's — what does "success" mean in each case? 3. Walk through the credential-revocation drill: what did deactivating the IAM access key actually stop, what did it not stop, and what's the real fix the drill surfaced? 4. Name two categories from the experiment catalog beyond credential revocation, and state what "the experiment worked" looks like for each. 5. Why doesn't a detection rule that fired successfully six months ago guarantee it still fires today, and what's this page's proposed fix? 6. Name two concrete blast-radius controls you'd want in place before running any of these experiments against production, and explain the specific risk each one guards against.

Check your answers
  1. It tests whether detection and response still work unattended, on a schedule, over time — not just once, in a room, with people already watching for the result. It borrows Netflix's core method almost directly: a steady-state hypothesis, real-world-shaped events injected deliberately, running in production rather than a clean lab, automating the experiment to recur, and minimizing blast radius throughout.
  2. In a resilience experiment, success means the steady state holds despite the injected failure — checkout still works after a node dies. In a security detection experiment, success means the steady state (typically "no open incident") is expected to break — an alert should fire. If it doesn't break when it should have, that's not good news; it's the exact blind spot the experiment was designed to find.
  3. Deactivating the key stopped new AssumeRole and API calls made using that key — that part worked as expected. It did not retroactively invalidate an STS session token already issued from that key before deactivation; that session kept working until its own natural expiration, because STS sessions are self-contained and don't re-check the originating key's status per call. The real fix is revoking active sessions on the assumed role itself (an aws:TokenIssueTime-keyed deny policy), not just deactivating the key that was used to obtain the session.
  4. Any two of: leaked-secret simulation (a planted honeytoken is caught by secret scanning before use, or triggers an alert the moment it is used); benign attack pattern (the mapped Sigma/GuardDuty/CloudTrail rule fires and is triaged within SLA); cloud posture drift (a CSPM flags — and ideally auto-remediates — the drift within minutes); policy/gate bypass (the admission or IaC gate actually blocks the bad change, not just in documentation); telemetry blackout (someone notices the missing data itself, not just an attack it would have caught).
  5. Because the pipeline underneath the rule keeps changing even when the rule's own YAML doesn't — log fields get renamed, agents get upgraded, an unrelated cleanup narrows a filter — and a rule that silently stops firing looks identical to a quiet day with no attacks, so nobody notices without actively re-testing it. The proposed fix is to wire the experiment that originally earned the rule its "tested" score into a scheduled regression job (a nightly or weekly re-detonation with an automated pass/fail assertion) rather than treating a single successful test as permanent proof.
  6. Any two of: preferring reversible actions (e.g. deactivating a key instead of deleting it, so a single command undoes the experiment if something goes wrong) — guards against the drill itself causing lasting damage; a hard time-box with an automated abort condition — guards against a drill running longer or wider than intended if nobody's watching closely enough; a written, signed authorization scoping the exact systems and dates — guards against the exercise being indistinguishable from unauthorized access, legally and organizationally; a dated, visible experiment registry — guards against a responder mistaking the drill for a real incident and escalating externally over nothing.