Skip to content

CISSP Certification Guide / Chapter 30

Software Security Testing and Secure Coding

Testing and coding as one craft: how software actually gets broken and the tools and disciplines that find and prevent the breaks. The shared vocabulary of defects (CWE as the taxonomy of weakness types, CVE as specific instances, CVSS as severity, the NVD, the roles of MITRE and FIRST, the CWE Top 25), the OWASP Top 10 in its 2021 and 2025 editions and what moved between them, the injection family (SQL injection, command, LDAP, XPath, template, NoSQL) and parameterized queries as the defining control, cross-site scripting in its reflected, stored, and DOM forms with context-aware output encoding and Content Security Policy, CSRF and the synchronizer token plus SameSite, server-side request forgery with private-range blocking and cloud metadata defenses, broken access control and IDOR, insecure deserialization, XML external entities, error handling and CWE-209, secure logging, the tool family (SAST, DAST, IAST, RASP, software composition analysis, fuzzing, manual review, penetration testing) and where each sits in the pipeline and what each can and cannot see, secure coding as a discipline anchored in NIST SP 800-53 Rev. 5 SI-10 and SA-11, NIST SP 800-218 SSDF Version 1.1 practices PW.4 through PW.9, OWASP ASVS, ISO/IEC 27001:2022 Annex A controls 8.28 and 8.29, and PCI DSS v4.0 Requirement 6, with 20 practice questions and rationales.

The contract between components

Every vulnerability class in this chapter is a broken contract between two components. A contract says what one component may pass to another and what the second component may do with it. A web application is a chain of such contracts. The browser passes an HTTP request to the framework. The framework passes strings to the query builder. The query builder passes text to the database. The database returns rows that become HTML in a page. Security fails when one link in the chain treats what it received as something it is not: data from an earlier link treated as code, or untrusted data treated as trusted.

Injection, cross-site scripting, server-side request forgery, and the rest of the taxonomy that follows are the same underlying event in different costumes: one component accepted something it should not have accepted, or another component interpreted what it received more strongly than it should have. The coder’s half of this craft is keeping the contracts intact: never let untrusted input become code, never let it reach a sensitive sink unencoded, never trust a check that can be checked again. The tester’s half is auditing the seams: reading the code with the contract in mind, then hitting the running system with the inputs the contract forbids and watching what the seams do.

This chapter teaches both halves and the machinery that makes them repeatable. It is the craft chapter of this book’s Domain 8. The previous chapter built the lifecycle frame and the pipeline; this one fills the frame with the taxonomy of defects, the tool family that finds them, and the coding disciplines that keep them out. The exam draws on this material in two ways: it asks you to recognize a defect class from a description, and it asks you to choose the control that removes the class rather than the control that patches one instance. That second skill, choosing the root-cause control, is what this chapter is for.

The vocabulary that names a defect

Before the exam, and before any serious conversation about software risk, you need the three-layer vocabulary that the industry uses to talk about defects, because each layer answers a different question and the three are constantly confused.

CWE, the Common Weakness Enumeration, is the taxonomy of weakness types. It is a community-developed list curated by the MITRE Corporation, and each entry names a kind of defect, not a specific instance of one. CWE-89 is SQL Injection as a type. CWE-79 is Improper Neutralization of Input During Web Page Generation, the type commonly called cross-site scripting. CWE-918 is Server-Side Request Forgery. CWE-352 is Cross-Site Request Forgery. CWE-502 is Deserialization of Untrusted Data. CWE-611 is Improper Restriction of XML External Entity Reference. When someone says “the application has an input validation problem,” they are speaking at the CWE level: a type, a family, a shape that the defect takes.

CVE, the Common Vulnerabilities and Exposures catalog, names specific instances. A CVE identifier is a fixed record for one discovered vulnerability in one product, assigned by a CVE Numbering Authority under the CVE Program operated by MITRE. CVE-2021-44228 is Log4Shell, a specific code execution vulnerability in one logging library. The CVE record carries the identifier, a short description, affected versions, and references. The same weakness type can produce hundreds of CVEs, because every product that repeats the mistake gets its own instance.

CVSS, the Common Vulnerability Scoring System, is the severity yardstick. It is maintained by FIRST, the Forum of Incident Response and Security Teams, not by MITRE. The base score, a number from 0 to 10, expresses how bad an instance is through vectors: how the vulnerability is exploited, what privileges the attacker needs, whether it requires user interaction, and what it damages. CVSS v3.1 is the version most organizations still use in their risk processes, and CVSS v4.0, published in November 2023, is the current revision. The NVD, the National Vulnerability Database run by NIST, is the place where CVE records are enriched with CVSS scores, affected-version data, and references, and it is the reference database most vulnerability scanners match against.

The three layers chain together: a CVE record for a specific product defect carries a CVSS score, and both are instances of a CWE type. MITRE also publishes an annual CWE Top 25 Most Dangerous Software Weaknesses, ranked from real CVE and NVD data, which is a useful shortcut for where the industry keeps cutting itself. A security manager who can move between these layers fluently can ask the questions that matter: what type of weakness is this (CWE), where has it bitten us before (CVE history), how severe is this instance (CVSS), and is our inventory affected (NVD matching). Those four questions are, in miniature, the entire job of software vulnerability management.

The OWASP Top 10, in two editions

The OWASP Top 10 is the most widely read ranking of web application security risks, produced by the OWASP Foundation from contributed data about real applications. It is not a standard and it is not a threat model; it is a prevalence ranking, a snapshot of what attackers actually exploit in the wild. The exam tests the underlying vulnerability classes, and the classes do not change when the ranking does. But the ranking itself exists in two recent editions, and a current professional should know what moved between them, because the moves teach something about how the field thinks.

