Safeguard
Best Practices

Secure Coding Fundamentals: A No-Jargon Checklist for New Developers

Three habits — validating input, managing secrets, and pinning dependencies — sit behind most preventable breaches, from Log4Shell to the event-stream hack.

Safeguard Research Team
Research
7 min read

The OWASP Top 10 for 2025 — the current edition — puts Security Misconfiguration at #2, up from #5 just four years earlier, with 100% of tested applications showing some form of it. It also introduces a new category, Software Supply Chain Failures, at #3, with the highest average incidence rate on the list — numbers that have nothing to do with exotic zero-days and everything to do with skipped fundamentals. Two of the most damaging supply chain incidents of the last decade illustrate exactly what that new category is measuring. Log4Shell (CVE-2021-44228) was privately reported to Apache on November 24, 2021; Apache shipped a first patch, 2.15.0, on December 6, but the flaw only became widely known after public disclosure on December 9, and because that first fix proved incomplete, two more patches — 2.16.0 and 2.17.0 — followed by December 17. And in 2018, a single hijacked maintainer account let an attacker slip a malicious package called flatmap-stream into the popular event-stream library, which was downloaded roughly eight million times before anyone noticed it was quietly hunting for bitcoin wallet keys. Neither incident required a novel attack technique. Both came down to gaps in three unglamorous habits: validating input, protecting secrets, and managing dependencies deliberately. This checklist walks through what each of those actually means in practice, for developers who are new to security and tired of jargon.

Why do most breaches trace back to a handful of basic mistakes?

Because attackers reuse what works, and what works is rarely sophisticated. OWASP's 2025 Top 10 puts Security Misconfiguration (A02) at #2 and introduces Software Supply Chain Failures (A03) as a new category with the list's highest average incidence rate, reflecting exactly the kind of incidents described above. Injection, at #3 in the 2021 edition, has since fallen to #5, and Cryptographic Failures — covering hardcoded passwords and weak encryption (CWE-327, CWE-331) — fell from #2 to #4; both remain inside the top five. None of these require a nation-state attacker; a script that submits unescaped form input or a config file with a leftover admin password will find them. A small, consistently-applied checklist prevents more real damage than any single advanced control, because it closes the doors attackers actually try first.

What does "validate input" actually mean, in plain terms?

It means treating every piece of data your program didn't generate itself — form fields, URL parameters, uploaded files, API payloads, even HTTP headers — as untrusted until proven otherwise. The safest pattern is an allowlist: define exactly what a valid input looks like (an email matches a known shape, a quantity is a positive integer under some sane maximum) and reject anything that doesn't match, rather than trying to blocklist every bad pattern you can think of. For anything that touches a database, use parameterized queries or prepared statements instead of building SQL strings by concatenation — this is the single most effective defense against SQL injection, which is why it sits at the center of OWASP's Injection category. And validate on the server, always: client-side checks in JavaScript are a usability nicety, not a security control, because anyone can bypass them with a browser's developer tools or a raw HTTP request.

How do secrets end up in git history, and how do you stop it?

Almost always the same way: a developer pastes an API key or database password directly into a config file to get something working locally, commits it "just for now," and forgets it's there — except git never forgets, and a deleted line still lives in history unless you deliberately rewrite it. The fix starts before the commit happens: scan changes for secret patterns at the point of push, not after code review, using something like a pre-push git hook that blocks the commit locally with a clear reason before it ever reaches a shared repository. Treat any secret that does leak as compromised the moment it's found, not the moment someone proves it was misused — the standard response is revoke the credential at the issuer, rotate it to a new value, purge it from git history, and notify anyone who depends on it. Verifying a suspected leak against the real issuing service (for example, an AWS key checked with a harmless sts:GetCallerIdentity call) tells you in seconds whether you're dealing with a live credential or a dead example string, which matters enormously for triage speed.

Why isn't pinning a dependency version enough by itself?

Pinning stops a build from silently picking up a new, possibly-compromised version overnight, but it only protects you if you also revisit those pins when a real vulnerability is disclosed — and Log4Shell is the clearest illustration of why that second step can't be skipped. Alibaba Cloud researcher Chen Zhaojun privately reported the Log4j deserialization flaw to Apache on November 24, 2021; Apache shipped a first patch, 2.15.0, on December 6, before the flaw was widely known publicly on December 9. That fix was still exploitable in certain configurations, so 2.16.0 followed on December 13, and 2.17.0 followed on December 17 for a separate denial-of-service issue — three patches across eleven days. A team pinned to log4j-core 2.14.1 that never checked back was still exposed after the "fix" shipped, because they'd pinned to a version predating all three patches. The lesson isn't "don't pin" — unpinned ranges caused their own class of incidents — it's that pinning has to be paired with a routine for actually re-evaluating those pins when CVEs land.

What can the event-stream incident teach beginners about trusting packages?

That a dependency's popularity is not the same thing as its safety, and that trust can be transferred to an attacker without a single line of your own code changing. In 2018, the maintainer of the widely-used event-stream npm package handed publish rights to a contributor they didn't personally know, who then added a new dependency, flatmap-stream, containing code that specifically targeted the Copay bitcoin wallet application to steal private keys. Because event-stream was a transitive dependency pulled in by countless other projects, the malicious package rode along invisibly and was downloaded an estimated eight million times over roughly two and a half months before the npm security team and outside researchers caught it. For a beginner, the actionable habit is to periodically review your dependency tree — not just your direct package.json or requirements.txt entries, but what they pull in — and to be more cautious, not less, about packages that have recently changed maintainers or added new transitive dependencies without a corresponding release note explaining why.

How Safeguard Helps

These three habits are exactly where automated tooling earns its keep, because none of them scale well as manual discipline once a team grows past a handful of engineers. On secrets, Safeguard's pre-push git hook scans commits before they leave a developer's machine, and every issuer-specific match — an AWS key, a GitHub token — is verified against the real service with a harmless read-only call, so a leaked credential triggers a revoke-rotate-purge-notify playbook instead of a guessing game about whether it's actually live. On dependencies, Safeguard's package firewall sits on the install path itself for npm and pip, catching typosquats, namespace confusion, and known-malicious packages — including transitive ones — before they ever reach a developer's machine, which is precisely the kind of install-time check that would have flagged an unexpected new dependency riding along with a trusted package. Input validation stays a code-level discipline no tool can fully automate, but pairing it with automated secrets and supply-chain checks closes the two gaps that most often turn a single coding mistake into a full breach.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.