shared × unstructured — bad data written permanently, and it spreads
Stored / Second-Order Injection
Unvalidated input is persisted once, then trusted every time it is read back out.
01the recipe
In the wild
compound ofFile & Network AccessLack of Input ValidationCWE-20 Input ValidationcompoundCWE-89 SQL InjectionCWE-79 XSS (Cross-Site Scripting)
example.py
# SMELL: store unvalidated input, then trust it on the way back out.
# (file / network access x lack of input validation)
db.execute("INSERT INTO notes(body) VALUES (?)", [body]) # raw user HTML stored
...
page += row["body"] # later rendered to every viewer -> stored XSS
# or: db.execute("... WHERE name = '" + row["name"] + "'") # second-order SQLi
# RIGHT: validate on the way in, encode on the way out, parameterize always.
db.execute("INSERT INTO notes(body) VALUES (?)", [clean(body)])
page += escape_html(row["body"])The poison is written once and read forever: unvalidated input persisted to a shared store comes back as executable markup or SQL. The damage is durable and reaches every later reader, not just the attacker.
// observed
stored: one bad row scripts every viewer / re-injects on read sanitized: cleaned in, encoded out -- inert at rest
02weakness catalog
Mapped weaknesses (CWE)
On its own, this defect is catalogued by MITRE as one or more of these weaknesses. The exploitable vulnerability usually appears only when it chains or combines with another.