Skip to content
back to the map
technique
stage: ship
2 min read

A08 Software or Data Integrity Failures

If the program accepts updates, plugins, serialized objects or pipeline output without proving where they came from, whoever can alter them can run code as you.

Sigstore cosignSLSA provenanceSubresource Integrity (SRI)ysoserial (to understand the risk, on your own lab)Semgrepin-toto

What it is

Integrity failures happen when code or data is trusted without verification: auto-updates that are not signed, scripts loaded from a CDN with no integrity check, deserialization of untrusted bytes, and CI/CD pipelines where one compromised step can alter what ships.

:2025 keeps this category as A08. It is close to A03 (supply chain) but focuses on the verification step: can you prove that what you run is what was meant to run?

How the attack works

  • : the application rebuilds objects from attacker-supplied bytes (Java serialization, Python pickle, unsafe YAML), and the stream triggers code execution.
  • Tampered update or plugin: a download over an unsigned channel is replaced in transit or on the mirror.
  • Unchecked third-party script: a compromised CDN serves modified JavaScript to every visitor.
  • Client-controlled state: a cookie or hidden field that carries a role, price or object is edited and accepted.

Vulnerable code

Deserializing untrusted bytes with pickle, trusting a user-editable cookie, and loading a script with no integrity attribute.

session.pypython
import pickle, base64

def load_prefs(cookie):
    return pickle.loads(base64.b64decode(cookie))   # attacker-controlled bytes run code
cart.jsjavascript
const cart = JSON.parse(Buffer.from(req.cookies.cart, "base64").toString());
total = cart.total;          // the client wrote this number
index.htmlhtml
<script src="https://cdn.example.com/lib/chart.min.js"></script>

The fix

Use data-only formats (JSON) with schema validation, never native object serialization for untrusted input. If state must round-trip through the client, sign it (HMAC or a signed token) and verify before use, or better, keep it on the server. Pin third-party scripts with , and verify signatures on anything you install or update.

session.pypython
import hmac, hashlib, json, base64

SECRET = load_secret("PREFS_HMAC_KEY")

def dump_prefs(prefs: dict) -> str:
    body = base64.urlsafe_b64encode(json.dumps(prefs).encode())
    mac = hmac.new(SECRET, body, hashlib.sha256).hexdigest().encode()
    return (body + b"." + mac).decode()

def load_prefs(cookie: str) -> dict:
    body, _, mac = cookie.encode().partition(b".")
    expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest().encode()
    if not hmac.compare_digest(mac, expected):
        raise ValueError("tampered")
    return json.loads(base64.urlsafe_b64decode(body))      # data only, validated after
cart.jsjavascript
// state lives on the server; the cookie only carries an opaque id
const cart = await carts.get(req.session.cartId);
const total = cart.items.reduce((s, i) => s + catalog.price(i.sku) * i.qty, 0);
index.htmlhtml
<script src="https://cdn.example.com/lib/chart-4.4.1.min.js"
        integrity="sha384-<hash of the exact file>"
        crossorigin="anonymous"></script>
verify before you runbash
# the artifact must be signed by the expected pipeline identity
cosign verify-blob --certificate-identity 'https://github.com/example/app/.github/workflows/release.yml@refs/heads/main' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --signature app.tgz.sig --certificate app.tgz.pem app.tgz

Prove the fix

Feed the code exactly what an attacker would: a modified cookie, a payload from a deserialization gadget, and a changed script file. The correct outcome is rejection, with no code executed.

test_integrity.py (pytest)python
def test_tampered_cookie_is_rejected():
    cookie = dump_prefs({"theme": "dark"})
    body, mac = cookie.split(".")
    forged = base64.urlsafe_b64encode(b'{"theme":"dark","role":"admin"}').decode() + "." + mac
    with pytest.raises(ValueError):
        load_prefs(forged)

def test_pickle_payload_is_not_executed(tmp_path):
    marker = tmp_path / "pwned"
    class Evil:
        def __reduce__(self):
            return (open, (str(marker), "w"))
    payload = base64.b64encode(pickle.dumps(Evil())).decode()
    with pytest.raises(Exception):
        load_prefs(payload)
    assert not marker.exists()
sri-check.shbash
# every external <script> and <link rel=stylesheet> must carry an integrity attribute
! grep -RInE '<script[^>]+src="https?://' dist/ | grep -v 'integrity='

Checklist

  • Never deserialize untrusted data with a format that can instantiate objects; prefer JSON plus schema validation.
  • Sign anything that must cross a (cookies, update files, messages) and verify before use.
  • Use Subresource Integrity for third-party scripts and styles, or self-host them.
  • Sign build artifacts and container images; verify the signature at deploy time.
  • Protect the pipeline: reviewed changes, separate build and deploy permissions, no unreviewed code in release steps.
  • Prefer server-side state for prices, roles and permissions.
OWASP Top 10The best-known reference list of web application security risks (A01 broken access control, A02 cryptographic failures, A03 injection...), revised every few years by the OWASP community. It is an educational map of risk categories, not a tool checklist or a certification.see in glossary →Insecure deserializationRebuilding objects from untrusted bytes with a format that can instantiate arbitrary types (Java serialization, pickle). The bytes can trigger code execution.see in glossary →Subresource IntegritySRIA hash in a script or link tag that makes the browser refuse the file if it does not match, so a compromised CDN cannot change your page.see in glossary →Trust boundarytrust boundaryA line where the trust level of the data or of whoever sends it changes (internet to VPC, service to database, user to admin). Everything that crosses it must be authenticated, authorized and validated.see in glossary →

connections on the map

updated on 14/10/2026 · sources to go deeper · report an error

in other domains

Study before

test what you learned

Loading questions…