Application security
Secure design, implementation, testing, and response throughout the software lifecycle.
67 guides · 21 labs · 12 local projects · 3 learning paths
Start here
Secure coding
Keep untrusted input out of queries, markup, commands and parsers, and fail safely.
Fix SQL injection in Python sqlite3
Bind query values separately from SQL syntax, then prove the fix preserves legitimate behavior and blocks an injection probe.
Prevent XSS at the output boundary
Choose text rendering, context-aware encoding, or deliberate HTML sanitization according to how the browser will interpret the value.
Handle failures without leaking internals or weakening controls
Give callers a useful, bounded response while preserving protected diagnostic evidence and safe application state.
Ship without debug modes, verbose errors or exposed files
Turn off debug output, publish an allowlisted build and probe each deployment for exposed files and endpoints.
Keep untrusted values out of executable syntax
Choose a safe interface for each interpreter and bound the work performed by parsers and converters.
Run external programs without command or argument injection
Run programs from an argument list with no shell, end options before user values, and keep untrusted arguments away from Windows batch files.
Keep untrusted input out of eval, expression languages and templates
Keep code and templates constant, pass input as data, and treat in-process sandboxes as one layer, not a boundary.
Preserve framework security controls across the request lifecycle
Trace middleware, persistence and rendering so custom code does not bypass the protections supplied by the framework.
Parse bounded JSON and XML into an explicit application schema
Separate parser capabilities from schema rules, reject ambiguous and coercible inputs, and implement the boundary in a complete Python project.
Keep untrusted object keys out of prototype and authority paths
Trace an actual nested-merge mutation, replace it with a narrow schema, and make authorization independent of inherited properties.
Prevent CRLF header injection, log forging and CSV formula injection
Carriage return and line feed separate HTTP header fields, log records and CSV rows, and a leading = turns a spreadsheet cell into a formula. Let the framework write headers, log structured JSON, and neutralize formula triggers in exports.
Prevent NoSQL, LDAP, XPath and expression-language injection
SQL is not the only query language that receives user input. Stop MongoDB operator injection with type checks and $eq, escape LDAP filter values, pass XPath values as variables, and never evaluate SpEL or OGNL built from request data.
Validate input safely and prevent regex denial of service
Find and fix exponential and quadratic regex backtracking with measured examples, choose linear-time engines or timeouts, and build allowlist validation that decodes once and rejects type confusion and parameter pollution.
Prevent file inclusion: LFI, RFI, dynamic imports and template names
Stop request data from choosing which file, module or template your application loads: local and remote file inclusion in PHP, dynamic require() and import() in Node.js, importlib and template names in Python, Thymeleaf view names in Spring, render in Rails, and the allowlist fix that works in all of them.
Create temporary files safely: mkstemp, O_EXCL and private directories
Stop symlink, hardlink and pre-created-file attacks on the temporary files your code writes in shared directories such as /tmp: create each file atomically with O_EXCL and owner-only permissions through mkstemp or its equivalent in shell, Python, Java, Node.js, Go, .NET and Rust, keep related files in a private directory, and treat the Linux protected_* sysctls and systemd PrivateTmp as a backstop.
Case study: Spring4Shell (CVE-2022-22965)
How Spring's data binding let request parameters reach the class loader on JDK 9 and later in 2022, why the denylist workaround leaked, and what it teaches about framework-level mass assignment.
Languages and memory safety
Find and contain memory-safety bugs in C and C++, and audit unsafe Rust and FFI.
Find, fix and contain memory-safety bugs in C and C++
Recognize out-of-bounds access, use after free, NULL dereference and integer overflow in C and C++, fix them with checked lengths and single ownership, harden release builds, catch the rest with sanitizers and fuzzing, and plan new code in a memory-safe language.
Audit unsafe Rust, FFI boundaries and crate dependencies
Know what the Rust compiler proves and what it leaves to you: review unsafe code for soundness, wrap foreign calls in small safe functions, decide how overflow and panics behave in release builds, gate crates with RustSec, and replace C one boundary at a time.
Access control
Check every object, function and field against the caller, not the request.
Enforce authorization for every object action
Authorize the authenticated subject, requested operation, and specific resource together; a valid login or unpredictable identifier is insufficient.
Authorize functions and fields as well as objects
Prevent mass assignment, sensitive-field exposure and administrative actions that a valid object lookup alone cannot protect.
Case study: First American's EaglePro document exposure (2019)
How sequential IDs in unauthenticated links exposed First American's title documents in 2019, after its own penetration test found the flaw and a clerical error let it go unfixed.
Browser security
Request forgery, cross-origin access, content security policy and rich text.
Protect cookie-authenticated actions against CSRF
Make the server distinguish an intended action from a request another site caused the browser to send.
Configure CORS and CSP around the browser capabilities you need
Control who can read API responses and which resources a page may execute without confusing either control with authorization.
Render rich text without granting it application privileges
Choose text rendering or a restricted HTML policy, then test the final DOM after every transformation.
Prevent DOM XSS, postMessage abuse and client-side injection
Trace attacker-controlled values from the URL, messages and storage to the browser APIs that parse HTML or run code, then close those sinks with safe APIs, a sanitizer, Trusted Types and strict message checks.
Configure HTTP security headers from a tested baseline
Send the response headers that make browsers enforce HTTPS, run only your own scripts, refuse hostile framing and keep cookies on one host, then prove they appear on every kind of response.
Secure features
Uploads, archives, webhooks, outbound fetches and concurrent operations.
Design a safer file-upload pipeline
Treat upload receipt, content processing, storage, and later delivery as separate security decisions.
Verify webhook deliveries before acting
Verify the provider’s signature over the correct bytes, then apply replay, authorization, and idempotency controls before a business action.
Constrain server-side fetches to their intended destinations
Prevent untrusted URLs from turning a preview, import, or webhook feature into access to internal services.
Preserve business invariants under concurrent requests
Make a balance, reservation or approval rule hold when requests overlap, retry and fail halfway through.
Extract archives without escaping the destination
Prevent traversal, link tricks, overwrites and decompression exhaustion while preserving legitimate file extraction.
Convert untrusted office documents and PDFs in an isolated parser
DOCX, XLSX and ODF files are ZIP archives of XML, and PDFs can carry scripts; the converters that read them are large C and C++ programs with a long CVE history. Check the package before any parser reads it, convert each document in a container with no network, no writable root and hard limits, kill it at a deadline, and re-render to pixels when people will open the result.
Secure WebRTC signaling, TURN relays and media servers
WebRTC encrypts media, but signaling decides whose keys you trust and TURN relays wherever it is allowed. Authorize every signaling message, mint short-lived TURN credentials, deny internal addresses and harden media servers.
Prevent business-logic flaws with server-side invariants
Recompute prices on the server, run workflows as state machines, build reset links from configuration, limit valuable flows, and turn each business rule into an invariant that the database and the tests both check.
Redirect users only to destinations you control
Check every redirect target taken from a request, so attackers cannot borrow your domain for phishing, carry OAuth tokens off-site or steer a server-side fetcher through your redirects.
Consume third-party APIs safely (OWASP API10:2023)
Treat every upstream and third-party API response as untrusted input: validate it against a schema, set connect and read timeouts, cap the response size, refuse to follow redirects blindly, keep TLS verification on, and scope the credentials you hold for each provider.
Design and code review
Find trust-boundary failures before they ship: threat models and change review.
Review tenant boundaries before implementation
Trace tenant identity through requests, data stores, caches, jobs, and administration, then turn plausible isolation failures into tests.
Review a code change through its trust boundaries
Turn a pull request into a small set of security claims that can be checked against the surrounding system and tests.
Choose a threat modeling method and turn its findings into tests
Use the four-question frame to scope a design, pick STRIDE, attack trees, LINDDUN or PASTA by the question you need answered, and leave behind threat records and abuse-case tests the team can run.
Write testable security requirements and a security Definition of Done
Derive security requirements from OWASP ASVS 5.0, abuse cases and the threat model, write them as acceptance criteria a test can fail, trace each one to a test or evidence, and close stories against a short security Definition of Done.
Abuse case library: tests and controls by feature
Recurring abuse cases for sign-up and login, password reset, MFA enrolment, invites, file upload, payments and coupons, webhooks, search, export, admin and AI chat or agents: the attacker goal, preconditions, a concrete test and the control, each linked to CWE and CAPEC.
Threat pattern library for threat modeling
Thirty-two recurring threat patterns grouped by identity, authorization, parsers, concurrency, integrations, data exposure, supply chain, availability and AI agents, each with where it shows up on a data-flow diagram, mitigations, a verification step and STRIDE, CWE and CAPEC links, plus a YAML catalog and a script that lists patterns per diagram element.
Secure design patterns: a catalogue for insecure-design (A06)
A catalogue of reusable secure-design patterns, grouped by concern, each with when to use it, a short example and the failure it prevents: choke-point authorization, capability tokens, parse-don't-validate, safe-by-default APIs, tenant scoping, fail-closed, idempotency, and more.
Worked threat model: a mobile banking app
A complete worked threat model for a mobile banking-style app: the data-flow diagram and its elements, STRIDE threats on the client, the transport and the backend, and the mitigations mapped to OWASP MASVS v2 controls and current Android and iOS platform APIs.
Worked threat model: an LLM agent with tools
A complete worked threat model for an LLM agent with tools: the data-flow diagram, the threats from prompt injection, excessive agency, data exfiltration, confused-deputy and memory poisoning, and the mitigations, mapped to the OWASP Top 10 for LLM Applications 2026 and the OWASP Top 10 for Agentic Applications 2026.
Security testing
Static and dynamic analysis around a verified finding, and parser fuzzing.
Combine static and dynamic tests around a verified finding
Use analysis tools to find evidence, then retain an application regression that demonstrates the failure and the fix.
Fuzz a parser and turn crashes into lasting regressions
Build a narrow harness, observe memory and resource failures, minimize the input, and test the repaired parser.
Place each security control in the pipeline and decide what it blocks
Put secrets scanning, SAST, SCA, IaC and image scanning, DAST, fuzzing, attestation and admission control where each sees the right input, block only on findings a developer can act on, and deploy only images the pipeline has attested.
Choose and evaluate application security testing tools
What SAST, DAST, IAST, SCA, secrets, IaC, container and API scanners and ASPM each find and miss, where each runs, how to evaluate them on a seeded repository with per-CWE scoring and triage cost, and which to adopt first.
Mobile, desktop, embedded and smart contracts
Platform storage and permissions, privileged bridges, firmware updates and smart-contract invariants.
Review trust boundaries in mobile and native clients
Protect local data and platform interactions while keeping server-side authorization independent of the client’s behavior.
Review mobile storage, links and platform permissions
Keep local artifacts and platform entry points from exposing authority that belongs on the server.
Constrain desktop IPC and privileged helpers
Keep renderer content, local callers and plugins from turning a narrow feature into unrestricted operating-system access.
Design firmware updates that can recover safely
Connect trusted boot, update authorization, rollback policy and end-of-life behavior to the device’s actual failure modes.
Test smart contracts through state invariants
Define the properties funds and privileges must preserve, then exercise callbacks, transaction sequences and exceptional conditions locally.
Harden an Android app's storage, network and entry points
Encrypt local secrets with Android Keystore keys now that Jetpack Security is deprecated, set transport policy in Network Security Configuration, close exported components and mutable PendingIntents, confine WebViews, verify App Links and judge Play Integrity verdicts on the server, mapped to OWASP MASVS 2.1.
Harden an iOS app's storage, network and entry points
Store secrets in the Keychain with the narrowest accessibility class, set file protection explicitly, keep App Transport Security on, prefer universal links, constrain WKWebView bridges, keep step-up keys in the Secure Enclave, verify App Attest on the server and ship an accurate privacy manifest, mapped to OWASP MASVS 2.1.
DeFi attack patterns: oracles, flash loans, proxies and signatures
The four smart-contract attack classes beyond reentrancy: price-oracle manipulation, flash-loan amplification, upgradeable-proxy storage collisions and uninitialized implementations, and signature replay, each with vulnerable and fixed Solidity, Foundry tests and verified incidents.
Case study: CrowdStrike Channel File 291 (July 2024)
How a CrowdStrike content update crashed Windows hosts in July 2024: a field-count mismatch hidden by wildcard tests, a missing bounds check, and no staged rollout.
Vulnerability response
Respond to a vulnerable dependency and run a disclosure process.
Respond to an exploited or malicious dependency
When a dependency vulnerability is being exploited, or a package version is malicious, triage gives way to incident response: find every running copy, contain it, patch and redeploy, hunt for exploitation, rotate secrets and close on deployed evidence.
Run coordinated vulnerability disclosure from report to advisory
Make private reporting discoverable, verify reports, coordinate a tested remedy, and give affected users enough information to act.
Case study: Log4Shell (CVE-2021-44228)
How a JNDI lookup in Apache Log4j became internet-wide remote code execution, why transitive and shaded dependencies made it so hard to patch, which mitigations held, and what it teaches about dependency inventory and SBOMs.
Start with a VDP, then add bug bounties and penetration tests
Publish a vulnerability disclosure policy and security.txt first, add a bug bounty only when you can triage and fix at the pace rewards bring, and buy penetration tests with a written scope, rules of engagement and a retest.
Case study: MOVEit Transfer mass exploitation (CVE-2023-34362)
How one unauthenticated SQL injection in an internet-facing file transfer product let the Cl0p group steal data from thousands of organizations in days in 2023, how defenders found the web shell, and the vendor and customer controls that would have limited it.
Set up a PSIRT: services, intake, triage, SLAs and the CNA role
How to structure and run a product security incident response team on the FIRST PSIRT Services Framework: organizational models, intake channels, triage, response targets, a case record and SLA script, becoming a CVE Numbering Authority under the CNA rules 4.2.0, and coordinating with other vendors.
Publish security advisories with CSAF, VEX, CVE records and OSV
What a useful security advisory contains and how to publish it in the formats tools read: a validated CSAF 2.0 VEX document, the CSAF provider files, CVE records through a CNA (CVE Record Format 5.2), and OSV records for open-source packages. Covers the status of CSAF 2.1 as of October 2026.
Respond to product security incidents: plan, tabletop and postmortem
An incident response plan for product security incidents (exploited vulnerabilities, compromised releases, leaked signing keys, malicious dependencies shipped to customers), aligned with NIST SP 800-61 Rev. 3, with reporting clocks, a two-scenario tabletop exercise template and a blameless postmortem template.
Case study: Citrix Bleed (CVE-2023-4966)
How a NetScaler memory over-read leaked session cookies that bypassed passwords and MFA in 2023, why patching did not end hijacked sessions, and how the pattern returned in 2025.
Developer foundations
Start at the data boundary. Build habits that make common defects harder to introduce.
Learning path · 18 stepsAppSec in practice
Review a system, build a test plan, and carry a finding through to verified remediation.
Learning path · 18 stepsBrowser and API boundaries
Protect state changes, browser policies, rendering, object access and concurrent business operations, repairing seven concrete defects along the way.
CSRF lab: Protect an account change from a forged request
An account settings handler trusts the session cookie even when another site causes the request. Repair the request policy and preserve legitimate settings updates.
Path traversal lab: Keep archive entries inside their extraction directory
An import job writes archive entry names directly beneath its work directory. An entry containing ../ can escape. Repair the entry-name policy before any file is created.
Race condition lab: Repair a balance check that races
Two simultaneous purchases both see the same balance and spend more credit than the account owns. Replace the read-then-write sequence with the supplied atomic debit.
IDOR lab: Repair an invoice authorization bug
Bob can retrieve Alice’s invoice by changing the URL. You own the endpoint’s permission check. Reproduce the leak, patch the function below, and prove that Alice can still read her invoice.
Webhook signature and replay lab: Repair a webhook handler that pays twice
Your handler rejects some genuine deliveries and records replayed payments twice. Fix the function below, then test valid, changed, replayed, and concurrent deliveries.
SSRF lab: Guard an outbound fetch against SSRF
A webhook sender fetches whatever URL the user gives it, and follows redirects. An attacker can point it at the cloud metadata service or an internal admin page, directly or through a redirect. Repair the guard so it resolves each host and refuses private addresses, in literals, encodings and redirects alike.
CORS policy lab: Grant CORS to the right origins only
An API reflects whatever Origin a request carries and allows credentials, so any website can read a signed-in user’s data from it. Repair the CORS policy so it grants access only to the configured origins and keeps caches correct.
CSP builder lab: Build a strict Content-Security-Policy
A page sends a Content-Security-Policy whose script-src allows any HTTPS origin, so an injected external script runs despite the nonce. Repair the builder so it produces a strict nonce-based policy from the document’s declared needs.
HTML sanitization lab: Configure a sanitizer for user comments
A comment feature keeps the formatting users submit, but its sanitizer configuration re-enables event handlers, unknown URL schemes and extra tags, so a comment can run script. Repair the configuration so only a small set of formatting tags and safe links survive.
Business logic lab: Price an order without trusting the client
A checkout trusts the price and quantity each cart line sends, adds every coupon code it recognizes, and never limits redemptions. Customers can choose their price, order negative quantities and stack discounts. Repair the pricing function so every amount comes from server data and every coupon rule holds.
SQL injection lab: Separate SQL structure from data with sqlite3
A product search builds its SQL by formatting the shopping category and search term straight into the query string, so a crafted term can read other tables or return every row. Repair it to use sqlite3 parameters, while keeping the legitimate search working.
Path traversal lab: Serve an uploaded file without path traversal
A download endpoint joins the requested name onto the upload directory and opens it, so ../ or an absolute path reads files anywhere on disk, and a symlink in the directory points outside it. Repair it to resolve the final path and refuse anything outside the upload directory.
SSRF validation lab: Validate a fetch URL with ipaddress
An image importer fetches any URL a user submits, so a crafted URL reaches the cloud metadata service or an internal host, directly or through an encoded IP literal. Repair the validator so it parses the URL, resolves the host and allows only public addresses.
Open redirect lab: Return users to this site only
After sign-in, the login page redirects to whatever its next parameter says, so a link to your own domain can end on a look-alike site. Return users only to a page on this site.
File upload lab: Accept avatars without publishing pages
A profile-picture upload trusts the client’s Content-Type and file name, stores files in the public folder and serves them with the client’s type, so an SVG with a script runs in the site’s origin. Repair the upload decision.
OS command injection lab: Run git without a shell
A repository browser pastes request values into a git command line for the shell, so a semicolon starts a second command and a leading dash becomes a git option. Run git directly, with each value as one argument.
Mass assignment lab: Update only the fields users may edit
PATCH /me merges the whole request body into the stored user, so adding "role": "admin" turns a member into an administrator. Change only the fields a user may edit on their own profile.
Buffer over-read lab: Stop a heartbeat over-read
A model of C memory: a TLS heartbeat handler echoes as many bytes as the request claims, not as many as arrived, so a short request returns the next client’s cookie, as Heartbleed did. Add the bounds checks.
SSTI lab: Render a welcome banner without template injection
A welcome banner is built by concatenating the user’s display name into a Jinja template string, so a crafted name is evaluated as a template: it runs expressions, bypasses autoescaping and reads the render context. Repair render_welcome so the name is data, not template source.
XXE lab: Parse an uploaded invoice without resolving external entities
An invoice importer parses uploaded XML with a parser that resolves external entities, so a crafted DOCTYPE reads a local file into the response (XXE). Repair parse_invoice so the parser never resolves external entities, loads no DTD and makes no network request.
Pickle deserialization lab: Load a session token without pickle
A session loader parses JSON but falls back to pickle for “legacy” tokens, so a crafted pickle runs code when it is loaded. Repair load_token to parse JSON only and validate it against a fixed schema, and leave no path that unpickles untrusted bytes.
Secure a real HTTP API
Run Express requests with real signed tokens. Repair invoice authorization and stop user-controlled properties from changing roles or tenants.
Make a checkout transaction atomic
Reproduce two competing withdrawals against SQLite, then repair the transaction so funds cannot be spent twice.
Keep extracted files inside their boundary
Inspect actual ZIP entries and repair a path policy. Test traversal, links, duplicate entries, expansion limits and ordinary nested files.
Parse bounded JSON and XML invoices safely
Repair a real JSON/XML parser boundary: reject duplicate fields, coercion, unknown schema, DTDs and oversized documents while keeping valid invoices usable.
Repair a native parser using sanitizer evidence
Compile a C parser with AddressSanitizer, observe a real out-of-bounds access, and repair allocation, length and output-capacity checks against boundary tests and 80 generated frames.
Stop prototype pollution at merge and authorization boundaries
Observe real prototype mutation from nested JSON merges, then repair schema selection, own-property authorization and arbitrary-key dictionaries without breaking valid updates.
Repair a security pipeline for GitHub Actions and GitLab CI
Repair two pipelines that run gitleaks, Semgrep, OSV-Scanner, Trivy and cosign but cannot fail and trust pull requests, until every action is pinned to a commit SHA, every image to a digest, gates block and only the main branch signs the pushed digest.
Keep an iOS app's storage, links and web bridge in bounds
Repair a SwiftUI banking app until its XCTest suite passes in the iOS Simulator: Keychain storage, App Transport Security in the built Info.plist, link handling and a WKWebView bridge.
Repair an Android app's links, components, network policy and storage
Repair a Kotlin banking app until its Robolectric tests and Android Lint pass in a pinned SDK container: links and intent redirection, exported screens, network security configuration and Keystore-encrypted storage.
Stop untrusted pages from using an Electron bridge
Run a real Electron notes app whose preload passes any IPC channel through and whose main process trusts every sender. Repair the sandbox, the contextBridge API, the sender and payload checks, navigation and window rules, the CSP and permissions until a link, a foreign page or injected script can no longer write files or launch programs.
Recover from failed firmware updates with A/B slots
Repair the update client and bootloader of a simulated dual-slot device: Ed25519-signed manifests, a monotonic anti-rollback floor, writes to the inactive slot, trial boots with automatic fallback and authenticated recovery. The tests inject bad signatures, signed rollbacks, power loss at every flash write and images that never confirm, and write an evidence file of each failure and the boot that followed.
Convert untrusted documents in a locked-down container
Repair a document worker that drives LibreOffice and Ghostscript in Docker, until a zip bomb and DTD payloads stop at preflight, the container has no network or writable root, and a hung conversion is killed with its container.
CSP evaluator
Check a Content Security Policy for unsafe-inline, unsafe-eval, wildcards and broad schemes, missing object-src, base-uri and frame-ancestors, and weak reporting.
Security headers analyzer
Check pasted response headers: HSTS, CSP, X-Content-Type-Options, framing, Referrer-Policy, Permissions-Policy, COOP, COEP, CORP, caching and Set-Cookie attributes.
ReDoS checker
Find nested quantifiers, ambiguous alternatives, adjacent overlapping repeats and unanchored leading repeats in a regex, and time it against generated attack strings.
security.txt validator
Validate a security.txt file against RFC 9116 and the IANA field registry: Contact, Expires, Preferred-Languages, https URIs, Canonical, CSAF and the OpenPGP signature wrapper.