You don’t need to be a security expert to protect your app; just stop repeating these same seven mistakes.

Introduction: Security Isn’t Optional Anymore
Every week, another data breach hits the news not because attackers discovered some zero-day exploit, but because a developer made a predictable mistake.
A forgotten validation.
An unescaped string.
An exposed key in a Git commit.
The pattern repeats because most developers see security as something separate from coding, when in reality, security is just good engineering with more attention to detail.
You don’t need to memorize the OWASP Top 10.
You just need to stop making the same seven mistakes that developers have been making for the last twenty years.
Here’s what they are and how to fix them for good.
1. Trusting User Input
Every vulnerability starts here.
Developers assume that “normal” users will enter “normal” data, but attackers never do.
The Mistake
Accepting input directly from forms, APIs, query parameters, or files without validating or sanitizing it.
// naive
app.post('/login', (req, res) => {
const { email, password } = req.body;
const query = `SELECT * FROM users WHERE email = '${email}' AND password = '${password}'`;
db.query(query).then(result => res.json(result));
});
That one string interpolation lets anyone inject SQL and bypass authentication.
The Fix
- Validate all input on the server using schemas (
Joi,Zod,Yup). - Use parameterized queries or ORM bindings; never concatenate strings into SQL.
- Sanitize rich text if your app allows HTML (
DOMPurify,sanitize-html). - Reject unexpected fields; “extra” data can hide malicious payloads.
2. Storing Secrets in Code or Repos
Hardcoding secrets is one of the most dangerous yet common developer habits.
The Mistake
const DB_PASSWORD = "supersecret123";
const STRIPE_KEY = "sk_live_...";
Once committed, it’s forever. Even if you delete the file, Git history preserves it.
The Fix
- Use environment variables for secrets.
- Use secret managers like AWS Secrets Manager, HashiCorp Vault, or Doppler.
- Rotate keys periodically every few months or after team changes.
- Add
.envto.gitignoreand never push it. - Scan your repos using tools like
truffleHogor GitHub’s built-in secret scanning.
3. Poor Authentication and Session Management
Authentication isn’t just about checking passwords; it’s about protecting sessions, tokens, and cookies too.
The Mistake
- Using plain session cookies without security flags.
- Not expiring tokens.
- Implementing “remember me” features without proper encryption.
- Storing passwords in plain text or with weak hashing.
The Fix
- Always hash passwords using a slow algorithm like
bcrypt,scrypt, orArgon2. - Set cookie flags properly:
HttpOnly: prevents JavaScript access.Secure: only send over HTTPS.SameSite: prevents cross-site attacks.
3. Expire tokens regularly and use refresh tokens with short lifespans.
4. Invalidate sessions when users log out or change passwords.
5. Use proven libraries (passport.js, next-auth, django-allauth) — don’t roll your own auth.
4. Ignoring Output Encoding
Even if your input is clean, your output might not be.
This is the root cause of Cross-Site Scripting (XSS) attacks, one of the oldest and still most effective web vulnerabilities.
The Mistake
Rendering user data directly into HTML:
res.send(`<p>${req.query.name}</p>`);
If name contains <script>malicious()</script>, it executes in the user’s browser.
The Fix
- Escape output before rendering. In React, Vue, and Angular, expressions are escaped by default.
- Avoid
innerHTMLunless strictly necessary. - If rendering HTML, sanitize it with an allowlist library like DOMPurify.
- Add Content Security Policy (CSP) headers to block inline or remote scripts.
5. Overlooking Error Handling and Logging
Attackers love detailed error messages.
They reveal stack traces, file paths, SQL queries, and system internals, everything needed to plan an exploit.
The Mistake
Returning detailed errors in production:
res.status(500).send(err.stack);
The Fix
- Hide stack traces in production, show generic messages to users.
- Log errors securely on the server (e.g., Winston, Bunyan, pino) but never expose them to clients.
- Redact sensitive fields (tokens, passwords) before logging.
- Separate logs by severity and keep them in restricted storage.
- Monitor logs for patterns repeated 401s, suspicious payloads, and large POST bodies.
6. Forgetting About HTTPS and Secure Headers
Unencrypted traffic is still common in internal tools and test deployments, but intercepting unencrypted traffic is trivial.
The Mistake
Deploying apps over HTTP or misconfiguring security headers.
The Fix
- Always use HTTPS, even for staging.
- Use free SSL with Let’s Encrypt or Cloudflare.
2. Set secure headers:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: no-referrer
3. Redirect all HTTP requests to HTTPS.
4. Don’t load mixed content; every external asset should use HTTPS too.
7. Not Updating Dependencies
Every modern project uses dozens of open-source packages, each a potential vulnerability if left outdated.
The Mistake
Locking dependencies forever because “upgrades might break things.”
Then a year later, one outdated library becomes your weakest link.
The Fix
- Regularly update dependencies monthly or per sprint.
- Use automated tools:
npm audit,yarn audit- GitHub Dependabot
- Snyk, Renovate
3. Check changelogs before major updates, but don’t avoid them indefinitely.
4. Pin versions explicitly to prevent unreviewed updates from breaking production.
Bonus: Security-by-Default Mindset
Security isn’t just a checklist. It’s a mindset.
Every time you write code, ask:
- What could happen if this input isn’t what I expect?
- Can an attacker control this value?
- What data am I trusting here, and should I?
Preventing vulnerabilities is about building habits, not memorizing exploits.
Here’s what that mindset looks like in practice:
- Default to deny: assume input is unsafe until proven valid.
- Defense in depth: validate, sanitize, and escape, even if you “think” the other layer handled it.
- Principle of least privilege: apps, users, and tokens should only have what they absolutely need.
- Automate audits: let tools like Semgrep or CodeQL catch dangerous patterns before review.
Conclusion: Small Mistakes, Big Consequences
Most security breaches don’t happen because developers are careless; they happen because they’re busy.
Deadlines push features ahead of discipline.
But ignoring security is like skipping tests: it saves time now, and costs far more later.
The good news?
Fixing these seven mistakes is easy once you commit to it.
- Validate input.
- Escape output.
- Store secrets safely.
- Hash passwords properly.
- Use HTTPS.
- Log responsibly.
- Keep your dependencies up to date.
That’s 80% of web security right there.
Good developers write code that works.
Great developers write code that’s safe to run in the real world.
Call to Action
Which of these mistakes have you seen most often or made yourself?
Drop your thoughts in the comments, and let’s make security a default habit, not an afterthought.
If you found this useful, bookmark it or share it with your team.
One developer learning these lessons early can save an entire company from a headline later.


Leave a Reply