DEV Community

OWASP Top 10 for Developers (2026 Edition) — How to Actually Fix the Most Dangerous Web Vulnerabilities

Hi, Mahdi Shamlou here. In my last article (What Is a Sandbox? How to Safely Run and Analyze Any Unknown .exe), you learned how to safely detonate an unknown .exe in a sandbox. That’s reactive security — analyzing malware after it exists.

But what if you could prevent most attacks before they ever reach your server? That’s where OWASP comes in.

Mahdi Shamlou

In this guide, I’ll walk you through the OWASP Top 10 for 2026 — but not as a boring list. I’ll show you:

  • What each vulnerability actually means for your code.
  • A real vulnerable code snippet (and how to fix it).
  • Which tools you can use to find these bugs automatically.

By the end, you’ll have a security checklist you can apply to your next web app.


What Is OWASP? (And Why You, as a Developer, Should Care)

OWASP = Open Web Application Security Project. It’s a non‑profit community that produces free, world‑class security guidance.

The most famous is the OWASP Top 10 — a ranked list of the most critical web security risks. It’s updated every few years to reflect real‑world attack data. The 2026 edition (which builds on the 2021 version) remains the must‑know list for any web developer.

Why should you care? Because:

  • 75% of web apps have at least one Top 10 vulnerability.
  • Fixing a bug in production costs 30x more than catching it in coding.
  • Security is now a shared responsibility — developers can’t just throw code over the wall to a “security team”.

So let’s dive in. I’ll present each risk, show you a bad code example, and then give you the secure version.


A01: Broken Access Control

What it is: Users can see or edit data they’re not supposed to. For example, a normal user changing their ?user_id=123 to ?user_id=124 and seeing someone else’s profile.

Vulnerable code (Flask):

@app.route('/profile')
def profile():
    user_id = request.args.get('user_id')
    # No check if logged-in user owns this ID
    user = db.query(f"SELECT * FROM users WHERE id = {user_id}")
    return render_template('profile.html', user=user)
Enter fullscreen mode Exit fullscreen mode

Fix — enforce server‑side access control:

@app.route('/profile')
@login_required
def profile():
    # Only allow access to the currently logged-in user's own profile
    user_id = current_user.id   # from session, not from request
    user = db.query("SELECT * FROM users WHERE id = %s", (user_id,))
    return render_template('profile.html', user=user)
Enter fullscreen mode Exit fullscreen mode

Tool to catch this: OWASP ZAP or Burp Suite — try changing ID parameters manually.


A02: Cryptographic Failures

What it is: Sensitive data (passwords, credit cards) transmitted or stored without proper encryption. That includes using old algorithms like MD5 or SHA‑1.

Vulnerable code:

# Storing password in plain text? Ouch.
user.password = request.form['password']

# Or using MD5 (crackable in seconds)
import hashlib
hashlib.md5(password.encode()).hexdigest()

Enter fullscreen mode Exit fullscreen mode

Fix — use strong, adaptive hashing (bcrypt, Argon2):

from bcrypt import hashpw, gensalt

hashed = hashpw(request.form['password'].encode(), gensalt())
# Store 'hashed' in DB.
# Also enforce HTTPS everywhere – use HSTS header.
Enter fullscreen mode Exit fullscreen mode

Tool: sslscan for HTTPS config, hashcat to test weak password hashes.


A03: Injection (SQL, NoSQL, OS Command, LDAP…)

What it is: Attacker sends malicious input that gets interpreted as a command. Classic example: ' OR '1'='1 logging you in without a password.

Vulnerable code (string concatenation):

username = request.form['username']
password = request.form['password']
query = f"SELECT * FROM users WHERE name = '{username}' AND pass = '{password}'"
# Input: username = "admin' --" -> bypasses password check!

Enter fullscreen mode Exit fullscreen mode

Fix — use parameterized queries / ORM:

cursor.execute(
    "SELECT * FROM users WHERE name = %s AND pass = %s",
    (username, password)
)

# Or use an ORM like SQLAlchemy which escapes automatically.
Enter fullscreen mode Exit fullscreen mode

Tool: sqlmap (automated detection) or Burp Scanner.


A04: Insecure Design

What it is: Flaws in the architecture or workflow — not a single line of code, but a design that makes security impossible. Example: a password reset that sends the new password by email (in plain text).

Vulnerable design:
“Forgot password” emails your current password in plain text. That means the system stored it reversibly — huge risk.

Fix — design with security in mind:

  • Never store passwords reversibly.
  • Password reset should send a time‑limited token (not a new password).
  • Use threat modeling (e.g., STRIDE) before writing code.

Tool: OWASP Threat Dragon for threat modeling diagrams.


A05: Security Misconfiguration

What it is: Default passwords, verbose error messages, unnecessary features enabled, missing security headers.

Vulnerable example (Flask debug mode in production):

app.run(debug=True)   # Shows full stack traces to attackers – exposes code!
Enter fullscreen mode Exit fullscreen mode

Fix — secure configuration:

# Use environment variables
import os
debug_mode = os.getenv('FLASK_DEBUG', 'False').lower() == 'true'
app.run(debug=debug_mode)
Enter fullscreen mode Exit fullscreen mode

And add security headers:

@app.after_request
def add_security_headers(resp):
    resp.headers['X-Content-Type-Options'] = 'nosniff'
    resp.headers['X-Frame-Options'] = 'DENY'
    resp.headers['Strict-Transport-Security'] = 'max-age=31536000'
    return resp
