Research2026-03-2114 min read

Top 10 Web Vulnerabilities in 2026

After analyzing 500+ automated pentests across SaaS applications, here are the 10 most common vulnerability categories we discover, with real-world examples and remediation guidance.

Research Report

Top 10 Web Vulnerabilities in 2026

The most common security issues we find across hundreds of pentests. Based on real data from 500+ automated assessments.

Recon0x Research·March 2026·12 min read
500+
Pentests analyzed
3,200+
Vulnerabilities found
72%
Apps had at least 1 High/Critical

Overview

After analyzing 500+ automated pentests across SaaS applications, e-commerce platforms, and enterprise tools, here are the 10 most common vulnerability categories we discover. These findings span organizations of all sizes, from early-stage startups to Fortune 500 companies. The patterns are remarkably consistent: most teams ship fast and patch later, leaving well-known attack surfaces exposed for weeks or months.

Each vulnerability category below includes its prevalence rate (percentage of tested applications where we found at least one instance), real-world examples from our engagements (anonymized), and actionable remediation guidance. We also map each category to the relevant CWE identifiers for compliance and tracking purposes.

Top 10 vulnerability frequency (% of apps affected)

Broken Access Control68%
Missing Rate Limiting60%
Security Misconfiguration55%
Sensitive Data Exposure45%
Injection42%
Authentication Flaws38%
Cross-Site Scripting35%
SSRF22%
Subdomain Takeover18%
Insecure Deserialization12%

#1. Broken Access Control (68% of apps)

Broken access control remains the single most common vulnerability we encounter. It appears in more than two-thirds of all applications we test. The root cause is straightforward: developers check whether a user is authenticated but forget to check whether that user is authorized to perform the specific action they requested.

This category includes Insecure Direct Object References (IDOR), missing function-level authorization checks, privilege escalation from regular user to admin, and horizontal access violations where one tenant can read another tenant's data. In multi-tenant SaaS products, the impact is especially severe because a single IDOR can expose the data of every customer on the platform.

Real example (anonymized): A SaaS billing platform allowed any authenticated user to access other users' invoices by changing the id parameter in the URL. The endpoint verified the session token but never checked whether the invoice belonged to the requesting user. We extracted 14,000 invoices containing full names, addresses, and payment details in under 3 minutes.

// Vulnerable: no ownership check
app.get("/api/invoices/:id", requireAuth, async (req, res) => {
  const invoice = await db.invoice.findUnique({
    where: { id: req.params.id },
  });
  return res.json(invoice);
});

// Fixed: verify the invoice belongs to the user
app.get("/api/invoices/:id", requireAuth, async (req, res) => {
  const invoice = await db.invoice.findUnique({
    where: {
      id: req.params.id,
      userId: req.user.id, // ownership check
    },
  });
  if (!invoice) return res.status(404).json({ error: "Not found" });
  return res.json(invoice);
});

CWE-284 · Improper Access Control CWE-639 · Authorization Bypass Through User-Controlled Key

Broken access control sub-types

IDOR (horizontal)45%
Missing function-level checks32%
Privilege escalation (vertical)22%
Multi-tenant isolation bypass18%
Path traversal11%

#2. Missing Rate Limiting (60% of apps)

Missing rate limiting is the second most prevalent issue and often the easiest to exploit. When APIs accept unlimited requests, attackers can brute-force passwords, stuff credentials from leaked databases, enumerate user accounts, scrape entire datasets, and abuse expensive operations to inflate costs.

We consistently find login endpoints, password reset flows, OTP verification, and API search endpoints with zero rate limiting. The consequence is simple: any attacker with a script and a proxy list can test thousands of credentials per minute. For applications that charge per API call or per AI inference, the absence of rate limiting also creates a direct financial risk.

Real example (anonymized): A fintech app had no rate limit on its four-digit OTP verification endpoint. We wrote a script that cycled through all 10,000 combinations in 47 seconds, achieving full account takeover for any phone number.

