OWASP Top 10: The Most Critical Web Vulnerabilities and How to Prevent Them
Learn about the most dangerous vulnerabilities threatening web applications and how to protect your code against each of them.
Introduction to OWASP
OWASP (Open Web Application Security Project) is a non-profit organization dedicated to improving software security. Its most well-known project is the OWASP Top 10, a periodically updated list of the ten most critical web security vulnerabilities. This list is considered the de facto industry standard and is referenced by compliance frameworks such as PCI DSS, SOC 2, and ISO 27001.
As a developer, knowing the OWASP Top 10 is not optional: it's your responsibility to write secure code. Most security breaches are not caused by sophisticated attackers exploiting zero-day vulnerabilities, but by basic mistakes developers make out of ignorance. This article will help you identify and prevent each of these vulnerabilities in your code.
1. Broken Access Control
Broken access control is the number one vulnerability according to OWASP. It occurs when users can act outside their intended permissions: accessing other users' data, modifying resources they don't own, or escalating their privileges to administrator.
Example: A user changes the URL from /api/users/123/profile to /api/users/456/profile and accesses another user's profile because the server doesn't verify that the authenticated user is the owner of resource 123.
Prevention: Implement authorization checks on every endpoint, not just the frontend. Use the principle of least privilege: each user should only be able to access the strictly necessary resources. Implement failed access logs and alert on suspicious patterns. Never rely on the frontend to hide options; security must always be enforced on the server.
2. Cryptographic Failures
This category includes any failure related to cryptography: passwords stored in plain text, sensitive data transmitted without encryption, use of weak algorithms (MD5, SHA1 for passwords), keys hardcoded in source code, or misconfigured SSL/TLS certificates.
Prevention: Use bcrypt, scrypt, or Argon2 for hashing passwords (never MD5 or SHA). Encrypt all sensitive data in transit (mandatory HTTPS) and at rest. Use environment variables for keys and secrets, never include them in code. Keep your cryptographic dependencies up to date and use tested, audited libraries — never implement your own cryptography.
3. Injection
Injection vulnerabilities occur when untrusted data is sent to an interpreter as part of a command or query. The most common types are SQL Injection (injecting SQL code into database queries), Command Injection (executing operating system commands), LDAP Injection, and NoSQL Injection.
SQL Injection Example: A query like SELECT * FROM users WHERE email = '" + email + "' is vulnerable. If the attacker enters ' OR '1'='1 as the email, the query returns all users in the database.
Prevention: Use prepared statements (parameterized queries) or an ORM that uses them internally. Never build SQL queries by concatenating strings with user data. Validate and sanitize all user inputs. Use the principle of least privilege for database accounts: the application account should not have DROP TABLE permissions or access to other databases.
4. Insecure Design
Insecure design refers to architectural and design flaws that cannot be resolved with correct implementation. These are fundamental problems in how the system was conceived: lack of rate limiting, absence of resource ownership verification, business flows that can be abused, or poorly defined trust boundaries.
Prevention: Perform threat modeling during the design phase. Identify critical assets, potential attackers, and attack vectors. Implement security controls from the design stage, not as after-the-fact patches. Use established security patterns such as defense in depth, zero trust, and least privilege.
5. Security Misconfiguration
Misconfiguration is one of the most common and easiest vulnerabilities to prevent. It includes: servers with default configurations, admin accounts with default passwords, publicly listed directories, missing security headers, error messages that reveal sensitive information (stack traces, software versions), and unnecessary services enabled.
Prevention: Automate security configuration with scripts or tools like Ansible. Remove or disable functionalities, frameworks, and services you don't need. Implement HTTP security headers: Content-Security-Policy, X-Content-Type-Options, X-Frame-Options, Strict-Transport-Security. Use tools like Mozilla Observatory to audit your configuration.
6. Vulnerable and Outdated Components
Modern applications depend on hundreds of third-party libraries. If one of these libraries has a known vulnerability and you don't update it, your application is vulnerable. The most famous example is Equifax (2017), where a breach that exposed data of 147 million people was caused by an outdated version of Apache Struts with a vulnerability that had been patched months earlier.
Prevention: Use dependency analysis tools like npm audit, Snyk, Dependabot, or Renovate to detect and update vulnerable dependencies automatically. Configure security alerts on GitHub/GitLab. Remove dependencies you don't use. Review dependencies before adding them: is it maintained? Does it have an active team? Is it popular?
7. Identification and Authentication Failures
This category covers weaknesses in authentication mechanisms and session management: weak passwords without minimum requirements, absence of multi-factor authentication (MFA), sessions that don't expire, tokens not invalidated after logout, credential stuffing (attacks using leaked credential lists), and insecure token storage.
Prevention: Implement MFA for all accounts, especially administrators. Use rate limiting on login endpoints to prevent brute force. Implement reasonable password policies (minimum 12 characters, no arbitrary special character requirements). Use sessions with short expiration and refresh tokens. Store tokens in httpOnly cookies with Secure and SameSite=Strict flags, never in localStorage.
8. Software and Data Integrity Failures
Integrity failures occur when plugins, libraries, modules, or data from unverified sources are used without validating their integrity. The most notable supply chain attack was SolarWinds in 2020, where attackers compromised legitimate update software to distribute malware to 18,000 organizations.
Prevention: Use Subresource Integrity (SRI) for scripts loaded from CDNs. Verify npm/pip package signatures before installing them. Use lockfiles (package-lock.json, yarn.lock) to ensure exact dependency versions. Review dependency changes before updating. Consider using a private registry with integrated security analysis.
9. Security Logging and Monitoring Failures
Without proper logging and monitoring, security breaches can go undetected for months. The average time to detect a breach is over 200 days. If you don't log failed login attempts, unauthorized access, input validation errors, and sensitive actions, you won't be able to detect or investigate an ongoing attack.
Prevention: Log all security events: successful and failed logins, permission changes, sensitive data access, validation errors. Implement automatic alerts for suspicious patterns (multiple failed logins, access from unusual IPs). Use a centralized logging system like ELK Stack, Datadog, or Grafana Loki. Ensure logs don't contain sensitive data (passwords, tokens, PII).
10. Server-Side Request Forgery (SSRF)
SSRF occurs when a web application makes HTTP requests to user-provided URLs without proper validation. An attacker can exploit this to access internal services, cloud instance metadata (such as AWS EC2 metadata containing temporary credentials), or scan the internal network.
Example: An application that allows the user to enter a URL to import an image. The attacker enters http://169.254.169.254/latest/meta-data/iam/security-credentials/ and obtains the server's IAM credentials on AWS.
Prevention: Validate and sanitize all user-provided URLs. Use allowlists of permitted domains. 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 server-side requests. Use a proxy or dedicated service for external requests instead of making them directly from your application server.
Security Testing Tools
SAST (Static Application Security Testing): Tools like SonarQube, Semgrep, and CodeQL analyze your source code for vulnerable patterns without running the application. Integrate them into your CI pipeline to detect vulnerabilities before reaching production.
DAST (Dynamic Application Security Testing): Tools like OWASP ZAP, Burp Suite, and Nikto analyze your running application, sending malicious requests and verifying responses. They complement SAST and detect vulnerabilities that only manifest at runtime.
SCA (Software Composition Analysis): Tools like Snyk, Dependabot, and npm audit analyze your third-party dependencies for known vulnerabilities (CVEs). Configure them to run automatically and create PRs with security updates.
Secret scanning: Tools like GitGuardian, truffleHog, and GitHub's native secret scanning detect credentials, API keys, and tokens accidentally committed to the repository. Enable these tools from the first commit to prevent leaks.
Conclusion
Security is not a feature you add at the end of development; it's a mindset that must be present in every line of code you write. The OWASP Top 10 is your minimum security checklist: if you can prevent these ten categories of vulnerabilities, your application will be significantly more secure than average.
Invest time in continuously learning web security. Threats evolve constantly, and so do prevention techniques. Participate in security CTFs (Capture The Flag), read write-ups of real vulnerabilities, and practice in controlled environments like OWASP WebGoat or HackTheBox. Security is a skill that improves with constant practice.