Attackers don't follow a straight line. They zigzag, backtrack, pivot, and adapt. This talk explores how the MITRE ATT&CK framework maps these crooked paths — and what developers can do to straighten out their defenses. The crooked-line metaphor frames the talk: defenders often expect a straight path, while attackers zigzag, backtrack, and pivot across tactics like reconnaissance, lateral movement, and exfiltration.
Quick intro — I'm Chris, a Principal Software Engineer at Microsoft. I spend a lot of time thinking about how developers can build more secure applications without needing a PhD in cybersecurity.
The attack surface has exploded. We're not just building monoliths anymore — we have APIs, microservices, serverless, and cloud infrastructure. And attackers don't just try one thing. They chain techniques together in complex kill chains. Our defenses need to evolve beyond "patch and pray."
Most developers know OWASP. It's fantastic for understanding vulnerabilities — what can go wrong in your code. But it's fundamentally a prevention-focused, vulnerability-centric view. It answers "what's broken" but doesn't tell you much about who's attacking or how they actually behave.
MITRE ATT&CK flips the perspective. Instead of cataloging vulnerabilities, it catalogs attacker behavior. It started at Fort Meade when MITRE researchers studied real adversaries on a network. With over 200 techniques mapped from real-world attacks, it's the most comprehensive map of how hackers actually operate.
ATT&CK isn't the only framework from MITRE. D3FEND maps defensive countermeasures, ATLAS covers AI/ML threats, and ENGAGE provides adversary engagement strategies. Together they form a comprehensive ecosystem. But ATT&CK is the foundation — and the most relevant for developers.
Think of it as a hierarchy. Tactics are the goals — "I want to get initial access." Techniques are how — "I'll use spear phishing." Sub-techniques get specific — "I'll send a phishing email with a malicious attachment." And procedures are documented cases where real threat groups actually did this.
These are attacker goals, not a required sequence. The arrows organize this overview; real attacks loop back, skip steps, and adapt. Notice the groups: Pre-Attack for reconnaissance, Get In for initial compromise, Stay In for maintaining access, and Act for achieving objectives. Today we'll cover seven developer-focused areas: Initial Access, Execution, Persistence, Credential Access, Defense Evasion, Supply Chain, and Collection/Exfiltration. These are teaching groups, not seven official tactics: supply-chain compromise is a technique, and collection/exfiltration spans two tactics. Lateral Movement and Privilege Escalation often fall to infrastructure and platform teams — we'll touch on where they intersect with your code.
Side by side, you can see the difference. OWASP helps identify an injectable SQL query. ATT&CK helps describe how exploitation can lead to privilege escalation, lateral movement, and data theft. These are complementary perspectives, not an exclusive split between prevention and detection. ATT&CK is a knowledge base; our controls and telemetry perform the detection.
The setup IS the punchline: OWASP versus ATT&CK sounds like a choice — vulnerabilities or adversary behavior, prevention or detection. Real-world breaches are never a single vulnerability; they're chains of techniques. SolarWinds was supply chain compromise leading to lateral movement leading to data exfiltration. You need prevention AND detection to handle the full lifecycle.
This mapping is incredibly useful. When you fix an OWASP vulnerability, you're actually blocking specific ATT&CK techniques. Fixing SQL injection doesn't just close a bug — it blocks T1190, which is the front door for dozens of attack chains. Understanding this connection helps you prioritize what to fix first.
Now we shift gears. We're going to look at code through the eyes of an attacker and then see how to defend it. At each step, ask the same three questions: what can the attacker do, what can our application observe, and what decision should that signal trigger?
This is the core insight of the talk. Defenders build straight-line defenses — firewall, IDS, patch management. But attackers zigzag, loop back, escalate, discover new targets, and escalate again. ATT&CK captures this messy reality that a linear kill chain model misses.
SolarWinds illustrates why developers need ATT&CK. The build compromise placed malicious code in signed updates; a valid signature identifies the signer but does not prove safe behavior. These are examples from the broader campaign, not a mandatory sequence followed at every victim. Loading the SUNBURST DLL is execution, but is not by itself T1059, which specifically concerns command and scripting interpreters. SUNBURST used DNS signaling and subsequently HTTP communication. Golden SAML is token forgery (T1606.002), distinct from the prerequisite theft of a token-signing private key (T1552.004). Up to 18,000 customers received affected updates; that is not the number subjected to every follow-on technique. Sources: https://attack.mitre.org/software/S0559/ and https://attack.mitre.org/techniques/T1606/002/.
Process alone is not security. Enforced policy checks, a threat model, behavioral baselines, and tested recovery procedures provide evidence that a deployment is controlled. A green checklist or a filed ticket is not equivalent to a working control. Rushed patches, exposed debug endpoints, and overly broad file permissions can turn an initial compromise into persistent access. Apply the same scrutiny to build identities and delivery infrastructure as to application code.
The attacker needs a first foothold, whether through a web vulnerability, stolen credentials, or phishing. Start with the trust boundary: which endpoints are exposed, which identities are accepted, and what signals would distinguish legitimate use from abuse? A valid login can bypass a perfectly patched endpoint.
These are the most common initial access techniques. T1190 is your classic web app exploit — SQL injection, XSS, etc. T1078 is even scarier — the attacker has real, valid credentials. Brute force and phishing are how they get those credentials in the first place.
Each protected operation needs an explicit response, but not every control is a new algorithm to write. Configure the identity provider's supported protections against credential attacks (T1110) and risky sign-ins; use authentication middleware to validate tokens. Availability, licensing, and enforcement behavior vary by product. Application policy still decides which records, tenants, and export volumes are permitted. For sensitive actions, the app requests and verifies the provider's supported step-up result before proceeding; a successful MFA challenge does not override authorization or volume limits. A concealed 404 is appropriate when resource existence should not be disclosed, not a universal replacement for 403.
Avoid rebuilding capabilities your identity provider already supports, but do not assume every product automatically supplies or enables every control. Provider risk models can combine signals across connected services; your app adds resource and business context. IP changes alone are not proof of compromise, especially for mobile users. Use framework and policy-engine support for authorization and sessions rather than hand-writing token or cookie cryptography. Your team still defines and integrates the object, tenant, export, and audit policies. IdP sign-out or refresh-token revocation does not necessarily invalidate an app's existing session or a self-contained access token immediately: test the supported revocation path and document its delay. Specialized custom detections can complement these protections; they should not replace well-supported defaults without a reason.
Classic SQL injection. The attacker passes "1 OR 1=1--" as the ID, which dumps the entire users table. This is the number one way attackers exploit public-facing applications. Simple string concatenation is all it takes to open the door.
Parameter binding keeps the identifier separate from the SQL program, addressing this T1190 injection path. Validation also handles a missing or malformed id. A consistent 404 for missing records is useful API behavior, but does not by itself prevent T1087 account discovery; authorization, rate limits, and response design still matter. Authentication and record-level authorization are omitted from this query-focused excerpt.
Scenario: an attacker with compromised valid credentials (T1078) probes records outside those credentials' authorization. Authentication middleware has already validated the principal; subject is taken from that principal, not a request header. Load the resource metadata, then ask the framework's authorization service to evaluate the app's tenant and object policy. [Authorize] alone cannot decide ownership of a resource that has not yet been loaded. Keep tenant isolation explicit even when roles or a policy service are used. The concealed 404 is an intentional policy here, so this response does not confirm whether an inaccessible id exists. Repeated denials are a signal to correlate, not automatic proof of an attack. Normal sharing can authorize a record owned by someone else; the relevant question is the policy, not ownership alone.
Once attackers get in, they may try to execute code through the application. The next trust boundary is between user data and an interpreter. Watch what happens when a filename stops being data and becomes part of a shell program.
Command injection is when user input ends up in an OS command. Exploitation for client execution targets the user's browser or client application. Process injection is more advanced — injecting code into running processes. As developers, we mostly encounter T1059.
The harmless echo illustrates a POSIX shell interpreting a second command. If untrusted input reaches a shell, the attacker gets a language runtime, not a filename parser. The impact is limited by the application's process identity and permissions, not the intended file-conversion operation. The next slide shows the same boundary failure with Windows cmd.exe syntax.
The filename goes directly into a Windows cmd.exe command. The ampersand separates commands; the harmless echo makes that visible without a destructive payload. The ImageMagick conversion is no longer the only operation the process runs. This is intentionally vulnerable code, not a command to run against a real service.
This is the process-launch excerpt inside the same controller action. ArgumentList replaces hand-built quoting; each value is an argument, not a shell program. IsValidFilename must constrain extensions and the upload directory and reject option-like names. Use a trusted executable path, least privilege, bounded runtime with child-process termination, and exit-code checking in the full implementation. The excerpt focuses on T1059's shell boundary, not every file-conversion risk.
These are alternative bodies of the same HTTP handler, not code to run sequentially. Imports and framework setup are omitted. CWE-502 describes the weakness; exploiting this Internet-facing endpoint is a T1190 scenario. T1059.006 specifically means Python execution, not deserialization in every language, and T1203 concerns client execution. During pickling, __reduce__ can specify a callable and arguments; during unpickling, the reconstruction machinery invokes that callable. Validating the resulting object is too late. Use a data-only format, enforce a body-size limit before parsing, and validate allowed fields, types, ranges, and nesting before processing. The same class of unsafe object reconstruction occurs with unsafe YAML loaders, Java ObjectInputStream, PHP unserialize, and .NET BinaryFormatter. Sources: https://docs.python.org/3/library/pickle.html and https://attack.mitre.org/techniques/T1190/.
Attackers don't want to re-exploit every time. Once they have access, they look for a reusable identity or session. The question now changes from "is this token valid?" to "is it still being used by the expected client, and can we revoke it?"
These are related teaching examples, not a single ATT&CK tactic. T1098 changes accounts or permissions to maintain access. T1539 is cookie theft; T1550.004 is use of the stolen cookie to access an already-authenticated session. T1185 instead describes hijacking or pivoting through the victim's browser, so it is not a synonym for cookie replay. Web shells provide server-side persistence when attacker-controlled code is made executable by the server. Sources: https://attack.mitre.org/techniques/T1539/, https://attack.mitre.org/techniques/T1550/004/, and https://attack.mitre.org/techniques/T1185/.
The code shows a hardcoded signing secret (T1552), cookies that are not restricted to HTTPS or hidden from JavaScript, and a long opportunity for reuse of a stolen cookie (T1550.004). Cookie theft itself is T1539. Express sessions also depend on server-side state; knowing the signing secret does not reveal every session. The endpoint demonstrates authentication only, not complete record authorization. A stolen cookie can remain usable until expiration or effective revocation.
This configures an established session library rather than inventing a session manager. Validate that SESSION_SECRET is securely supplied at startup; sessionStore is an appropriately configured shared production store, not Express's default MemoryStore. Rolling refresh extends idle expiry, not the session identifier; use req.session.regenerate with error handling at authentication and privilege boundaries. Configure an absolute lifetime as well. Choose SameSite settings with the actual OIDC redirect and CSRF flow in mind; Strict is not a blanket setting for every authentication cookie. An IP change alone does not establish theft. Wire supported password-change, account-disable, privilege-change, and compromise responses to app-session invalidation. IdP revocation alone may not end this local session. Replaying a stolen cookie (T1550.004) can avoid repeating the original MFA challenge, so test effective revocation and any step-up requirements. These controls reduce risk; they do not guarantee prevention of every replay or browser compromise.
Authenticated, authorized upload-action excerpt; configure request-size limits and CSRF protection in the framework before accepting multipart data. _allowed is a case-insensitive extension allowlist. quarantineRoot is an application-configured private directory outside served content, with restricted ACLs and no web-server interpreter mappings; object storage is another option. The client never chooses the stored path, and the output stream is disposed before returning. Accepted means staged, not safe: persist pending state and let an isolated worker perform format-aware validation, malware scanning and, where appropriate, content disarm before an explicit release. Never serve pending or failed objects; clean up abandoned/partial uploads. Magic bytes alone are not full validation, and searching binary files for eval( is not malware detection. Removing Unix execute bits does not stop PHP or another interpreter from reading a file, and does not protect a vulnerable image/document parser. Content remains untrusted after storage. Authorize downloads and use attachment/nosniff headers as appropriate. Source: https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html.
Once an application is compromised, its credentials can extend the attack to other services. Developers often leave secrets in source, configuration, or environment variables. Reduce static secrets, scope workload permissions, and monitor credential use; moving a secret alone does not remove every way to steal it.
T1552 is huge — hardcoded credentials in source code are found in almost every codebase audit. T1555 targets credential stores like browser password managers. T1528 is about stealing OAuth tokens and API keys from running applications. All three are preventable with proper secrets management.
The Drake comparison reinforces the same choice: credentials in code create T1552 exposure, while workload identity and scoped authorization reduce static secrets in the application path. RBAC controls what the identity can access; the identity provider issues the short-lived tokens.
These are deliberately fake values, showing the same T1552 exposure in all three languages. Credentials in connection strings, static fields, or configuration objects are still credentials in source. Removing them from the latest revision does not remove historical copies or revoke access. Rotate or revoke exposed credentials and use secret-scanning tools such as Gitleaks and TruffleHog. The full configuration boilerplate is not needed to recognize the pattern.
Workload identity removes the bootstrap credential from application code, reducing T1552 exposure; Key Vault stores a secret when the downstream service still needs one. DefaultAzureCredential can use developer credentials locally and managed identity in a configured Azure host. Provision the identity and grant narrowly scoped access, such as Key Vault Secrets User, separately. The code does not configure those permissions or rotate the database secret. Prefer direct identity-based access to the downstream service where supported.
Don't build your own scanner — use GitHub's built-in secret scanning. It covers 200+ partner patterns and blocks pushes before secrets ever hit version control. For private repos, GitHub Advanced Security adds custom patterns and organization-wide coverage. Combined with pre-commit hooks, you get defense in depth for credential leaks.
Secrets hygiene often becomes real only after an incident. Push protection, vault-backed runtime access, and scoped identities address the risk earlier. Deleting a secret from the latest revision does not revoke it or remove earlier copies: rotate or revoke first, then clean up exposure.
Once attackers are in, they don't want to be detected. They may tamper with logs, obfuscate their tools, or masquerade as legitimate processes. The next question is whether we can trust the evidence used to detect and investigate them.
T1027 is about hiding malicious payloads — encoding, encryption, packing. T1070 is log tampering — deleting or modifying logs to cover tracks. T1036 is masquerading — making malicious files look like legitimate system files. These techniques make forensic investigation extremely difficult.
Individually, a shell, an HTTP transfer, and a maintenance command can look legitimate. Correlate the same process identity across this short time window before deciding to investigate or contain it. This synthetic evidence sequence is an illustration, not a ready-made detection rule; expected build behavior provides the baseline.
This excerpt runs on an already-denied export request, not a homegrown login endpoint. In a line-oriented text log, the newline in the untrusted display label can impersonate a second event and mislead investigation. Parameterized message formatting alone does not necessarily escape those characters. Use a structured serializer and bounded, intentional fields instead of embedding arbitrary request text in the message. CWE-117 names this weakness; T1070 describes indicator removal, not every instance of forged log text. Logging integrity matters to investigating that broader defense-evasion behavior, but this example should not be labeled proof of T1070.
The IdP can report a sign-in; the application can explain which resource and operation were denied and why. identity is the validated authentication context, resource/operation/reason come from server-side policy, and request_id is a server-generated correlation identifier, not a session credential. Configure this audit logger with a message-only line formatter or an equivalent JSON sink; json.dumps escapes embedded control characters so one event remains one JSON record. A real sink should enforce field/size limits, access controls, and explicit delivery-failure handling. Join these events with IdP and platform telemetry; if tagging T1213 as a collection hypothesis, distinguish that analytic context from a confirmed attack. Exporting telemetry does not create cryptographic integrity or immutability. Remote storage, retention/deletion permissions, protected integrity evidence, and missing-event alerts are separate controls shown next.
Application events go to a remote collector under a separate administrative boundary. A protected archive has an explicitly configured retention or write-once policy; the app identity cannot delete records or relax that policy. The SIEM correlates events with identity and endpoint evidence and alerts on missing delivery. Encryption protects confidentiality, not immutability. Cryptographic integrity, where required, needs a trusted verification mechanism and evidence anchored outside the compromised host. Neither an exporter nor a local hash chain makes the application unable to lie or omit an event; independent signals and gap detection remain necessary.
So far we've followed abuse through application features. Now change the entry point: what if malicious code arrives through something the build already trusts? SolarWinds, Shai-Hulud, and event-stream illustrate upstream compromise; Log4Shell illustrates a critical flaw in a trusted dependency. Keep those two failure modes distinct as we walk through the existing cases.
T1195 is the broad category — any compromise of something upstream of you. T1195.001 specifically targets software dependencies — the npm packages, PyPI packages, and NuGet packages we all depend on. The average application has hundreds of dependencies, each one a potential attack vector.
The arc tells a single story: your dependency graph is an attack surface at every layer. event-stream is the historical setup — a tiny trusted package became a weapon after a maintainer handoff. Shai-Hulud and Axios show the modern npm threat: install equals code execution when maintainer accounts are compromised. Notepad++ proves it isn't npm-only — developer tools and their update pipelines are targets too. Log4Shell is the other failure mode: no attacker needed, a trusted library's own flaw was enough. SolarWinds and XZ Utils are the nation-state tier — years of patience, build-pipeline access, and signed artifacts that looked entirely official.
Log4Shell differs from SolarWinds or event-stream: there was no malicious maintainer, poisoned package, or hijacked build pipeline. The library's design had a critical flaw that attackers could exploit. Version 2.15.0 was an initial response, not a current upgrade recommendation; later fixes followed, so use current vendor guidance. The developer lesson is dependency visibility: teams first needed to answer "do we have Log4j?" A current Software Bill of Materials helps locate affected components.
The XZ incident illustrates patient abuse of maintainer trust and release packaging. The actor spent years contributing and gaining influence; the release tarballs contained malicious build-stage material that ordinary source review did not expose. Andres Freund investigated unexpected SSH performance degradation and found the backdoor. RCE means remote code execution; CVSS, the Common Vulnerability Scoring System, rated CVE-2024-3094 at 10.0. The developer takeaway is to verify the built release, not just the source changes.
The source code was clean. The GitHub repo was clean. The problem was the update delivery channel — attacker-controlled infrastructure intercepted update traffic and served trojanized NSIS installers to selected targets. Public project disclosure put the compromise at the hosting/update-infrastructure level, not the source repository. Developer tools are privileged trust anchors: if your editor's updater is hijacked, the attacker inherits the trust of your normal daily workflow. The lesson: signed source does not mean safe delivery channel.
The Axios case combines maintainer-account compromise with an install-time dependency. The hidden dependency's postinstall script downloaded a platform-specific RAT targeting secrets on Windows, macOS, and Linux. The three-hour window and affected versions matter when investigating exposure. The weekly-download figure describes package reach, not a confirmed victim count. RAT means remote access Trojan. The developer takeaway is to treat dependency installation as code execution under the build identity.
Speaker note: The joke is that patching is not a button. For developers, the real work is asset inventory, safe rollout, compensating controls, and post-patch detection.
Three complementary controls, not proof of safe dependencies. npm audit, pip-audit, and dotnet's vulnerable-package listing check known advisories; Bandit analyzes Python code. Lockfiles and hashes reproduce selected bytes, including malicious bytes if an approved release is compromised. npm ci --ignore-scripts disables dependency lifecycle scripts during installation; --omit=dev is appropriate for a production dependency install, not every build/test job. Packages needing native builds require a narrowly reviewed build path, not blanket script re-enablement. npm audit signatures verifies supported registry signatures and provenance attestations; validate their source/build identity against your policy rather than treating a valid signature as a malware verdict. Use least-privileged isolated runners without production credentials for every ecosystem, including Python source builds. Syft writes the SBOM consumed by Grype; npm sbom --sbom-format cyclonedx is an alternative inventory command. Sources: https://docs.npmjs.com/cli/v11/commands/npm-ci/ and https://docs.npmjs.com/cli/v11/commands/npm-audit/.
This diagram is an illustrative attacker path, not the defensive build pipeline or a universal mapping. In this scenario the compromised package runs an install script (T1059), exposes credentials, and enables account abuse. Its final T1567 step means uploading collected data to a legitimate external web service controlled by the attacker; it does not mean any large response from our own API. Dependency controls address entry; runtime identity and data-access controls limit later steps. Ask where our earlier controls would interrupt this chain.
Data theft is an objective in many attacks. Assume the attacker now has a usable identity and can reach the application. A valid request can still be abusive: which data, how many records, and how much outbound volume are normal for that user?
T1213 is bulk data harvesting — think SELECT * FROM customers. T1567 uses legitimate cloud services like Dropbox or Google Drive to exfiltrate data, making it hard to distinguish from normal traffic. T1020 automates the process with scripts that systematically extract and transfer data.
These cases show why sign-in monitoring alone is not enough: an authenticated identity can read far more data than its normal work requires. Anthem affected 78.8 million people; do not confuse affected people with a verified row-count measurement from our application. LastPass disclosed that targeting a DevOps engineer enabled access to cloud backups. The stolen backup-storage decryption keys exposed stored backup contents, but sensitive customer-vault fields remained encrypted with keys derived from each user's master password. Those master passwords were not included in the stolen data; offline guessing against vaults remained a serious risk. Some other backup data, including the MFA/federation database, had separately stolen decryption keys, so distinguish those layers rather than saying the attacker had all vault keys. Collection from cloud storage is T1530; downloading those backups through their normal API is not by itself T1567. Source: https://blog.lastpass.com/posts/security-incident-update-recommended-actions.
This is an atomic check-and-reserve, not a read followed by an unprotected increment. The example is a fixed UTC-hour quota: 500 rows per request and 2,000 reserved rows per hour for one role. Fixed windows permit a boundary burst; use a correctly implemented rolling window or token bucket if the product policy requires one. On a shared transactional database, provision the current bucket with a unique (tenant_id, subject_id, hour_start) key and a nonnegative integer reserved_rows column. Bind named parameters using the driver's supported syntax; tenant and subject come from validated identity, utc_hour from the server clock, and rows must be a validated positive integer. Authorization and role selection happen first. The single conditional UPDATE serializes competing reservations of the same bucket; no returned row means deny and audit, including a missing/uninitialized bucket. It returns allowed_rows for this request, not the cumulative reserved_rows total. Commit successfully before reading data; datastore failures must fail closed and surface as operational errors. The actual data query must keep its tenant/object predicates and bind allowed_rows as its LIMIT, not merely return the count to an unbounded query. Charge admitted work even if a later step fails; refunds or approved bulk jobs require explicit, idempotent policy. Hard limits complement behavioral analytics. A quota denial is a collection/abuse signal in T1213 context, not proof of T1567 or any exfiltration channel.
Single-process teaching excerpt: the constructor initializes tracking to a Map and logDenied emits the structured denial event shown earlier. The caller constructs key = JSON.stringify([tenantId, subjectId]) from validated identity, serializes the already row-bounded result once, and supplies that exact buffer's nonnegative byte length. Do not accept a client-declared size. Exactly 100 MiB is allowed; exceeding it returns false without adding the rejected bytes. A subsequent small response can still use remaining capacity. The caller must stop before writing headers or payload on false, and send the measured body unchanged on true. These are admitted-byte reservations, not proof of successful delivery; aborted sends remain charged under this conservative policy. Unlike the previous fixed-hour row example, this byte example uses a rolling hour. Within one Node process the check and update do not yield; production replicas require shared atomic storage and expiration, not separate Maps. Streamed exports need chunk reservations before each write. Keep denied attempts in audit telemetry, not in the transferred/reserved counter. This is an application collection/abuse limit; T1567 requires evidence of an external web-service exfiltration channel.
Keep the two decisions distinct. Authenticate and authorize the tenant/resource first, then atomically reserve the row allowance before executing the bounded query. Serialize that bounded result and reserve its actual byte size before sending anything. Either budget can deny and emit an audit event without releasing the payload. Approved reservations stay charged under the conservative policy in these examples; denied candidates are not charged. Request-rate controls can run before this flow, and the SIEM can correlate these events with behavioral and IdP signals.
Now apply the same questions in the development workflow: what can an attacker do to this feature, what can we observe, and what response should follow? Start with identity, sessions, and data access rather than trying to cover the entire matrix.
This is your threat modeling loop. For every feature, ask: what ATT&CK techniques could target this? Then design detections, implement them, and test. The loop is continuous — as new techniques are added to ATT&CK, revisit your features. This is a shift from reactive patching to proactive defense design.
This is a starting-point worksheet, not a universal risk ranking or a one-to-one mapping from weakness to technique. User login raises credential-attack scenarios; session theft and replay are distinct; file upload can enable a web shell if server configuration permits execution. Export endpoints expose collection opportunities. T1567 requires an external web-service exfiltration path, while T1020 requires evidence of automated exfiltration; neither is established by a large response alone. Adjust hypotheses and risk labels for your exposure, privileges, data sensitivity and existing controls. Dependencies can be critical even though this illustrative table labels them Medium.
Use framework or policy-engine authorization with your application's resource rules. Structured events provide technique context for SIEM correlation without declaring every denial a confirmed attack. Adaptive controls use the provider's documented claims, risk APIs and challenge protocol; acr/amr values and risk exposure are not universal, and the app must verify that a requested step-up actually completed. Honey tokens can add high-signal evidence when deliberately designed and monitored. Protected auditing needs storage and operational controls beyond serializing or exporting a record.
The maturity curve moves from isolated log searches to alerts, correlated techniques, and behavioral context. These are complementary capabilities, not replacements for each other. Improve one useful signal and its response before adding more detection complexity.
Prevention combines supported identity controls, resource authorization, safe interpreter APIs and bounded data access. Detection joins application audit context with IdP and endpoint signals rather than rebuilding all their analytics inside the app. Response requires tested session revocation, workload containment and preservation of evidence. These capabilities complement one another; a threshold breach is a policy decision to investigate, not an automatic attribution.
OWASP guidance helps identify weaknesses and design secure code; ATT&CK supplies observed adversary behaviors for threat scenarios, abuse-case tests and detection hypotheses. Combine both in one engineering workflow: prevent with controls and tests, detect with application events and cross-system correlation, and respond with practiced containment and recovery. Neither framework is itself a scanner or a guarantee of coverage. Feed incidents and test results back into the next design review.
Don't try to boil the ocean. Phase 1 is mapping and logging — understand what you're defending and make sure you can see what's happening. Phase 2 adds active detection and automated response. Phase 3 adds advanced capabilities like deception and threat intelligence. Each phase builds on the last.
This is the response handoff: detection only matters if the team knows the first moves. The practical lesson is to predefine containment and evidence-preservation actions for high-risk ATT&CK-tagged alerts.
Security culture is as important as security code. Train your team on ATT&CK, include technique IDs in your Jira tickets, and encourage "red team thinking" in design reviews. The ATT&CK Navigator is a free tool for visualizing your coverage — a visual heat map of your security posture is worth a thousand bullet points. Ask: "If I were an attacker, how would I abuse this feature?"
Start with these application policies while configuring the identity provider's supported credential-attack and risk protections. Frameworks, gateways and policy services can implement much of the mechanism; your team defines the resource rules, integrations and tests. Object and tenant authorization limits what an authenticated identity can reach. Export budgets limit admitted work and data release. Safe input APIs keep data separate from interpreters. Keep useful behavioral monitoring and consume provider signals rather than asserting that every custom detection is inherently wrong. Test app-session revocation alongside the IdP's own lifecycle controls.
Security TODOs can leave opportunities for attackers. Use ATT&CK to prioritize the ones relevant to your application's exposure and likely attack paths. This is a reminder to act on the three priorities from the previous slide, not an instruction to fix every possible technique at once.
If you remember nothing else: OWASP and ATT&CK are complementary, not competing, and neither guarantees complete security. Identify likely attacker behavior, instrument the relevant signals, and define a response. Configure the identity provider for credential attacks, and spend your own effort on authorization, export limits, and input boundaries — the controls that stay broken until a developer fixes them. Revisit the crooked-line visual: we do not need to predict every move to make that path harder and more observable.
Thank you! I'm happy to take questions. If we run out of time, catch me in the hallway or reach out on BlueSky or LinkedIn.
Your feedback shapes the next revision of this deck. Share what worked, what didn't, and where you'd like me to focus next time, either in person or through the contact links on the next slide.
Here are resources to continue your journey. The ATT&CK framework site and Navigator are your primary tools. D3FEND is MITRE's companion project that maps defensive countermeasures to techniques. And please reach out — I love talking about this stuff.