// Example: rate limiting with a sliding window
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(5, "60 s"),
  prefix: "login",
});

app.post("/api/login", async (req, res) => {
  const ip = req.headers["x-forwarded-for"] || req.ip;
  const { success } = await ratelimit.limit(ip);
  if (!success) {
    return res.status(429).json({ error: "Too many attempts" });
  }
  // proceed with authentication
});

CWE-307 · Improper Restriction of Excessive Authentication Attempts CWE-770 · Allocation of Resources Without Limits

#3. Security Misconfiguration (55% of apps)

Security misconfiguration covers a broad spectrum of issues that arise from default settings, incomplete hardening, and forgotten debug features. We see this in more than half of all applications, making it one of the most persistent vulnerability categories year after year. The problem is rarely a lack of security features; it is a failure to enable and configure them properly.

Common findings include default admin credentials on third-party services, verbose error messages that leak stack traces and database queries, unnecessary HTTP methods (PUT, DELETE, TRACE) enabled on production servers, and cloud storage buckets with public read access. In 2026, cloud misconfigurations are especially common because teams provision infrastructure through code but rarely audit the resulting permissions.

Cloud misconfigurations we find most often: S3 buckets with public ACLs, Firebase Realtime Database rules set to {".read": true}, GCP service account keys committed to repositories, and Azure Blob containers with anonymous access enabled. Each of these takes less than 60 seconds to discover using automated tooling.

Real example (anonymized): An e-commerce platform left its staging environment accessible at staging.example.com with default credentials (admin:admin). The staging database was a recent copy of production, containing 230,000 customer records with hashed passwords and order history.

// Checklist: common misconfigurations to audit
// 1. Remove default credentials on ALL services
// 2. Disable verbose errors in production
//    app.use((err, req, res, next) => {
//      res.status(500).json({ error: "Internal server error" });
//    });
// 3. Restrict HTTP methods
// 4. Set security headers (CSP, HSTS, X-Frame-Options)
// 5. Audit cloud storage permissions quarterly
// 6. Remove /debug, /graphql-playground, /swagger in prod

CWE-16 · Configuration CWE-1188 · Insecure Default Initialization of Resource

#4. Sensitive Data Exposure (45% of apps)

Nearly half of the applications we test expose sensitive data in places where it should not exist. This is not limited to unencrypted databases or missing TLS. The more common pattern in 2026 is accidental leakage: API keys embedded in client-side JavaScript bundles, personally identifiable information (PII) returned in API responses that are broader than necessary, secrets visible in error messages, and credentials preserved in Git history long after they were rotated.

The most dangerous variant is API key exposure in frontend code. We routinely find Stripe secret keys, database connection strings, internal API tokens, and third-party service credentials bundled into production JavaScript files. Attackers scan public JavaScript bundles at scale using automated tools, and exposed keys are exploited within minutes of deployment.

Real example (anonymized): A SaaS analytics platform shipped its Stripe sk_live_ key inside a Next.js client component. The key had full read/write access to customer subscriptions, payment methods, and billing history. We discovered it by searching the compiled JavaScript bundle for common key prefixes.

Where we find exposed data

Client-side JS35%
API responses25%
Error messages20%
Git history12%
Logs8%

CWE-200 · Exposure of Sensitive Information CWE-312 · Cleartext Storage of Sensitive Information CWE-532 · Information Exposure Through Log Files

#5. Injection (42% of apps)

Injection vulnerabilities remain stubbornly present despite decades of awareness. In 2026, classic SQL injection through raw query strings is less common thanks to ORMs and parameterized queries. However, new injection vectors have taken its place. NoSQL injection against MongoDB queries, ORM-bypass attacks that exploit raw query methods in Prisma, Sequelize, and TypeORM, command injection through unsanitized shell arguments, and template injection in server-side rendering engines are all actively exploited.

The rise of ORM-bypass attacks is particularly notable. Developers trust their ORM to handle sanitization, but most ORMs provide escape hatches for complex queries. When user input reaches these raw query methods, the same injection risks apply. We find this pattern most often in search, filtering, and reporting features where developers need queries more complex than the ORM's query builder supports.