The 2021 edition, OWASP Top 10:2021, is the list most training material, job descriptions, and existing scanners still reference:

  • A01:2021 Broken Access Control
  • A02:2021 Cryptographic Failures
  • A03:2021 Injection
  • A04:2021 Insecure Design
  • A05:2021 Security Misconfiguration
  • A06:2021 Vulnerable and Outdated Components
  • A07:2021 Identification and Authentication Failures
  • A08:2021 Software and Data Integrity Failures
  • A09:2021 Security Logging and Monitoring Failures
  • A10:2021 Server-Side Request Forgery

The 2025 edition, OWASP Top 10:2025, published in 2025, reshuffles and renames:

  • A01:2025 Broken Access Control
  • A02:2025 Security Misconfiguration
  • A03:2025 Software Supply Chain Failures
  • A04:2025 Cryptographic Failures
  • A05:2025 Injection
  • A06:2025 Insecure Design
  • A07:2025 Authentication Failures
  • A08:2025 Software or Data Integrity Failures
  • A09:2025 Security Logging and Alerting Failures
  • A10:2025 Mishandling of Exceptional Conditions

The moves between editions are the lesson. Cross-site scripting, which had its own entry as part of A03:2021 Injection, is folded into Injection in 2025, because the data says XSS is high frequency and low impact while SQL injection is low frequency and high impact, and both are failures to neutralize untrusted input. Server-side request forgery, which was A10:2021 on its own, is folded into Broken Access Control in 2025, alongside cross-site request forgery. Vulnerable and Outdated Components broadens into Software Supply Chain Failures, which now spans compromised packages, insecure CI/CD pipelines, and unsafe third-party integrations. Identification and Authentication Failures shortens to Authentication Failures. Security Logging and Monitoring Failures becomes Security Logging and Alerting Failures. And a new entry, Mishandling of Exceptional Conditions, gathers the defects that live in abnormal paths: error messages that leak internals, code that fails open, null-pointer failures.

Two practical lessons follow. First, when a practice test or an interview asks about “the OWASP Top 10,” the safe answer names the classes, not the edition, and if a number is needed, name the edition you mean. Second, the churn is the point: the taxonomy is a tool for conversation, and the underlying CWEs are the durable vocabulary. This chapter teaches the classes through their mechanisms, because a mechanism survives any ranking.

Injection: the code and data contract

Injection is the class where a program assembles code from untrusted input, so the input stops being data and starts being instructions. The canonical case is SQL injection. A query is a string. If the application builds the string by concatenating user input, the user’s text becomes part of the query grammar. A login form that evaluates the condition password = '<input>' changes meaning when the input is ' OR '1'='1, because the query now reads “password equals empty string, or true”, and the database agrees to everything.

The defining control is the parameterized query, also called a prepared statement. The application sends the database a query template with placeholders, and sends the values separately, through a typed channel the database understands as data. The input never enters the grammar; ' OR '1'='1 becomes a string literal that the query compares as a string. This is why the exam’s preferred answer for SQL injection is parameterized queries, and why “validate the input” or “put a web application firewall in front” are compensating controls, not root-cause controls. Validation rejectlists can be bypassed and allowlists are hard to keep complete. A WAF pattern-match is evasion bait. Parameterized queries remove the class.

Defense in depth still applies around the class. NIST SP 800-53 Revision 5 (Security and Privacy Controls for Information Systems and Organizations, September 2020) control SI-10, Information Input Validation, requires the organization to validate input for correctness, appropriateness, and expected structure, which is the discipline that reduces how much hostile input reaches the query layer at all. The database account the application uses should be least-privilege: a web tier that only ever selects should not connect with a schema owner. Output should be encoded for the contexts it reaches. And because injection is a family, not a single disease, the same reasoning must be applied everywhere a program builds a command from strings: operating system commands, LDAP filters, XPath expressions, HTML templates, and NoSQL queries all have grammars that attacker text can enter. The control family is the same in every case: keep data out of the grammar, and where a grammar must be built, build it from a fixed, vetted template and pass values through the language’s binding mechanism.

Two additional classes sit next to injection and are worth naming precisely. Path traversal, also called directory traversal, is the case where an application composes a filesystem path from user input and ../ sequences walk out of the intended directory; the control is canonicalizing and validating the resolved path against the intended root. And log injection is the case where user input containing newlines and control characters forges entries in a log; the control is encoding or rejecting control characters at the logging boundary. Both are the same story: a component received text and treated it as structure.

Cross-site scripting: trust in the rendering

Cross-site scripting, XSS, is the vulnerability where attacker-controlled text is rendered by a browser as code in another user’s session. The broken contract is between the application and the browser’s rendering engine: the application said “this is text,” and the browser correctly executed what the application’s own markup construction allowed. An attacker who can inject a script into a page that a victim visits can run in the victim’s session context: read and exfiltrate the session cookie, submit requests as the victim, rewrite the page to phish the victim, or keylog the victim’s input. The damage is everything the victim can do, because the script runs as the victim.

The three flavors are distinguished by where the injection lives. Reflected XSS is the immediate echo: the input is placed into the response that contains it, and the attack is a URL a victim is tricked into clicking. Stored XSS is the persistent variant: the input is saved by the application, in a post, a profile field, a comment, and served later to every viewer, which makes it the damaging one, because the attacker does not need to re-deliver anything. DOM-based XSS is the client-side variant: the input never reaches the server, or the server returns it unchanged, and JavaScript in the page reads it from a source such as location, document.referrer, or localStorage, and passes it to a dangerous sink such as innerHTML or eval. All three are CWE-79, and the fix in every case is the same discipline applied at the right place.

