Injection, cross-site scripting and insecure configuration
Why this matters
Objective 4.2 covers vulnerabilities and attack surface, and this lesson is the defect side of it. CAS-005 examines these at an architectural level: not how to exploit them, but how to recognise from a design where they will be, and which control class addresses them.
That framing is what makes the material tractable. Injection looks like several unrelated vulnerabilities — SQL, command, template, LDAP — and is one structural defect with several instances. Cross-site scripting looks like one vulnerability and is a family whose members need different fixes depending on where the data lands. And insecure configuration is not a coding problem at all, which is why it survives code review and secure development training entirely.
The reader who takes one thing from this lesson should take the architectural question: where does untrusted input reach an interpreter, and what stops it being read as instruction rather than as data? That question locates the whole injection class from a diagram, before any code exists.
The lesson
Injection as a class: SQL, command, template and LDAP sharing one root cause
Every injection vulnerability has the same shape. Data supplied by an untrusted source is combined with a command or query, and the interpreter cannot tell which part was the developer's instruction and which was the input. The input escapes its intended role and is executed as instruction.
The instances differ only in which interpreter is involved:
- SQL injection — the database engine. Input alters the structure of a query, changing what is selected, what conditions apply, or what other statements run.
- Command injection — the operating system shell. Input adds separators or substitutions so that additional commands execute with the application's privileges.
- Template injection — the template engine. Input is evaluated as template syntax, and in many engines that reaches the underlying runtime.
- LDAP injection — the directory server. Input alters filter structure so a query returns entries it should not.
- XML and XPath injection, including external entity processing, where a parser is induced to fetch local files or internal URLs.
- Deserialisation, where untrusted structured input is reconstructed into objects and the reconstruction itself executes code.
- Header and log injection, where control characters in input alter the structure of a downstream message or record.
Because they share a cause, they share a structural fix, and this is the architectural point: separate the instruction from the data so the interpreter is never asked to decide. Parameterised queries, argument arrays rather than shell strings, template engines given data rather than template text, and directory APIs that build filters rather than concatenating strings. Filtering input is a mitigation; separating instruction from data is a fix.
Two architectural signals that locate this class in a design without reading code: every point where untrusted input crosses a trust boundary toward an interpreter — the boundaries from lesson ten — and every place where a string is built and then executed, in whatever language. A design that shows user input reaching a component that constructs queries has an injection question to answer.
A note on scope worth making, since this course is not a development course: CAS-005 asks you to recognise the class, name the structural fix, and place the control. It does not ask you to write exploits, and this course does not teach that.
Cross-site scripting variants and the contexts that decide the encoding required
Cross-site scripting is injection where the interpreter is the victim's browser and the injected instruction is script running in the site's context — with access to the session, the page and anything the user can do.
The three forms differ in where the payload lives, which changes both impact and detection:
- Stored. The payload is saved server-side and served to every viewer. The most serious, because it needs no interaction from the victim beyond visiting a normal page.
- Reflected. The payload is in the request and echoed in the response, requiring the victim to follow a crafted link.
- DOM-based. The payload never reaches the server; client-side code reads part of the URL or storage and writes it into the page unsafely. Invisible to server-side logging and testing, which is why it is frequently missed.
The part that makes this more than "encode output" is context. The same value needs different treatment depending on where it lands:
| Where the value lands | What is required |
|---|---|
| HTML body text | HTML entity encoding |
| An HTML attribute | Attribute encoding, and the attribute must be quoted |
| Inside a script block | JavaScript string encoding — and this should be avoided rather than encoded |
| A URL or a parameter | URL encoding, plus scheme validation to exclude script schemes |
| A style context | Generally unsafe; avoid |
So a single encoding function applied everywhere is insufficient, and a scenario describing an application that "encodes all output" and is still vulnerable is describing a context mismatch.
The controls that work structurally, rather than by remembering to encode:
- A framework that encodes contextually by default, so safety is the default and unsafety is explicit. This is by far the largest single reduction.
- Content Security Policy, which constrains what the browser will execute and turns many successful injections into inert text. Defence in depth rather than a fix.
- Avoiding unsafe sinks in client-side code — assigning to properties that parse HTML rather than ones that set text.
- Cookie flags limiting what a successful script can reach.
Insecure configuration: defaults, verbose errors, and the debug endpoint left enabled
This category is not a code defect, which is why secure coding practices do not address it and why it is so persistent.
The recurring instances:
- Default credentials, on appliances, databases, management interfaces and embedded devices. Still one of the most reliable ways into an estate.
- Verbose errors, returning stack traces, query text, file paths and version information — which is reconnaissance delivered by the application itself.
- Debug and administrative endpoints enabled in production: profilers, health endpoints exposing configuration, management consoles, and documentation interfaces that permit live requests.
- Directory listing enabled, exposing files never intended to be served — backups, configuration, source archives.
- Unnecessary services and features enabled, which is attack surface from lesson ten in its most literal form.
- Permissive cross-origin configuration, which converts a same-origin protection into none.
- Missing security headers, which is a default rather than a decision in most stacks.
- Excessive permissions on files, storage and cloud resources.
Two properties make configuration defects distinctive and are worth carrying: they are introduced by deployment rather than by development, so they appear after code review and after testing, frequently during an urgent fix; and they are trivially discoverable by an unauthenticated attacker, which puts them high in any honest prioritisation even when their individual severity reads as moderate.
The controls are the ones from earlier domains, and this is where they pay off: hardened baselines as declarative infrastructure code from lesson twenty-seven, policy as code refusing a non-compliant deployment from lesson fourteen, continuous configuration assessment from lesson eighteen, and environment parity so that production does not differ from what was tested.
Outdated software and weak ciphers as configuration problems, not code problems
Two more items from CompTIA's bullet, placed here because they behave like configuration rather than like defects, and treating them as configuration is what makes them tractable.
Outdated software is a lifecycle failure. The defect was found and fixed by someone else; what remains is the operational question of whether the fix has been applied. The interesting cases at this level are the ones patching does not reach:
- Transitive dependencies — a library inside a library, which the application's own manifest does not name. This is what a software bill of materials exists to surface.
- Embedded and bundled components, where a vendor ships a runtime inside their product and patches it on their schedule, not yours.
- Container base images, which are frozen at build time and accumulate vulnerabilities without changing, per lesson thirty.
- End-of-life software, where no fix will ever exist and the answer is replacement, isolation or a documented exception with a compensating control.
Weak ciphers and protocols are a configuration standard problem: obsolete protocol versions enabled alongside current ones, weak suites retained for compatibility with a client nobody can identify, self-signed or expired certificates tolerated, and — the one that undoes the rest — validation disabled on the client side, per lesson thirty-six.
The reason both belong in a security operations objective rather than in engineering: they are found by scanning and fixed by configuration management, and the programme that finds them is lesson thirty's and the programme that fixes them is lesson twenty-seven's. A scenario describing an organisation that finds these repeatedly and keeps finding them is describing a gap between those two programmes, not a gap in either.
Reading an architecture for where untrusted input reaches an interpreter
The closing method, and it is what this objective is really examining.
Given a design, locate the vulnerability classes without reading any code, by asking four questions in order.
One: where does untrusted input enter? Everything from outside a trust boundary: user requests, uploaded files, API payloads, message queues, webhooks, imported data, third-party responses, and — the one most often forgotten — data from your own database that was originally user-supplied. Stored cross-site scripting exists because output was treated as trusted since it came from the database.
Two: which interpreters does it reach? Database engines, shells, template engines, parsers, deserialisers, browsers, directory services, and any component that evaluates its input.
Three: what separates instruction from data at each of those points? A parameterised interface, an argument array, a contextual encoder, a schema validator. If the answer is "the input is validated", that is a mitigation, not a separation, and the design has an injection question outstanding.
Four: what is the blast radius if it fails? Which identity does the interpreter run as, what can it reach, and what does the component hold. This is what decides priority and is why the over-privileged database account and the broadly permissioned execution role from earlier lessons appear here again — a successful injection into a least-privileged component is a much smaller incident.
That four-question pass is an architecture review of the kind lesson ten described, aimed at this specific class, and it finds design-level defects that a scanner cannot. It is also the form a CAS-005 scenario takes: a described system, a described input path, and a question about what is missing.
Practise what you just read
1. What do SQL, command, template and directory injection share?
Select one
Show answer
B. The input escapes its intended role and is executed as instruction. The instances differ only in which interpreter is involved, which is why they share a structural fix.
2. What is the structural fix for the injection class?
Select one
Show answer
C. Parameterised queries, argument arrays rather than shell strings, template engines given data rather than template text. Filtering is a mitigation; separation removes the class from that code path.
3. Which architectural signal locates the injection class without reading code?
Select one
Show answer
D. Every place where a string is built and then executed is the second signal. A design showing user input reaching a component that constructs queries has a question to answer.
12 more questions on this objective are part of the full course.
Hands-on labs
Part of the free CompTIA SecurityX CAS-005 course — 49 lessons and 77 hands-on labs.
This is an independent study companion for CompTIA SecurityX CAS-005 and is not produced by or endorsed by CompTIA.