DEV Community

Ryu0705
Ryu0705

Posted on

Security Vulnerabilities Every Developer Should Know (OWASP Top 10 Simplified)

Most developers know they should "write secure code." But when asked what that means specifically, the answer gets vague fast.

The OWASP Top 10 is the industry standard list of critical web application security risks. But the official documentation is dense and aimed at security professionals. This guide translates each vulnerability into vulnerable code vs. fixed code examples that any developer can understand and act on.


A01: Broken Access Control

The problem: Users can act outside their intended permissions — viewing other users' data, modifying records they don't own, or escalating privileges.

This is the #1 vulnerability on the OWASP list, and it's surprisingly common.

// VULNERABLE: Any authenticated user can view any profile
app.get('/api/users/:id', auth, (req, res) => {
  const user = db.getUser(req.params.id);
  res.json(user);
});

// FIXED: Verify resource ownership
app.get('/api/users/:id', auth, (req, res) => {
  if (req.user.id !== req.params.id && !req.user.isAdmin) {
    return res.status(403).json({ error: 'Forbidden' });
  }
  const user = db.getUser(req.params.id);
  res.json(user);
});
Enter fullscreen mode Exit fullscreen mode

Checklist:

  • [ ] Every endpoint checks authorization (not just authentication)
  • [ ] Resource ownership is verified before access
  • [ ] CORS is configured restrictively (never * in production)
  • [ ] CSRF tokens protect state-changing operations
  • [ ] No path traversal in file operations (../)

A02: Cryptographic Failures

The problem: Sensitive data is exposed through weak cryptography, missing encryption, or accidental leakage.

# VULNERABLE: MD5 for password hashing
import hashlib
password_hash = hashlib.md5(password.encode()).hexdigest()

# FIXED: Use bcrypt with auto-generated salt
import bcrypt
password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
Enter fullscreen mode Exit fullscreen mode

Checklist:

  • [ ] Passwords hashed with bcrypt or argon2 (never MD5/SHA)
  • [ ] No hardcoded secrets or API keys in source code
  • [ ] Sensitive data not logged or in URL parameters
  • [ ] HTTPS enforced for all data transmission
  • [ ] PII encrypted at rest in the database

A03: Injection

The problem: Untrusted data is sent to an interpreter as part of a command or query. SQL injection is the classic example, but it extends to OS commands, LDAP, and more.

// VULNERABLE: SQL injection via string interpolation
const query = `SELECT * FROM users WHERE email = '${email}'`;
db.query(query);
// Attacker input: ' OR 1=1 --
// Resulting query: SELECT * FROM users WHERE email = '' OR 1=1 --'

// FIXED: Parameterized query
const query = 'SELECT * FROM users WHERE email = $1';
db.query(query, [email]);
Enter fullscreen mode Exit fullscreen mode
// VULNERABLE: Command injection
const { exec } = require('child_process');
exec(`convert ${userFilename} output.png`); // userFilename = "; rm -rf /"

// FIXED: Use array-based API that doesn't invoke shell
const { execFile } = require('child_process');
execFile('convert', [userFilename, 'output.png']);
Enter fullscreen mode Exit fullscreen mode

Checklist:

  • [ ] All SQL uses parameterized queries or ORM
  • [ ] No eval() or new Function() with user input
  • [ ] No shell execution with user-controlled strings
  • [ ] No innerHTML / dangerouslySetInnerHTML with user data
  • [ ] NoSQL queries don't pass user input to $where or $regex

A04: Insecure Design

The problem: Security flaws baked into the architecture itself, not just the implementation. No amount of good code fixes a bad design.

Example: A password reset flow that sends a reset link to the email address provided in the request (instead of the email on file).

Checklist:

  • [ ] Rate limiting on authentication endpoints
  • [ ] Account lockout after failed login attempts
  • [ ] Password reset uses the email on file, not user input
  • [ ] Business logic validated server-side (never trust the client)
  • [ ] Threat modeling done for sensitive features

A05: Security Misconfiguration

The problem: Insecure default settings, incomplete configurations, or unnecessary features left enabled.

// VULNERABLE: Express app in production
app.use(express.errorHandler()); // Exposes stack traces

// FIXED: Custom error handler for production
app.use((err, req, res, next) => {
  console.error(err); // Log internally
  res.status(500).json({ 
    error: 'Internal server error' // Don't leak details
  });
});
Enter fullscreen mode Exit fullscreen mode

Checklist:

  • [ ] Debug mode disabled in production
  • [ ] Default credentials changed everywhere
  • [ ] Security headers configured (CSP, HSTS, X-Frame-Options)
  • [ ] Error messages don't expose stack traces or internals
  • [ ] Directory listing disabled
  • [ ] Unnecessary features and endpoints removed