Real example (anonymized): An enterprise HR platform used Prisma's $queryRaw to build a dynamic employee search. The search term was interpolated directly into the SQL string. We extracted the entire employee database, including salary data and social security numbers, through a UNION-based injection.

// Vulnerable: string interpolation in raw query
const results = await prisma.$queryRaw(
  `SELECT * FROM employees WHERE name LIKE '%${search}%'`
);

// Fixed: use parameterized query
const results = await prisma.$queryRaw(
  Prisma.sql`SELECT * FROM employees WHERE name LIKE ${'%' + search + '%'}`
);

// Better: use the ORM query builder
const results = await prisma.employee.findMany({
  where: { name: { contains: search, mode: "insensitive" } },
});

CWE-89 · SQL Injection CWE-78 · OS Command Injection CWE-943 · NoSQL Injection

Injection sub-types (% of injection findings)

SQL injection (ORM bypass)38%
NoSQL injection24%
Command injection18%
Template injection (SSTI)12%
LDAP / XPath injection8%

#6. Authentication Flaws (38% of apps)

Authentication flaws cover everything from weak password policies to broken session management. While many modern applications delegate authentication to providers like Clerk, Auth0, or Firebase Auth, custom implementations remain common. And even when using a provider, integration mistakes introduce vulnerabilities.

The most frequent issues we find: missing multi-factor authentication on admin and sensitive accounts, session tokens that survive password changes, JWT tokens signed with weak or default secrets, password reset flows that leak user existence through timing differences, and session fixation attacks where an attacker can set the session ID before the victim logs in.

JWT misuse deserves special attention. We regularly find applications using thenone algorithm, signing tokens with easily guessable secrets likesecret or the application name, storing sensitive data in the JWT payload without encryption, and failing to validate token expiration. Each of these allows complete authentication bypass.

Real example (anonymized): A project management SaaS signed its JWTs with the string mysecretkey123. We cracked it in under 10 seconds using a common wordlist, forged an admin token, and gained full access to every workspace on the platform.

// JWT security checklist
// 1. Use RS256 or ES256 (asymmetric) instead of HS256
// 2. Secret must be at least 256 bits of entropy
// 3. Always validate: exp, iss, aud claims
// 4. Invalidate tokens on password change
// 5. Never store sensitive data in JWT payload
// 6. Implement token refresh with short-lived access tokens

import jwt from "jsonwebtoken";

// Verify with strict options
const payload = jwt.verify(token, publicKey, {
  algorithms: ["RS256"],      // reject 'none' and HS256
  issuer: "https://myapp.com",
  audience: "myapp-api",
  clockTolerance: 30,         // 30s tolerance
});

CWE-287 · Improper Authentication CWE-384 · Session Fixation CWE-347 · Improper Verification of Cryptographic Signature

#7. Cross-Site Scripting (35% of apps)

Modern frontend frameworks like React, Vue, and Angular escape output by default, which has significantly reduced the prevalence of XSS compared to a decade ago. Yet we still find it in over a third of applications. The remaining XSS vulnerabilities cluster around specific patterns: dangerouslySetInnerHTML in React, v-html in Vue, server-rendered content that bypasses framework escaping, and DOM-based XSS through URL parameters processed in client-side JavaScript.

Stored XSS remains the most impactful variant. When an attacker can inject a script that persists in the database and executes for every user who views the affected page, the blast radius is enormous. We find stored XSS most often in rich text editors, user profile fields, comment systems, and support ticket interfaces.

Real example (anonymized): A customer support platform rendered ticket descriptions using dangerouslySetInnerHTML to support basic formatting. An attacker submitted a ticket containing an <img onerror>payload. Every support agent who opened the ticket had their session cookie exfiltrated to an attacker-controlled server. Because the support portal had admin-level access to customer accounts, this led to a full platform compromise.