The primary control is context-aware output encoding. “Context-aware” is the whole sentence. Text destined for the HTML body needs HTML encoding; text destined for an attribute value needs attribute encoding; text destined for a script block needs JavaScript string escaping; text destined for a URL needs URL encoding. Encoding for the wrong context is a false sense of security, and frameworks that auto-encode per context are how modern teams avoid the class entirely. Input validation is the second line: an allowlist can reject characters and shapes that no legitimate input needs. But validation alone is not the answer, because the correct encoding depends on the output context, which validation, running at the input boundary, cannot see.

Two further controls blunt the weapon. Content Security Policy, CSP, is an HTTP header that tells the browser which scripts may execute, by source, by nonce, or by hash; a strict policy means an injected inline script simply does not run, which makes CSP a strong defense in depth even against DOM-based XSS that server-side encoding cannot see. And the HttpOnly attribute on a session cookie tells the browser to withhold the cookie from document.cookie, so a successful script cannot exfiltrate the session token; HttpOnly does not prevent XSS, it limits what a successful XSS can steal, which is exactly the kind of distinction the exam likes to draw.

CSRF: the ambient credential

Cross-site request forgery, CSRF, exploits a property of how browsers handle cookies: they attach them automatically to every request for the cookie’s domain, without asking whether the request came from the user’s intent or from another site. The attack is a state-changing request issued from the victim’s browser at the attacker’s command. The victim visits a page the attacker controls, the page submits a form or an image request to the victim’s banking application, and the browser dutifully includes the session cookie. The bank cannot distinguish “the user clicked the transfer button” from “the attacker’s page asked the browser to click it,” because the evidence the bank has, the cookie, is identical.

The defining control is the synchronizer token. The application embeds an unguessable token in each form and verifies it on every state-changing request; the attacker’s page cannot read the token because the browser’s same-origin policy prevents it, so the forged request arrives without the token and is rejected. The token must be unpredictable, tied to the session, and verified server-side. Related and weaker controls deserve their place in the layered defense: the SameSite attribute on cookies tells the browser not to send the cookie on cross-site requests, and modern browsers treat SameSite as Lax by default, which already stops the classic form-based attack; requiring a custom header, such as an X-Requested-With header, on state-changing endpoints only works where cross-origin requests cannot add custom headers without the site allowing it through CORS. The right hierarchy for the exam: synchronizer tokens are the primary defense, SameSite is a browser-level reinforcement, and header checks and referrer checks are auxiliary.

CSRF is CWE-352. In the 2025 edition it sits inside A01 Broken Access Control, which is a fair home: the attack is an authorization problem, the server failing to confirm that the requester intended the request.

SSRF: the server as a proxy

Server-side request forgery, SSRF, is the vulnerability where an attacker makes the server fetch a URL of the attacker’s choosing. The broken contract is between the application and its own network position: the application sits inside the perimeter, on a network path to internal services and metadata that an internet attacker cannot reach directly, and it is willing to fetch URLs supplied by users. The attacker submits a URL, the server fetches it, and the server’s reach becomes the attacker’s reach. CWE-918 names the type, and it is CWE-918 that the exam expects you to recognize from the description: “the application fetches a URL supplied by the user.”

The reach is the danger. The server can reach its own loopback, the private network, and the cloud metadata service. The classic target is the cloud metadata endpoint, such as the AWS instance metadata service at 169.254.169.254, which on many clouds hands out temporary credentials and instance configuration to any process that can reach it, precisely because it is designed to be reachable only from inside the instance. An SSRF against that address is a credential grab. The second order of reach is internal administration interfaces: databases, orchestration APIs, internal dashboards that assume network trust.

The controls follow the contract. Validate the URL against an allowlist of expected destinations: allowed schemes (https, and not file, gopher, or other oddities), allowed hosts, allowed ports. Resolve the host and reject private, loopback, link-local, and reserved ranges, because an allowlist on the literal hostname can be defeated by DNS tricks and redirects; the final check must happen on the resolved address. Do not follow redirects blindly, because a redirect can carry the request from an allowed external host to an internal one. Route outbound fetches through an egress proxy that enforces the same allowlist centrally, so the defense is not the app developer’s to forget. And for cloud environments, harden the metadata service itself: the AWS Instance Metadata Service version 2, IMDSv2, requires a session token obtained by a PUT request before metadata is served, which stops the common SSRF one-shot fetch. The exam-relevant sentence: SSRF is defeated by not letting the server fetch arbitrary destinations, and the mechanism is validation at the resolved-address level plus egress control.

The rest of the list: access control, deserialization, and configuration

The four classes above, injection, XSS, CSRF, and SSRF, are the mechanisms the exam asks about most inside this domain, but the rest of the Top 10 is the daily work of the profession, and the exam will test your control choices there too.

Broken access control is the number one category in both editions, and it is the most manager-relevant one, because its defects are usually design errors rather than code typos. The representative flaw is the insecure direct object reference, IDOR: the application exposes an object identifier, an account number, a document ID, an order number, and trusts that a user will only ask for their own. The control is authorization enforced server-side on every object access, deny by default, with the check based on identity and ownership, never on what the client chose to display or hide. Function-level access control is the same discipline one level up: every API endpoint and administrative function checks whether the caller is permitted, instead of relying on the UI to hide buttons.

Cryptographic failures, A02:2021 and A04:2025, is the category for the whole family of “we used cryptography wrong”: homegrown algorithms, outdated ones, keys hardcoded in source, weak parameter choices, and transport without TLS. The control family belongs to the cryptography chapter of this book: use vetted, current libraries; never invent algorithms; manage keys in a key management system; enforce TLS everywhere data moves. The exam wants the manager’s framing: cryptography is a component you procure and configure, not one you write.