Enter fullscreen mode Exit fullscreen mode

Tool: Nmap scripts for misconfig, OWASP ZAP passive scanner.


A06: Vulnerable and Outdated Components

What it is: You use a library like lodash or Flask with a known CVE (Common Vulnerability). Attackers scan for these.
Download the Medium app

Vulnerable scenario:
Your requirements.txt has Flask==1.0.2 (released 2018 – many security fixes since). An attacker finds a public exploit for older Flask.

Fix — keep dependencies updated:

  • Run pip list --outdated regularly.
  • Use Dependabot (GitHub) or Snyk to auto‑open PRs.
  • Before installing a new package, check npm audit or safety check.

Tool: OWASP Dependency-Check (free, scans for known vulnerabilities).


A07: Identification and Authentication Failures

What it is: Weak session management, no MFA, credential stuffing vulnerabilities, or session IDs in URLs.

Vulnerable code (session in URL):

# http://example.com/profile?session_id=abc123
# Attacker steals this link – now they're logged in as you.
Enter fullscreen mode Exit fullscreen mode

Fix — use framework session management (secure flags):

app.config.update(
    SESSION_COOKIE_SECURE=True,   # only over HTTPS
    SESSION_COOKIE_HTTPONLY=True, # no JavaScript access
    SESSION_COOKIE_SAMESITE='Lax'
)
# Never put session in URL.
Enter fullscreen mode Exit fullscreen mode

Also enforce rate limiting on login attempts and offer MFA.

Tool: hydra or ffuf for brute‑force testing.


A08: Software and Data Integrity Failures

What it is: You trust software or data from an untrusted source without verifying integrity. For example, downloading a dependency over HTTP (not HTTPS) or deserializing untrusted data.

Vulnerable example (pickle deserialization in Python):

import pickle
data = request.form['user_data']
obj = pickle.loads(data)  # Can execute arbitrary code!
Enter fullscreen mode Exit fullscreen mode

Fix — avoid pickle on untrusted input, use JSON with schema validation:


import json
try:
    obj = json.loads(data)
    if not isinstance(obj, dict) or 'name' not in obj:
        raise ValueError
except (json.JSONDecodeError, ValueError):
    abort(400)

Enter fullscreen mode Exit fullscreen mode

And pin dependency hashes (using pipenv or poetry) to prevent supply chain attacks.

Tool: in-toto or Sigstore for integrity verification.


A09: Security Logging and Monitoring Failures

What it is: You don’t log attacks, or you log sensitive data, or you never monitor logs. So you only discover a breach months later (if ever).

Vulnerable — no logging:

@app.route('/login')
def login():
    # user logs in – but no record of failed attempts
    ...
Enter fullscreen mode Exit fullscreen mode

Fix — log security‑relevant events:

import logging
logging.basicConfig(level=logging.INFO)

@app.route('/login')
def login():
    if login_failed:
        logging.warning(f"Failed login attempt for {username} from {request.remote_addr}")
    else:
        logging.info(f"Successful login for {username}")
Enter fullscreen mode Exit fullscreen mode

Don’t log passwords or session tokens. Send logs to a central system (ELK, Splunk) and set up alerts for multiple failures.

Tool: OWASP AppSensor — defines detection points.


A10: Server‑Side Request Forgery (SSRF)

What it is: Attacker makes your server send requests to internal systems (like http://localhost:8080/admin or AWS metadata endpoint 169.254.169.254).

Vulnerable code (fetching a user‑supplied URL):

import requests
url = request.args.get('webhook')
response = requests.get(url)   # Can hit internal services!
Enter fullscreen mode Exit fullscreen mode

Fix — allowlist allowed domains / IPs:

from urllib.parse import urlparse

allowed_hosts = ['api.example.com', 'trusted-service.com']
parsed = urlparse(url)
if parsed.hostname not in allowed_hosts:
    abort(400)
response = requests.get(url, timeout=5)

Enter fullscreen mode Exit fullscreen mode

Better: use a separate internal firewall and disable HTTP redirects.

Tool: SSRFmap (automated exploitation tool).


Mahdi Shamlou

How to Test Yourself (Hands‑On)

Reading about vulnerabilities isn’t enough — you need to practice.

I recommend these free, safe environments:

  • PortSwigger Web Security Academy — free labs for every OWASP Top 10 category.
  • OWASP Juice Shop — an intentionally vulnerable web app you can run locally (in Docker) and hack.
  • TryHackMe or HackTheBox — beginner rooms for web security.

And remember the sandbox principle from my last article: always run unknown code or vulnerable apps inside isolated VMs (VirtualBox, VMware) or Docker containers. That way, even if you intentionally exploit a vulnerability, your main machine stays safe.


Want More?

If you enjoyed this practical security guide, you might also like:

Mahdi Shamlou

🔗 LinkedIn:
https://www.linkedin.com/in/mahdi-shamlou-3b52b8278
📱 Telegram:
https://telegram.me/mahdi0shamlou
📸 Instagram:
https://www.instagram.com/mahdi0shamlou/

Author: Mahdi Shamlou | مهدی شاملو

Top comments (1)

Collapse
 
jing_mark1998 profile image
Mark Jing

Thanks Mahdi Shamlou
This is one of the best OWASP Top 10 articles I've come across . not just because it explains the risks clearly, but because the code examples make everything click