// Vulnerable: raw HTML rendering
function TicketBody({ html }: { html: string }) {
  return <div dangerouslySetInnerHTML={{ __html: html }} />;
}

// Fixed: sanitize before rendering
import DOMPurify from "dompurify";

function TicketBody({ html }: { html: string }) {
  const clean = DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ["b", "i", "em", "strong", "a", "p", "br", "ul", "li"],
    ALLOWED_ATTR: ["href"],
  });
  return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}

CWE-79 · Cross-site Scripting (XSS)

XSS variant distribution

Stored XSS42%
Reflected XSS31%
DOM-based XSS27%

#8. SSRF (22% of apps)

Server-Side Request Forgery (SSRF) occurs when an application fetches a URL provided by the user without properly validating the destination. The attacker tricks the server into making requests to internal services, cloud metadata endpoints, or other hosts that are not meant to be accessible from the outside. In cloud environments, SSRF is particularly dangerous because it can expose instance metadata, including IAM credentials.

The classic SSRF target in AWS is the Instance Metadata Service (IMDS) at169.254.169.254. IMDSv1 allows simple GET requests, meaning any SSRF vulnerability gives immediate access to temporary IAM credentials. IMDSv2 mitigates this by requiring a PUT request with a TTL header to obtain a session token first. However, many organizations still run IMDSv1 or have not enforced IMDSv2 across all instances. GCP and Azure have similar metadata endpoints with their own protections.

We find SSRF most often in webhook handlers, URL preview generators (link unfurling), file import features that accept URLs, PDF generation services, and image proxy endpoints. Any feature that takes a URL from the user and fetches it server-side is a potential SSRF vector.

Real example (anonymized): A collaboration tool had a link preview feature that fetched Open Graph metadata from user-supplied URLs. By submittinghttp://169.254.169.254/latest/meta-data/iam/security-credentials/, we retrieved the IAM role name, then fetched temporary AWS credentials with full S3 and DynamoDB access.

// SSRF prevention: validate URL before fetching
import { URL } from "url";
import dns from "dns/promises";

async function safeFetch(input: string) {
  const url = new URL(input);

  // Block private/internal IPs
  const blocked = ["127.0.0.1", "0.0.0.0", "169.254.169.254", "::1"];
  const { address } = await dns.lookup(url.hostname);
  if (
    blocked.includes(address) ||
    address.startsWith("10.") ||
    address.startsWith("172.16.") ||
    address.startsWith("192.168.")
  ) {
    throw new Error("Internal addresses are not allowed");
  }

  // Only allow HTTPS
  if (url.protocol !== "https:") {
    throw new Error("Only HTTPS is allowed");
  }

  return fetch(url.toString(), { redirect: "error" });
}

CWE-918 · Server-Side Request Forgery (SSRF)

#9. Subdomain Takeover (18% of apps)

Subdomain takeover happens when a DNS record (usually a CNAME) points to an external service that has been deprovisioned. The dangling DNS record remains, and an attacker can register the same resource on the external service, effectively taking control of the subdomain. This allows them to host arbitrary content under the victim's domain, steal cookies scoped to the parent domain, and launch highly convincing phishing attacks.

The services most commonly involved in subdomain takeovers are: GitHub Pages, Heroku, AWS S3 (when configured as a website), Shopify, Zendesk, Fastly, Surge.sh, and various other SaaS platforms that allow custom domain binding. When a team cancels a Heroku app or deletes a GitHub Pages repository but forgets to remove the CNAME record, the subdomain becomes claimable.

Real example (anonymized): A company had blog.example.compointing via CNAME to a Heroku app that was deleted 8 months earlier. We registered a new Heroku app, added the custom domain, and served a proof-of-concept page. Because the company's session cookies were scoped to .example.com, the taken-over subdomain could read and exfiltrate active session tokens from the main application.

# Detection: find dangling CNAMEs
# Look for NXDOMAIN responses on CNAME targets

dig blog.example.com CNAME +short
# returns: example-old.herokuapp.com

dig example-old.herokuapp.com A +short
# returns: NXDOMAIN  <-- vulnerable!