Insecure design, A04:2021 and A06:2025, is the category for defects that exist before any code is written: missing threat modeling, missing security requirements, trust boundaries in the wrong place. The controls are the lifecycle activities of the previous chapter: threat modeling, misuse cases, design review. It is the one category whose fix is almost entirely upstream.

Security misconfiguration, A05:2021 and A02:2025, is the category for the mundane: default credentials, debug mode enabled, verbose directory listings, open storage, unpatched middleware. Its control is the hardening baseline, configuration management, and the secure-defaults discipline of SSDF practice PW.9, Configure Software to Have Secure Settings by Default. Two neighbors live here: XML external entity processing, XXE, CWE-611, where an XML parser configured to resolve external entities reads local files or makes internal requests on the parser’s behalf, defeated by disabling DTD processing and external entity resolution in the parser; and the general rule that every feature, service, and header not needed should be absent.

Vulnerable and outdated components, A06:2021, became software supply chain failures, A03:2025, and the control is software composition analysis plus disciplined patching, which the previous chapter covered from the pipeline side and this one covers from the tool side in the next section.

Software and data integrity failures, A08 in both editions, covers the cases where code or data is trusted without verification: insecure deserialization, unsigned software updates, and CI/CD pipelines that build without integrity checks. The representative is deserialization of untrusted data, CWE-502: applications that accept serialized objects, such as a Java object stream or a Python pickle, and reconstruct them without controls, which in many languages lets a crafted payload run arbitrary code through gadget chains, prebuilt sequences of legitimate classes whose side effects become the exploit. The control is to avoid deserializing untrusted data entirely, and where it cannot be avoided, to allowlist permitted classes, verify integrity with signatures, and never expose a deserialization endpoint to unauthenticated users.

Identification and authentication failures, A07:2021 and Authentication Failures in 2025, covers the credential and session handling that breaks login: default or weak credentials, missing rate limiting, session identifiers that survive privilege changes or never expire. The controls live in the identity domain of this book: strong credential policies, MFA, secure session management, and session invalidation on privilege change.

Security logging and monitoring failures, A09, renamed to Security Logging and Alerting Failures in 2025, is the category where the application forgets to produce evidence: no logging of authentication failures, no alerting on anomaly, logs that omit the details an investigator needs. Its control is a logging standard that captures security-relevant events with enough fidelity to detect and investigate, without capturing secrets, and the monitoring pipeline of the security operations domain that turns those logs into detection.

The tool family: what each test sees

A testing tool is a way of looking, and each way of looking sees a different slice of the software. The exam asks you to match the tool to the job, and the manager’s version of that question is which combination of tools, placed where, covers the software. The tool family has six members worth knowing cold.

Static application security testing, SAST, analyzes the source code without running it. It reads the code for the patterns of the defect classes: unsafe concatenation into queries, unsanitized sinks, weak cryptographic calls, dangerous deserialization. Because it works on source, it runs on every commit, it finds defects before the code ever executes, and it can point at the exact line. Because it does not run the code, it cannot see runtime state, it cannot verify that an authorization check actually gates a path, and it produces false positives that a human must triage. In the SSDF’s language it is part of PW.7, Review and/or Analyze Human-Readable Code to Identify Vulnerabilities and Verify Compliance with Security Requirements.

Dynamic application security testing, DAST, probes the running application through its external interfaces, sending crafted requests and observing responses, without needing the source. It sees what an attacker sees: reachable endpoints, real responses, actual behavior. It cannot see the code it does not drive, and it can be noisy, but it is the closest automated proxy for the attacker’s perspective, and it is anchored in PW.8, Test Executable Code to Identify Vulnerabilities and Verify Compliance with Security Requirements.

Interactive application security testing, IAST, runs inside the application during testing. The application is instrumented, the tests exercise it, and the instrumentation observes the actual data flow: where input arrives, where it reaches a sink unencoded, whether an authorization check ran. IAST combines the code-level precision of SAST with the runtime truth of DAST, and it is the modern answer to “which tool has fewer false positives,” at the cost of requiring instrumentation and a test environment.

Runtime application self-protection, RASP, is not a testing tool at all; it is a runtime control that sits inside the running application in production and blocks attacks as they happen, intercepting the dangerous call before the damage. RASP is the defense-in-depth complement to the testing family: the tests find what they find, and RASP catches what escaped, but it is not a substitute for finding the defect, because it protects one deployment, not the codebase.

Software composition analysis, SCA, is the dependency scanner. It inventories the third-party components, libraries, and frameworks in the application, records their versions, and matches them against vulnerability databases such as the NVD. SCA answers “what do we run and what is known broken in it,” and it is the mechanical heart of the supply chain category, supporting the SSDF practice PW.4, Reuse Existing, Well-Secured Software When Feasible, which includes verifying the security of acquired components. SCA without remediation is inventory; with a patch loop, it is vulnerability management.

Fuzzing is the input hammer. A fuzzer feeds a program large volumes of malformed, unexpected, or randomly mutated input and watches for crashes, hangs, memory errors, and assertion failures. It is how robustness defects, integer overflows, parsing bugs, and unhandled states are found in code that looks fine in review, and it belongs to PW.8, executable code testing. Coverage-guided fuzzers, which mutate inputs toward new code paths, have turned fuzzing from a curiosity into a standard practice for parsers, protocol handlers, and anything that consumes untrusted bytes.