A06: Vulnerable and Outdated Components

The problem: Using libraries or frameworks with known security vulnerabilities.

# Check your project right now
npm audit
# or
pip install safety && safety check
Enter fullscreen mode Exit fullscreen mode

Checklist:

  • [ ] npm audit / pip audit runs in CI pipeline
  • [ ] Dependencies updated regularly
  • [ ] No abandoned packages (2+ years without updates)
  • [ ] Lock files committed to version control

A07: Identification and Authentication Failures

The problem: Weaknesses in authentication that allow attackers to compromise passwords, sessions, or tokens.

// VULNERABLE: Session not invalidated on logout
app.post('/logout', (req, res) => {
  res.clearCookie('session'); // Only clears client-side!
  res.redirect('/login');
});

// FIXED: Destroy server-side session
app.post('/logout', (req, res) => {
  req.session.destroy((err) => {
    res.clearCookie('session');
    res.redirect('/login');
  });
});
Enter fullscreen mode Exit fullscreen mode

Checklist:

  • [ ] Strong password requirements enforced
  • [ ] Sessions invalidated server-side on logout
  • [ ] Session IDs never exposed in URLs
  • [ ] MFA available for sensitive operations
  • [ ] Credential stuffing mitigated with rate limiting

A08: Software and Data Integrity Failures

The problem: Code or data that is used without verifying its integrity.

<!-- VULNERABLE: CDN resource without integrity check -->
<script src=\"https://cdn.example.com/lib.js\"></script>

<!-- FIXED: Subresource Integrity (SRI) -->
<script src=\"https://cdn.example.com/lib.js\" 
        integrity=\"sha384-abc123...\" 
        crossorigin=\"anonymous\"></script>
Enter fullscreen mode Exit fullscreen mode

Checklist:

  • [ ] CDN resources use Subresource Integrity (SRI)
  • [ ] No deserialization of untrusted data
  • [ ] CI/CD pipeline has integrity checks
  • [ ] Auto-updates verify signatures

A09: Security Logging and Monitoring Failures

The problem: Without proper logging, breaches go undetected — the average time to detect a breach is 287 days.

Checklist:

  • [ ] Login attempts (success and failure) are logged
  • [ ] Access control failures are logged
  • [ ] Sensitive data is NEVER in logs (passwords, tokens, PII)
  • [ ] Alerts configured for suspicious activity patterns
  • [ ] Logs are tamper-protected

A10: Server-Side Request Forgery (SSRF)

The problem: The application fetches a remote resource using a user-supplied URL without validation, allowing attackers to reach internal services.

// VULNERABLE: User controls the fetch URL
app.get('/preview', async (req, res) => {
  const response = await fetch(req.query.url);
  res.send(await response.text());
});
// Attacker: /preview?url=http://169.254.169.254/metadata
// (AWS metadata endpoint - leaks credentials)

// FIXED: Allowlist validation
const ALLOWED_HOSTS = ['api.example.com', 'cdn.example.com'];
app.get('/preview', async (req, res) => {
  const url = new URL(req.query.url);
  if (!ALLOWED_HOSTS.includes(url.hostname)) {
    return res.status(400).json({ error: 'Host not allowed' });
  }
  const response = await fetch(url.toString());
  res.send(await response.text());
});
Enter fullscreen mode Exit fullscreen mode

Checklist:

  • [ ] User-supplied URLs validated against an allowlist
  • [ ] Internal network addresses blocked (10.x, 172.16.x, 169.254.x)
  • [ ] DNS rebinding prevention in place
  • [ ] Outbound requests have firewall rules

The Complete Security Checklist

Here's the consolidated quick-reference:

Category Key Rule
Access Control Verify ownership, not just authentication
Cryptography bcrypt for passwords, no hardcoded secrets
Injection Parameterize everything, never concatenate
Design Rate limit, validate server-side
Configuration No debug in prod, set security headers
Dependencies Audit regularly, update promptly
Authentication Destroy sessions server-side, enforce MFA
Integrity SRI for CDN, verify signatures
Logging Log auth events, never log secrets
SSRF Allowlist outbound URLs

Making Security Automatic

Manual security review is important but insufficient. I built Security Audit Pro — a Claude Code skill that automatically scans your codebase against OWASP Top 10 rules, CWE Top 25, dependency vulnerabilities, and secrets detection. It produces a prioritized report with specific line-level findings and fix suggestions.

Whether you use automated tools or this manual checklist, building security into your regular review process is what matters. Don't wait for a breach to start caring about security.


Which of these vulnerabilities have you encountered in your own codebase? I'd bet at least 3 of these 10 apply to most production applications right now.

Top comments (0)