Implera is currently offline. The blog stays up.
Back to insights

Insights

Security Patterns Every Developer Should Know

In March 2024 a maintainer called Jia Tan spent two years earning trust in the xz compression library, then shipped a backdoor that made it into Debian, Ubuntu and Fedora before a Microsoft engineer noticed SSH benchmarks running half a second slower than expected. Half the Linux servers on the internet were briefly exposed because nobody was watching trusted dependencies for suspicious behaviour.

Most security incidents are not that sophisticated. They are mundane. A committed secret. An unparameterised SQL query. An innerHTML assignment with user input in it. These are the patterns every team should be able to catch automatically, because the cost of missing them is high and the cost of detecting them is near zero.

Secrets in source control

The most common by volume. Someone commits an API key to experiment, intends to remove it, forgets.

A Starbucks engineer committed an AWS key to a public repo in 2019. A researcher found it within minutes and reported roughly 100 other keys in the same org by the end of the day. The discovery process is cheap. Attackers have bots doing it continuously.

Detection: scan diffs for the common secret formats (AWS keys, GCP service accounts, private keys, GitHub tokens, Stripe keys, JWT secrets). Most have fixed prefixes (AKIA, ghp_, sk_live_, -----BEGIN RSA PRIVATE KEY-----). Regex works.

Fix: once a secret is committed, git history makes it public forever. Rotate the secret immediately. Use tools like git-filter-repo to rewrite history if the repo is private. Treat the original value as burned regardless.

Unparameterised SQL

The 1998 bug that keeps shipping. OWASP has had injection at number one or near the top for 20 years.

// broken
db.query(`SELECT * FROM users WHERE id = ${userId}`);

// fixed
db.query("SELECT * FROM users WHERE id = ?", [userId]);

String concatenation into SQL is the bug. The fix is parameterised queries, prepared statements or an ORM that handles binding for you. Every mainstream database driver supports this. There is no reason in 2026 to concatenate.

Detection: grep for template strings and concatenation in anything that looks like a query. False-positive rate is moderate; human review confirms.

innerHTML with untrusted input

The XSS classic. element.innerHTML = userInput inserts arbitrary HTML, including <script> tags. On modern browsers inline scripts do not execute directly, but event-handler attributes still create attack surface.

// broken
container.innerHTML = user.bio;

// fixed
container.textContent = user.bio;

textContent renders as text. innerHTML renders as markup. If you actually need markup, use a sanitiser like DOMPurify. Never trust input and never hand-roll sanitisation.

CORS wildcard in production

Access-Control-Allow-Origin: * allows any origin to make cross-origin requests. Sometimes intentional for public APIs. Usually not, and usually combined with credentials.

// broken
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

// fixed
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true

Browsers block credentialed wildcard requests, but the intent in the first config is still wrong. Fix: list allowed origins explicitly.

Weak crypto

MD5 and SHA-1 collide. Cheap hardware can produce collisions at will. Neither should appear in any system where collision resistance matters.

DES and 3DES are deprecated. ECB mode is broken by design (identical plaintext blocks produce identical ciphertext, revealing structure). RSA keys below 2048 bits are below every credible guideline.

Detection: grep for md5, sha1, DES, ECB, any crypto primitive with a known weakness. Catch at CI. Replacement is usually trivial: SHA-256 for hashing, AES-GCM for symmetric, P-256 or RSA 2048+ for asymmetric.

JWT without verification

A startling number of codebases do this:

// broken
const payload = jwt.decode(token);
if (payload.userId) { /* ... */ }

// fixed
const payload = jwt.verify(token, secret);

decode parses the token without checking the signature. Anyone can forge a JWT, base64-encode it, and send it. The "fixed" line rejects any token that does not verify with the server's secret.

Insecure randomness for security-sensitive values

Math.random() is not cryptographically secure. It should never generate passwords, tokens, session IDs, nonces or anything an attacker should not be able to predict.

// broken
const token = Math.random().toString(36).slice(2);

// fixed
const token = crypto.randomBytes(32).toString("hex");

Detection: grep for Math.random in files that handle auth, tokens or session management. The false-positive rate is low because there is rarely a good reason to use non-crypto RNG near auth.

Open redirect

User-controlled URL used in a redirect without an allowlist.

// broken
res.redirect(req.query.return);

// fixed
const allowed = ["/dashboard", "/settings", "/profile"];
const target = allowed.includes(req.query.return) ? req.query.return : "/";
res.redirect(target);

Attackers use open redirects as the landing page in phishing campaigns. Your domain's trust transfers to the attacker's page. Every redirect target should be on a static allowlist or be a same-origin path.

What to do with all this

Most of these patterns are detectable with static analysis in under a second. There is no reason they should reach production. Put a scanner in CI, run it on every PR, and block merges on high-severity findings.

That is the floor. Above the floor, treat security findings as bugs, not suggestions. A committed secret or an unparameterised query is a defect of equal weight to a broken feature. The teams that get security right are not the ones with specialists; they are the ones with discipline about the basics.

The xz backdoor is a reminder that sophisticated attacks exist. But 95% of real incidents start with one of the patterns above. Fix those first.

FAQ

Common questions

© 2026 Implera