Manual review and penetration testing are the human members of the family, and they are not optional extras. Automated tools find the classes they are programmed to find; the design-level defects, the broken access control logic, the business logic abuse, the subtle race, are found by people reading code with the contract in mind and by testers thinking like the adversary. NIST SP 800-115 (Technical Guide to Information Security Testing and Assessment, September 2008) is the methodology reference for this work, organizing technical assessment into review, target identification and analysis, and vulnerability validation, and it is the standard the exam expects you to recognize as the discipline behind a proper penetration test. The penetration test is the assembled-system check: the tester, working under a defined scope and rules of engagement, uses the tool family as instruments and adds the judgment that no tool has.

The placement question is the pipeline question from the previous chapter. SAST and SCA run on every commit, where the cost is lowest. DAST and IAST run against the test and staging environments, on every release candidate. Fuzzing runs continuously against parsers and input handlers, in the build and in the test phase. Manual review happens at the design level and again before release, on the high-risk changes. Penetration testing happens on a schedule, against the assembled system, and before major changes. The coverage question that every manager should be able to answer is: which slice of the software is not being looked at, and by whom.

The false-positive problem deserves its own sentence, because it is the operational reality that kills tool programs. A scanner that floods the team with findings, most of them not real, teaches the team to ignore the tool. The discipline is triage: a defined workflow where each finding is confirmed, ranked, and assigned, with a tracking system that records the disposition. Tools are made effective by the process around them, not by their noise level, and a program that cannot explain what it does with a finding is a program that does not have a finding process.

Secure coding as a discipline

If testing finds the defect after it exists, secure coding keeps it from existing, and the two are the same craft from different sides of the timeline. The discipline has a recognizable shape, and the exam rewards you for knowing the shape as a whole rather than as a list of isolated tips.

Input validation is the gate at the door. Every external input, every query parameter, header, cookie, file upload, and body field, is untrusted until validated: type, length, format, character set, and value range, against an allowlist of what is legitimate where that is possible. NIST SP 800-53 SI-10 makes the point a control: validate input for correctness, appropriateness, and expected structure. Validation is not a substitute for the other controls, because validation sees the input, not the dozens of contexts the input will later reach; it is the first layer, and it is cheap.

Output encoding is the discipline at the other end. Anything the application renders or passes into another language must be encoded for the context it enters: HTML body, attribute, script, URL, style, SQL literal, LDAP filter, filesystem path. The pair, validate on the way in, encode on the way out, is the two-door version of the contract discipline, and parameterized queries are the same idea for the SQL grammar.

Least privilege governs what the code can do once compromised. The application process runs with the accounts it needs and no more; the web tier connects to the database with a restricted account; the service does not run as root or administrator; administrative functions require elevation. Least privilege does not prevent the defect; it caps what the defect can do, which is the entire purpose of the defense-in-depth argument.

Error handling is the discipline of the abnormal path. The application must fail secure: on failure, deny access, close the session, refuse the operation, never continue with a partially executed action. And the error that reaches the user must be sanitized. CWE-209, Generation of Error Message Containing Sensitive Information, is the type where a stack trace, internal path, SQL fragment, or library version leaks to the user, and it is the flagship of the 2025 category Mishandling of Exceptional Conditions, alongside failing open, CWE-636, where a component under pressure chooses availability over security. The control is a single pattern: generic messages to the user, full details to the logs, logs protected and monitored.

Secure logging is the discipline that makes detection possible. The application logs security-relevant events, authentication outcomes, authorization denials, input validation rejections, privilege changes, administrative actions, with timestamps and identifiers enough to reconstruct an incident. It never logs secrets, credentials, tokens, or full sensitive payloads, because a log is a storage system with its own compromise surface, and the category A09 exists because most breached applications had nothing logged to investigate. Logging is where the software development domain hands off to the security operations domain of this book.

Session and authentication handling follows the identity domain’s rules: session identifiers generated with sufficient entropy, transmitted only over TLS with the Secure flag, withheld from scripts with HttpOnly, constrained in scope with SameSite, expired after inactivity, and rotated when privilege changes. Access control is enforced server-side, deny by default, on every object and function, per the broken access control section. Cryptography is procured, not invented: vetted libraries, current algorithms, keys in a key management system. Secure defaults mean the product ships locked down: no default credentials, debug disabled, unnecessary features off, the baseline of PW.9. And race conditions are respected: a check-then-act sequence, such as checking a balance then transferring, or checking a file then reading it, is vulnerable to the time-of-check-to-time-of-use, TOCTOU, gap, and the control is to make the check and the act atomic, through locking or through an operation that is atomic by construction.

Two organizational facts hold the whole discipline together. First, none of it works if the team does not know it: the SSDF practice PW.5, Create Source Code by Adhering to Secure Coding Practices, is the process-side anchor, and the training that makes it real is the awareness and education machinery of this book’s personnel security material. Second, the discipline is verified by the testing family: the secure coding standard says what the code must be, and the tools and reviews say whether it is, which is why the exam treats the two as one subject.

The standards that demand the craft

The craft is not optional in regulated environments, because the standards name it. ISO/IEC 27001:2022 Annex A carries two controls that put secure coding and testing into an information security management system. Annex A 8.28, Secure coding, requires that secure coding principles be applied to software development, with consideration given to the threats named in this chapter. Annex A 8.29, Security testing in development and acceptance, requires security testing during the development and acceptance phases, covering the phases where the tool family works. Around them, Annex A 8.25 through 8.27 frame the development lifecycle, application security requirements, and secure architecture, and 8.31 and 8.33 separate environments and protect test information, which the previous chapter treated in full.