# Prevention checklist:
# 1. Audit DNS records monthly
# 2. Remove CNAMEs before deprovisioning services
# 3. Scope cookies to specific subdomains, not .example.com
# 4. Use CAA records to restrict certificate issuance
# 5. Monitor Certificate Transparency logs

CWE-284 · Improper Access Control

#10. Insecure Deserialization (12% of apps)

Insecure deserialization is less common than the other categories but carries disproportionately high impact. When an application deserializes untrusted data without validation, attackers can manipulate the serialized payload to achieve remote code execution, privilege escalation, or denial of service.

In the JavaScript/TypeScript ecosystem, the most relevant variants are prototype pollution (manipulating __proto__ or constructor.prototypeto inject properties into all objects), unsafe use of eval() orFunction() to parse data, and JSON parsing of user-controlled input without schema validation. Prototype pollution is especially insidious because it can turn otherwise unexploitable code paths into RCE vectors by altering the behavior of built-in methods.

Real example (anonymized): A Node.js application used_.merge() from an outdated Lodash version to deep-merge user preferences. By sending {"__proto__": {"isAdmin": true}} as the preference payload, we polluted the Object prototype. Every subsequent authorization check that testeduser.isAdmin returned true, granting admin access to all sessions.

// Vulnerable: deep merge without prototype check
import _ from "lodash";
const prefs = _.merge({}, existingPrefs, userInput);

// Fixed: use a safe merge or validate with Zod
import { z } from "zod";

const PrefsSchema = z.object({
  theme: z.enum(["dark", "light"]).optional(),
  language: z.string().max(5).optional(),
  notifications: z.boolean().optional(),
});

const validated = PrefsSchema.parse(userInput);
const prefs = { ...existingPrefs, ...validated };

CWE-502 · Deserialization of Untrusted Data CWE-1321 · Improperly Controlled Modification of Object Prototype

Vulnerability Trends (2022 to 2026)

Some categories are declining as frameworks and tools improve. Others are rising as application architectures evolve. The chart below shows the trajectory of our top three findings over the past five years.

Broken access control prevalence (2022-2026)

20222026+31%

XSS prevalence (2022-2026, declining)

20222026-40%

SSRF prevalence (2022-2026, rising with cloud adoption)

20222026+144%

Summary: Severity Distribution

Across all 3,200+ findings from our 500+ pentests, the severity distribution skews toward medium-severity issues. However, nearly one-third of findings are rated High or Critical, meaning most applications have at least one vulnerability that could lead to significant data exposure or system compromise if exploited.

Severity distribution across all findings

Critical8%
High24%
Medium40%
Low22%
Informational6%

The single most effective remediation strategy is implementing proper authorization checks. This directly addresses Broken Access Control (#1), Authentication Flaws (#6), and SSRF (#8). Adding a consistent authorization layer, whether through middleware, a policy engine like CASL or Cerbos, or framework-level guards, eliminates the largest category of findings in one systematic effort.

The second highest-impact fix is adding rate limiting to all public-facing endpoints. This mitigates Missing Rate Limiting (#2) and reduces the exploitability of Authentication Flaws (#6) and Injection (#5). Using a service like Upstash Rate Limit or Cloudflare WAF, teams can deploy rate limiting across their entire API surface in under a day.

For teams with limited security budgets, we recommend prioritizing in this order: authorization checks, rate limiting, input validation (covers injection and XSS), secret scanning in CI/CD (covers sensitive data exposure), and DNS record auditing (covers subdomain takeover). This sequence addresses 85% of the findings we report, sorted by impact and implementation effort.

How does your app compare?

Run a free security scan and find out which of these vulnerabilities affect you.

Get your free scan

Sources

OWASP Top 10 (2025 Edition)
Recon0x Internal Data (500+ automated pentests, 2024-2026)
HackerOne Top 10 Most Impactful Vulnerability Types (2025)
Snyk State of Open Source Security Report (2025)
Verizon Data Breach Investigations Report (2025)