PCI DSS v4.0 Requirement 6 is the most operational statement of the craft in commerce, because it is a payment-industry requirement audited against evidence. It requires bespoke and custom software to be developed securely, and its sub-requirements require that software to be reviewed prior to release or deployment, using automated tools or manual review, and that code changes be reviewed by an individual other than the originating developer. It also requires the vulnerability management loop around the result: an inventory of bespoke and custom software and the third-party components inside it, ranking of vulnerabilities by risk, and patching within defined timeframes. The two sentences to keep: review before release, and someone else reviews the code.

NIST SP 800-218, the Secure Software Development Framework, SSDF Version 1.1 of February 2022, is the federal government’s unified statement of the practices, and the practices of this chapter map onto it directly. PW.4, reuse existing well-secured software, covers SCA and component verification. PW.5, adhere to secure coding practices, is the coding standard. PW.6, configure the compilation and build processes for executable security, is the toolchain hygiene of the previous chapter. PW.7, review and analyze human-readable code, covers manual review and SAST. PW.8, test executable code, covers DAST, fuzzing, and dynamic testing. PW.9, configure secure settings by default, is the hardening discipline. PO.3, implement supporting toolchains, and PO.4, define and use criteria for software security checks, are the manager’s view: the tools exist as an automated, criteria-driven system, and the criteria are tracked through the lifecycle. In the same ecosystem, NIST SP 800-53 Rev. 5 anchors the controls: SA-11, Developer Security Testing and Evaluation, requires the developer to perform unit, integration, system, and fuzz testing among others; SA-15, Development Process, Standards, and Tools, requires a defined development process with security tools and standards; and SI-10, Information Input Validation, was named earlier as the input gate.

The checklist that makes the craft auditable is the OWASP Application Security Verification Standard, ASVS, whose 4.0.3 version of October 2021 long served as the reference and whose version 5.0 superseded it in May 2025. ASVS organizes verification requirements by assurance level, so an organization can choose the depth its risk demands and audit against a named checklist, which converts “the code is secure” from an opinion into a verified claim against a published list. Between the ISO Annex, the PCI requirement, the NIST frameworks, and the OWASP checklists, the message of the standards is identical to the message of this chapter: software security is a produced property, verified by defined testing and maintained by defined coding discipline, and the exam expects you to know which standard says which part of it.

Practice questions

  1. A login form concatenates the username and password fields directly into a SQL query. An attacker submits ' OR '1'='1 as the password and is authenticated without knowing any credentials. Which control removes this vulnerability class at its root?

    A. A web application firewall rule that blocks the OR keyword in login requests B. Input validation that rejects the quote character in password fields C. A stored procedure that runs the same concatenated query on the database D. A parameterized query that sends the query template and the values as separate data

  2. A support forum renders user posts as HTML. A user posts a message containing a script tag, and the script executes in the browser of every member who opens the thread. What is the vulnerability class, and what is the primary control?

    A. Reflected XSS; block script tags in the editor B. Stored XSS; context-aware output encoding when the post is rendered C. CSRF; require a token on the posting endpoint D. SQL injection; parameterize the query that stores the post

  3. A developer argues that XSS is fully prevented because the application rejects inputs containing <script>. Which response is correct?

    A. Input validation is a blacklist approach that attackers can evade, and context-aware output encoding at the rendering point is the primary control B. Rejecting <script> closes the class completely for all browser contexts C. Validation is the only control that works across all output contexts D. The defense is adequate if the rejection list also includes javascript: and onerror

  4. A logged-in user visits a page on an attacker’s site. The attacker’s page submits a form to the user’s banking application changing the transfer beneficiary. The browser attaches the banking session cookie automatically, and the bank processes the change. What is the vulnerability class, and which defense directly defeats it?

    A. XSS; add HttpOnly to the session cookie B. Session fixation; rotate the session ID on login C. CSRF; require a server-validated unguessable token on state-changing requests D. Clickjacking; add a frame-ancestors directive

  5. An application offers a “fetch a preview” feature: the user submits a URL and the server downloads it. An attacker submits http://169.254.169.254/latest/meta-data/, and the server returns the cloud instance’s metadata, including temporary credentials. What is the vulnerability class, and which control most directly prevents it?

    A. Open redirect; validate that the response is HTML B. SSRF; allowlist permitted destinations and reject requests to private, loopback, and link-local addresses after DNS resolution C. Injection; encode the URL before fetching D. CSRF; require an authorization header on the fetch endpoint

  6. A vulnerability researcher publishes an advisory for a SQL injection defect in a popular library. Which set correctly matches the three layers of vulnerability vocabulary?

    A. CVE is the weakness type, CWE is the specific instance, CVSS is the score B. CWE is the weakness type, CVE is the specific instance, CVSS scores the severity C. CVSS is the weakness type, CWE is the instance, CVE is the score D. CVE and CWE are both instance registries; CVSS is maintained by MITRE

  7. A team wants an automated security check that runs on source code at every commit, before the code is even built. Which tool fits the job?

    A. SAST, which analyzes the source for defect patterns without executing it B. DAST, which probes the running application C. RASP, which protects the production runtime D. SCA, which inventories third-party libraries

  8. A security tester without access to the application’s source code probes a staging deployment by sending crafted requests through its web interface and observing the responses. Which approach is this?

    A. Static application security testing B. Dynamic application security testing C. Software composition analysis D. Manual design review

  9. A scanner inventories every third-party library and framework in an application, records versions, and matches them against vulnerability databases such as the NVD. What is this tool category, and which SSDF practice does it support?

    A. SAST; PW.7, review and analyze human-readable code B. DAST; PW.8, test executable code C. SCA; PW.4, reuse existing well-secured software and verify acquired components D. Fuzzing; PW.6, configure build processes

  10. A security engineer wants to find robustness defects in a parser that consumes untrusted input: crashes, hangs, and unhandled states that review never catches. Which technique is designed for this job?

    A. Fuzzing, feeding large volumes of malformed and mutated input to the executable code B. SAST, scanning the parser source for known patterns C. SCA, matching parser dependencies against CVE data D. Manual review of the parser’s documentation

  11. An application accepts serialized objects from authenticated users and reconstructs them. An attacker crafts a serialized payload that, when reconstructed, executes code through a chain of legitimate classes. What is the class, and what is the correct control?

    A. XXE; disable external entities in the XML parser B. Insecure deserialization; avoid deserializing untrusted data, and where required, allowlist permitted classes and verify integrity C. SQL injection; parameterize the query that stores the payload D. CSRF; add a token to the upload endpoint

  12. An API returns a customer record when given its record number in the URL, and any authenticated user can request any number. A security test shows that user A can read user B’s records by changing the number. What is the class, and what is the fix?

    A. Broken access control; enforce authorization on the server for every object access, checking identity and ownership B. XSS; encode the record number in the response C. Injection; parameterize the database lookup D. Misconfiguration; hide the record number from the URL

  13. An application parses XML documents submitted by users. A crafted document references an external entity that reads a local file, and the application returns its contents. What is the class, and what is the fix?

    A. Path traversal; canonicalize the file path B. XXE; configure the parser to disable DTD processing and external entity resolution C. SSRF; validate the document’s origin D. Deserialization; sign the XML document

  14. An application’s XSS defense relies on a header that tells the browser which scripts are allowed to execute. What is this control, and what role does it play?

    A. HttpOnly; it prevents the browser from running injected scripts B. CSP; it restricts which scripts the browser executes, making injected scripts fail to run as defense in depth against XSS C. SameSite; it stops scripts from reading cookies D. HSTS; it forces HTTPS for the page

  15. A session cookie is set with the HttpOnly attribute. What does this attribute do?

    A. It prevents cross-site requests from carrying the cookie B. It forces the cookie to be sent only over HTTPS C. It prevents client-side script from reading the cookie, limiting what a successful XSS can steal D. It causes the browser to reject the cookie after a fixed number of uses

  16. When an application fails, it returns the full stack trace, internal class names, and database error details to the user’s browser. Which class is this, and what is the correct handling?

    A. Security logging failure; log nothing to avoid exposing details B. CWE-209, error messages containing sensitive information; return a generic message to the user and log full details server-side C. Injection; encode the error text before returning it D. Misconfiguration; this is acceptable because only authenticated users see it

  17. An application logs every request, including passwords, session tokens, and full payment payloads, “so incidents can be investigated.” Which change is correct?

    A. Stop logging request data entirely B. Keep the logs but encrypt them at rest C. Log security-relevant events with needed detail, and exclude secrets, credentials, and sensitive payloads from the logs D. Store the logs on a server that only security staff can reach

  18. An organization implements ISO/IEC 27001:2022 and asks what Annex A 8.29 requires of its development function. Which statement is correct?

    A. Security testing must be performed during development and acceptance phases B. All code must be developed in-house with no third-party components C. Penetration testing must be performed annually by an external vendor D. Developers must complete certification before writing code

  19. An organization develops bespoke payment software in-house. Under PCI DSS v4.0 Requirement 6, which practice is required?

    A. The software must be reviewed prior to release or deployment using automated tools or manual review, and code changes must be reviewed by an individual other than the originating developer B. All code must be covered by a commercial warranty before production use C. Third-party components must be replaced with custom implementations D. Automated scanning alone satisfies the requirement even if no review of the results occurs

  20. A team uses only SAST, run on every commit, and a penetration test then finds a broken access control defect that the scanner never flagged. Which conclusion is correct?

    A. The scanner is defective and should be replaced B. SAST alone cannot verify runtime authorization behavior, so coverage requires complementary methods and manual review C. The defect does not matter because the scanner approved the code D. Access control defects are outside the scope of secure coding

Answers and rationales

  1. D. The vulnerability is SQL injection, and the root-cause control is the parameterized query, which sends the query template and the values as separate data so the input can never alter the query grammar. The WAF rule (option A) and quote rejection (option B) are bypassable compensating controls, and a stored procedure (option C) is only safe if it itself avoids dynamic string concatenation, which the scenario does not establish.

  2. B. The post is saved and served later to every viewer, which is the defining property of stored XSS, and the primary control is context-aware output encoding at the point where the stored text is rendered. Blocking script tags (option A) is a blacklist, the token (option C) addresses CSRF, and parameterization (option D) addresses a different class.

  3. A. Blacklist validation can be evaded with encodings, case variants, and other markup, and validation at the input boundary cannot see the output context, so context-aware output encoding at the render point is the primary control. The other options treat the blacklist as sufficient, which it is not.

  4. C. The attack is CSRF: a state-changing request issued from the victim’s browser with the session cookie attached automatically, so the server must require evidence of intent. The synchronizer token, an unguessable value the attacker’s page cannot read and the server verifies, is the direct defense. HttpOnly (option A) addresses XSS impact, session rotation (option B) addresses fixation, and frame-ancestors (option D) addresses clickjacking.

  5. B. The vulnerability is SSRF, the server fetching an attacker-chosen URL, and 169.254.169.254 is the cloud metadata address. The direct control is destination validation: an allowlist of permitted hosts with a check on the resolved address that rejects private, loopback, and link-local ranges, which stops the fetch before it leaves the boundary. The other options address different classes or control only the response.

  6. B. CWE classifies the weakness type, CVE identifies the specific instance in a product, and CVSS, maintained by FIRST, scores severity. The other options swap the layers or misattribute the maintainer.

  7. A. SAST analyzes source without executing it, which is why it can run on every commit before the build. DAST needs a running application, RASP protects a runtime, and SCA inventories dependencies.

  8. B. Probing the running application through its interfaces without source access is dynamic application security testing. SAST works on source, SCA works on dependencies, and design review happens on documents.

  9. C. The tool is software composition analysis, which inventories components and matches versions against vulnerability data, and it supports SSDF PW.4, reuse of existing well-secured software, which includes verifying acquired components. The other pairings attach SCA to the wrong practices.

  10. A. Fuzzing feeds malformed and mutated input to executable code to expose crashes, hangs, and unhandled states, which is exactly the robustness job, anchored in PW.8. The other techniques look for known patterns or inventory, not robustness under hostile input.

  11. B. The class is insecure deserialization, CWE-502, and the control is to avoid deserializing untrusted data, and where it is unavoidable, to allowlist permitted classes and verify integrity. The other options address different classes or protections that do not stop the gadget chain.

  12. A. This is an insecure direct object reference, a form of broken access control: the server trusts the identifier in the URL. The fix is server-side authorization on every object access, checking identity and ownership. Encoding (option B), parameterization (option C), and hiding the number (option D) do not change who is permitted to read the record.

  13. B. The class is XXE, CWE-611, and the fix is to disable DTD processing and external entity resolution in the XML parser. Path canonicalization (option A) addresses traversal, and the other options address different classes.

  14. B. The control is Content Security Policy, which restricts the scripts the browser will execute, so injected inline scripts fail to run; it is defense in depth against XSS, not a substitute for encoding. HttpOnly (option A) hides cookies from scripts rather than stopping scripts, SameSite (option C) governs cookie sending, and HSTS (option D) enforces transport security.

  15. C. HttpOnly withholds the cookie from client-side script, so a successful XSS cannot read the session token; it limits impact rather than preventing XSS. Secure (option B) and SameSite (option A) are different attributes, and the rejection-after-use behavior (option D) is not what HttpOnly does.

  16. B. Returning stack traces and internal details is CWE-209, and the control is to return generic, sanitized messages to users while logging full details server-side for the investigation. Logging nothing (option A) destroys the evidence, encoding (option C) does not fix the disclosure, and the authenticated-only justification (option D) is not a defense.

  17. C. The correct posture is logging security-relevant events with the detail needed for detection and investigation, while excluding secrets, credentials, and sensitive payloads, because logs are a storage system with their own compromise surface. Stopping all logging (option A) removes the evidence, encryption (option B) and access restriction (option D) reduce exposure but leave the secrets captured.

  18. A. Annex A 8.29, Security testing in development and acceptance, requires security testing during development and acceptance phases. The other options state obligations the control does not impose.

  19. A. Requirement 6 requires bespoke and custom software to be developed securely, reviewed prior to release or deployment using automated tools or manual review, with code changes reviewed by an individual other than the originating developer, alongside the vulnerability management loop of its sub-requirements. The other options describe practices Requirement 6 does not require, and option D contradicts the requirement that review occur.

  20. B. SAST analyzes source patterns and cannot observe whether a runtime authorization check actually gates a path, so access control defects, which are design-level, escape it; coverage requires complementary methods such as DAST, IAST, manual review, and penetration testing. Replacing the tool (option A) misdiagnoses the limitation, and the other options misstate the responsibility.

Software security testing and secure coding on one page

Software security is a contracts discipline. Every vulnerability class is one component treating what it received as code or as trusted: injection (CWE-89 and the family) is data entering a grammar; XSS (CWE-79) is untrusted text rendered as markup; CSRF (CWE-352) is the ambient cookie standing in for intent; SSRF (CWE-918) is the server’s reach handed to the attacker. The vocabulary names the layers: CWE types, CVE instances, CVSS severity (FIRST, v3.1 current in practice, v4.0 since November 2023), NVD matching.

The OWASP Top 10 ranks prevalence, not risk, and exists in two editions: 2021 (injection A03, XSS inside it, SSRF A10, components A06) and 2025 (XSS folded into injection, SSRF and CSRF into broken access control, components broadened to supply chain, logging renamed, exceptional conditions new). The exam tests the classes, not the edition.

The defining controls: parameterized queries for SQL injection, context-aware output encoding and CSP for XSS, synchronizer tokens and SameSite for CSRF, destination allowlisting with resolved-address checks for SSRF, server-side per-object authorization for broken access control and IDOR, no untrusted deserialization (allowlist and integrity where unavoidable), parsers with DTDs and external entities disabled for XXE, generic user errors with server-side detail for CWE-209, and logs that capture security events without secrets.

The tool family: SAST on source at every commit (PW.7), DAST and IAST against running builds (PW.8), RASP in production as defense in depth, SCA on dependencies against NVD data (PW.4), fuzzing on parsers and input handlers (PW.8), manual review and penetration testing for design-level truth (NIST SP 800-115). Coverage, triage of false positives, and placement in the pipeline decide whether the tools work.

The coding discipline is the mirror image: validate input (NIST SP 800-53 SI-10), encode output per context, least privilege, fail secure with sanitized errors, log without secrets, secure session flags, deny-by-default access control, vetted cryptography, secure defaults (PW.9), and atomic check-and-act to beat TOCTOU.

The standards demand it: ISO/IEC 27001:2022 Annex A 8.28 and 8.29; PCI DSS v4.0 Requirement 6 (review before release, someone other than the author reviews the change, inventory and risk-ranked patching); NIST SP 800-218 SSDF v1.1 PW.4 through PW.9; NIST SP 800-53 SA-11 and SA-15; and OWASP ASVS as the auditable checklist. The tests prove the code; the coding standard sets what the tests check; the standards say both are mandatory.