27 August 2026

10 Security Issues JavaScript Developers Must Fix

Reza Khosravi
No items found.

Table of Contents

10 Security Issues JavaScript Developers Must Fix

A large web measurement study found that 37.8% of more than 133,000 websites used at least one JavaScript library version with a known vulnerability, while nearly 10% used two or more vulnerable versions. The same research showed how JavaScript dependencies had already spread across the web, with common libraries appearing on 87.7% of Alexa Top 75k sites and 46.5% of .com sites by 2017. The study's findings make one point clear: security issues in JavaScript rarely come from a single careless line of syntax.

They enter when an application loses the context around untrusted data, identity, authorization, dependency provenance, API boundaries, or deployment behavior. A clean-looking frontend can still trust a forged object identifier. A well-tested backend can still execute a compromised post-install script. A dependency scanner can report a vulnerability without telling the team whether the affected path is exposed or exploitable.

This roundup follows that path from design to code, dependencies, APIs, and operations. Each risk includes where it appears, how to detect it, what an exploit scenario looks like, and which control belongs before release. Teams building regulated products can also use secure digital transformation solutions alongside continuous security practices. DevArmor is one relevant example, combining continuous threat modeling, workflow-based design reviews, implementation verification, and Policy-as-Code enforcement so security decisions stay connected to developer work.

1. Cross-Site Scripting and Content Security Policy

Cross-site scripting, or XSS, starts when an application places attacker-controlled data into an HTML, JavaScript, URL, CSS, or DOM context without the right validation and output encoding. The result can be a script executing in a user's browser with access to the application's page context, including visible data, actions available to the user, and sometimes session information.

The source and delivery path matter. Stored XSS can persist in comments or profiles until another user views it. Reflected XSS can return an unsafe parameter directly in a response. DOM-based XSS can occur entirely in browser code, such as a single-page application reading a URL fragment and assigning it to innerHTML. A sanitization library helps only when developers use it for the correct context and keep it maintained.

A diagram illustrating how an XSS attack works and how Content Security Policy defends against these vulnerabilities.

Use CSP as a second boundary

A Content Security Policy limits where scripts and other resources may load from. It won't repair unsafe rendering, but it can reduce the impact of an injection when the policy excludes unexpected script sources. Policies containing unsafe-inline or broad source wildcards often weaken that benefit.

Practical rule: Treat CSP as defense in depth, not as permission to skip context-aware encoding.

Use framework templating with auto-escaping, validate input at entry points, and encode output for its destination. In modern applications, a nonce-based policy or strict-dynamic can support controlled script loading. Start with report-only mode, inspect violations, then enforce a policy that reflects actual third-party requirements. Add CSP presence and strength to pull-request policy checks, and combine it with X-Frame-Options and X-Content-Type-Options.

2. Cross-Site Request Forgery

CSRF abuses the browser's willingness to attach a user's existing credentials to a request. The attacker doesn't need to steal the session if a victim is already authenticated. A malicious page can submit a state-changing request to a target application, and the application may accept it because the request appears to come from the victim's browser.

A banking interface that accepts a transfer request without a CSRF defense illustrates the risk. Similar flaws can change passwords, modify administrator settings, cancel healthcare appointments, or update account details. A hidden form or iframe may be enough when the endpoint relies only on cookies and doesn't verify the request's intent.

Protect every state-changing route

CSRF protection belongs on POST, PUT, and DELETE operations, not only on visibly sensitive screens. Synchronizer tokens or signed double-submit tokens give the server evidence that the request came through the intended application flow. A custom header such as X-CSRF-Token is useful for APIs because ordinary cross-origin forms can't set it freely.

Cookie settings strengthen the boundary, but they aren't a complete substitute for server-side validation. Use SameSite=Strict where sensitive workflows can tolerate it, and consider SameSite=Lax for broader compatibility. Require re-authentication or step-up verification for especially consequential actions.

  • Threat-model modifying endpoints: Record the session mechanism and CSRF requirement for each authenticated route.
  • Enforce the rule centrally: Middleware and Policy-as-Code should reject missing validation instead of relying on individual developers.
  • Test browser behavior: Security tests should cover cross-origin forms, redirects, iframe attempts, and requests sent with existing cookies.

Client-side checks don't count as authorization or CSRF protection. The server must decide whether the request is valid.

A digital illustration showing a hacker exploiting a cross-site request forgery vulnerability on a bank website.

3. Insecure Deserialization

Deserialization turns data into an object or value that application code can use. The danger appears when developers treat untrusted serialized input as if it were a trusted internal object. In JavaScript, native JSON.parse is generally safer than executable serialization formats, but unsafe behavior can still enter through custom revivers, complex object reconstruction, YAML parsers, or third-party serialization libraries.

A server that accepts a serialized configuration and reconstructs classes without an allowlist may expose dangerous behavior. A malicious payload could manipulate object properties, trigger unexpected methods, or reach a code path that was never intended for external input. Prototype pollution can also begin at a parsing or merge boundary, then affect later authorization or configuration decisions.

Keep data separate from behavior

Use plain JSON for untrusted interchange where possible. Parse it into a narrow schema, reject unknown properties when the business contract allows that, and avoid custom reviver functions unless the application needs them and validates their output rigorously.

Untrusted serialized data should produce data structures, not executable behavior.

Treat every deserialization point as a threat-model entry point. Pin and review serialization dependencies, monitor advisories, and use Policy-as-Code to flag packages with known deserialization weaknesses. Don't serialize passwords, authentication tokens, or sensitive runtime objects when a purpose-built representation will do.

A practical test sends malformed, nested, unexpected, and type-confusing payloads to every parser. The test should verify both rejection and safe failure. Returning a generic validation error is preferable to exposing stack traces, parser internals, or partially constructed objects.

4. Prototype Pollution

Prototype pollution can turn one untrusted key into shared application state. The risk enters through deep-merge helpers, recursive assignments, configuration loaders, and query parsers. Keys such as __proto__, constructor, and prototype require explicit handling because ordinary object operations may change what unrelated objects inherit.

A request parameter that reaches a vulnerable merge could place a property on Object.prototype. Later code may treat that inherited value as an administrator flag, a default setting, or an option that controls a security-sensitive action. Browser applications and Node.js services are both exposed, especially when shared utilities process request data across multiple routes or services.

Recent reporting estimated that prototype pollution represented roughly 6% to 8% of new npm advisories in early 2025, compared with about 3% to 4% in 2022 and 2023. Deep-merge and configuration utilities remained prominent sources, while schema validators and templating libraries also appeared in findings, according to The 2025 trend report.

A hand-drawn illustration showing the JavaScript prototype chain with a crown on Object.prototype and various warning icons.

Make object boundaries explicit

Keep user input outside application objects until it passes schema validation. Reject dangerous property names, prefer allowlists, and use Object.create(null) for dictionaries that do not need inherited behavior. Freezing selected prototypes can block some mutation paths, but may break dependencies, so apply it selectively and test regulated workloads before enforcement.

Review every merge, extend, defaults helper, and recursive configuration function. Security decisions should use own-property checks, not inherited values. Dependency review must include transitive packages, since a safe direct dependency can still pull in a vulnerable utility.

Combine dependency advisories with static analysis and runtime tests. Send malformed, nested, and unexpected keys through affected parsers, then verify rejection, safe failure, and the absence of changed prototype state. A scanner identifies a vulnerable package. Threat modeling determines whether attacker-controlled keys can reach it.

5. Insecure Direct Object Reference

Authentication does not grant access to every object. An IDOR flaw appears when an application accepts a client-supplied reference without checking whether the authenticated user may access that specific resource. Changing /account/123 to another identifier must not expose someone else's statement, medical record, invoice, or appointment. Hiding identifiers in a JavaScript client offers no protection because users can inspect and modify requests.

Authorization belongs on the server and must cover every read, update, and delete operation. A user can belong to an organization yet lack permission for a particular patient record, file, or tenant. UUIDs make enumeration less convenient, but they do not replace an authorization decision.

The practical control is to make unauthorized access difficult at the data-access boundary. Fetch records through an authorization-aware repository, or include tenant and ownership constraints in the query itself. Avoid retrieving by ID first and checking ownership afterward, since later checks are easier to omit during maintenance or in a less-tested code path.

Build the control around three checks:

  • Map resources to permissions: Record who may read, update, delete, or administer each object.
  • Test neighboring identifiers: Integration tests should cross user, role, tenant, and organization boundaries.
  • Log denials: Repeated attempts against other users' objects can indicate enumeration or compromised credentials.

Secure code review guidance helps reviewers connect each endpoint's approved authorization rule to its implementation. Require an explicit permission path in API handlers, then verify rejection with server-side integration tests. For regulated workloads, retain denial records with enough context for investigation without logging sensitive object contents.

6. Supply Chain Vulnerabilities in npm Dependencies

JavaScript applications inherit behavior from direct and transitive npm packages. A compromised maintainer account, malicious release, typosquatted package, or vulnerable utility can introduce code that the application team didn't write and may not have inspected. Lifecycle scripts make the risk more urgent because installation itself can trigger file or network operations.

The ecosystem's scale creates a difficult trade-off. Developers need packages to deliver features quickly, while regulated teams must explain provenance, version selection, licensing, and remediation decisions. A lockfile improves reproducibility, but it doesn't prove that the locked package is trustworthy.

Sonatype's 2026 supply-chain analysis reported that npm accounted for more than 60% of new releases across major registries and concentrated over 99% of open-source malware activity in 2025. The same coverage reported that vulnerable npm releases rose from 16.8% in 2024 to 21.0% in 2025, with 838,778 releases associated with CVSS 9.0 or higher issues. The analysis supports treating dependency governance as a release control, not a housekeeping task.

A hand-drawn illustration showing a software dependency tree with a vulnerable typosquatted package highlighted in red.

Control what enters the build

Generate an SBOM, use an approved package allowlist, pin production dependencies, and route installs through a controlled registry proxy. Restrict lifecycle scripts where feasible, verify provenance, and monitor changes in transitive dependencies. Software composition analysis practices help teams connect package findings to actual applications and release decisions.

CISA described the 2025 Shai-Hulud incident as a self-replicating worm that compromised over 500 npm packages. CISA's alert explains why trusted publishing, maintainer-account protection, and CI monitoring matter. Scanning after installation is useful, but it can't be the only control.

7. Broken Authentication and Session Management

A single authentication flaw can turn a valid user session into unauthorized access. Common entry points include weak session lifecycle rules, unsafe token storage, custom cryptography, incomplete logout, and missing checks for sensitive actions. In a browser application, storing a session token in localStorage lets a successful XSS payload read it. In a Node.js service, a weak JWT signing secret can compromise every token issued with it.

Use established identity components for password handling, token formats, multifactor authentication, and account recovery. Store browser sessions in httpOnly, Secure, and appropriately configured SameSite cookies. Generate tokens with a cryptographically secure source. Revoke or rotate sessions after credential changes, privilege changes, or suspected compromise.

Test the identity lifecycle, not just login

Review registration, login, multifactor authentication, recovery, logout, renewal, device changes, and administrative impersonation as one connected flow. Define inactivity behavior and require re-authentication for operations such as changing credentials, permissions, or payment details.

  • Rate-limit authentication: Apply limits by account, source, and other relevant signals while keeping error messages and timing consistent enough to resist account enumeration.
  • Protect sensitive actions: Require a fresh authentication step before high-impact changes.
  • Audit identity events: Record successful and failed authentication, recovery attempts, session revocation, and privilege changes.
  • Use uniform responses: Do not reveal whether an account exists through messages or timing differences.

For regulated teams, policy checks can enforce approved authentication libraries, cookie attributes, rate limits, session invalidation, and audit events in CI and deployment reviews. These controls reduce preventable omissions, while penetration testing and incident response remain necessary for failures that configuration checks cannot detect.

8. Insecure API Development and Exposure

APIs define the trust boundaries between browsers, mobile clients, internal services, partners, and administrative tools. JavaScript frameworks such as Express, NestJS, and Next.js make it easy to expose a route quickly. They don't automatically determine whether the caller is authenticated, whether the requested object is permitted, or whether the response contains more information than the client needs.

A GraphQL endpoint with introspection enabled in production may disclose internal structure. A REST route may return every user field because the handler serializes a database object directly. A microservice may trust that requests from another service are safe, even though attackers can reach the same network path through a compromised workload.

Make the secure path the default

Authenticate every endpoint unless the route is explicitly public. Authorize at the resource and operation level, validate request bodies and query parameters with JSON Schema, Zod, or equivalent tooling, and return only fields required by the client. Rate limits should reflect user, token, IP, and operation risk rather than relying on one global threshold.

Use pagination and bounded filters to prevent accidental bulk extraction. Disable unnecessary GraphQL introspection, restrict CORS to known origins, and return generic errors that don't expose database schemas or stack traces. API modernization guidance is relevant when legacy routes need to be mapped before teams can apply consistent controls.

An API contract should state who may call an operation, which objects they may reach, what data they receive, and how abuse is detected.

Threat-model each endpoint and enforce required authentication, authorization, schema validation, headers, and rate limits before merge. Security tests should cover alternate HTTP methods, missing fields, oversized inputs, tenant changes, and unexpected content types.

9. Insufficient Logging and Monitoring

A security control without evidence is difficult to operate and defend during an audit. JavaScript applications often record routine events but omit authentication failures, authorization denials, sensitive-data access, rate-limit triggers, and administrative changes. Responders then lack the sequence required to separate a defect from abuse, especially when activity crosses APIs, services, and tenant boundaries.

Treat logs as security-sensitive data. Do not record passwords, access tokens, medical details, or unnecessary personal information. Each useful event should include a timestamp, request or correlation identifier, actor context, action, resource, result, and source metadata that fits the system's privacy requirements.

Make security decisions observable

Record successful and failed authentication, permission failures, privilege changes, access to sensitive records, bulk operations, suspicious input patterns, and unusual API behavior. Centralize those events in an access-controlled platform such as an ELK deployment, Splunk, or a cloud logging service. Protect log integrity, and set retention according to operational and regulatory requirements.

Detection depends on context and continuity:

  • Alert on patterns: Repeated failures, rapid object enumeration, unusual access times, and bulk reads should enter triage workflows.
  • Trace requests: Carry correlation identifiers across services so investigators can reconstruct a transaction.
  • Test the pipeline: Verify that logs arrive with required fields, trigger alerts, meet retention rules, and remain available during an incident.
  • Block debug leakage: Use build checks and policy controls to prevent production code from emitting sensitive debug output.

For regulated teams, logging requirements belong in threat models and control tests, not only in incident procedures. A scanner can identify missing logging calls, but application owners must confirm that each event provides enough context to support investigation without creating a second data-exposure problem. Review event schemas during design, validate them in staging, and sample production records under controlled access.

10. Unsafe Use of eval and Dynamic Code Execution

Dynamic code execution turns data into authority. eval(), the Function constructor, and timer functions that accept strings can execute attacker-controlled JavaScript when an input reaches the constructed string. In a browser, that code inherits the page's privileges. In Node.js, it may reach environment variables, files, network services, or credentials available to the process.

The risk often enters through legitimate features, including formula evaluation, configuration parsing, template compilation, and plugins. Separate those requirements from arbitrary execution. JSON.parse is suited to JSON, expression parsers can restrict mathematical syntax, and function maps can connect approved operation names to known functions.

Control capabilities at the execution boundary

A request parameter passed into an evaluator can become remote code execution. External content evaluated by a browser can cross the intended data boundary. Sandboxing can reduce impact in carefully designed cases, but it introduces isolation and maintenance requirements. Regulated teams should document which code may run, under which identity, and with access to which resources.

Apply controls across the delivery path:

  • Ban unsafe patterns: Configure ESLint with no-eval, then block eval, string-based timers, and unrestricted Function construction through Policy-as-Code.
  • Use allowlists: Map permitted operation names to fixed functions instead of assembling calls from strings.
  • Audit legacy paths: Search source files, generated bundles, templates, and configuration loaders for dynamic execution.
  • Review plugins separately: Define capabilities, isolate execution, and control module loading when extensibility is required.

JavaScript static analysis practices can help locate dangerous execution patterns during review. Findings still need threat modeling and tests. Confirm that replacements reject unexpected syntax, preserve authorization boundaries, and prevent access beyond the feature's stated capability. This evidence also gives security and compliance teams a concrete control trail.

10-Point Comparison of JavaScript Security Issues

ItemImplementation Complexity šŸ”„Resource Requirements ⚔Expected Outcomes šŸ“ŠIdeal Use Cases ⭐Key Advantages šŸ’”
Cross-Site Scripting (XSS) and Content Security Policy (CSP)Medium–High: policy tuning, nonce/strict-dynamic managementLow–Medium: config, dev time, CSP reporting infrastructureStrong reduction in XSS impact; blocks unauthorized scripts and enables violation telemetryRich JS front-ends, SPAs, regulated web appsDefense-in-depth, browser-enforced, complements sanitization; report-only testing
Cross-Site Request Forgery (CSRF)Low: tokens or SameSite cookies implementationLow: token middleware or cookie settingsPrevents forged state-changing requests and unauthorized transactionsSession-authenticated web apps, banking/health flowsStandardized defenses, simple to implement and test
Insecure DeserializationMedium: audit deserializers, reviver-safe designsMedium: dependency audits, allowlists, input validationPrevents RCE/data integrity compromise from malicious payloadsServices handling complex object formats or third-party serializersPrefer safe JSON.parse; allowlist patterns; straightforward remediations
Prototype PollutionHigh: deep-merge logic review and preventionMedium–High: static analysis, dependency checks, code changesPrevents app-wide behavior tampering and privilege escalation chainsNode.js libraries, deep-merge utilities, config mergingJavaScript-specific mitigations, documented hardening patterns
Insecure Direct Object Reference (IDOR)Low–Medium: enforce server-side authorization per endpointLow: authorization checks, UUIDs, ownership verificationPrevents unauthorized access/modification of resourcesREST APIs, SPAs, regulated data endpointsClear fixes (auth checks); testable via design review and automation
Supply Chain Vulnerabilities in npm DependenciesMedium: inventorying and vetting transitive depsHigh: SCA tooling, SBOMs, policy-as-code, registry proxiesReduces risk of compromised or malicious packages; aids complianceLarge projects, orgs needing provenance and complianceAutomated scanning, lockfiles, allowlists, SBOM support
Broken Authentication & Session ManagementMedium: secure token storage, MFA, session lifecycleMedium: auth providers, MFA services, loggingReduces account takeover and session hijacking risksAny user-auth systems, FinTech, HealthTechStrong standards and libraries (NIST, OWASP); clear remediations
Insecure API Development & ExposureMedium–High: per-endpoint auth, GraphQL authorizationMedium: schema validation, rate limiting, auth middlewarePrevents data leakage, abuse, and unauthorized accessMicroservices, public APIs, GraphQL endpointsEstablished best practices, middleware support, API schemas
Insufficient Logging & MonitoringLow–Medium: instrumenting logs, correlation IDs, alertsMedium–High: SIEM/ELK, retention, alert tuningFaster detection, forensic readiness, compliance evidenceRegulated environments, critical systems, production servicesMature tooling and frameworks; explicit audit requirements
Unsafe Use of eval() and Dynamic Code ExecutionLow–Medium: eliminate/replace eval patterns (may require refactor)Low: linters, safer libs, sandboxing if neededEliminates a primary RCE vector and reduces attack surfaceServer-side Node.js code and legacy client codeClear prohibition with linting; safe alternatives (JSON.parse, expr libs)

Turn Findings Into Enforceable Controls

The most useful response to security issues in JavaScript isn't a longer list of scanner alerts. It's a workflow that preserves security context from the first design decision through deployment and operation. Start by modeling untrusted data and trust boundaries before implementation. Identify where browser input, API payloads, serialized objects, dependency code, identity claims, and administrative actions enter the system. For each path, record the expected validation, authorization, encoding, logging, and failure behavior.

Design reviews should produce decisions that developers can apply. An endpoint model should say which identity can perform an operation and which resource constraints apply. A rendering decision should identify the output context and required encoding. A dependency decision should capture provenance, allowed behavior, update ownership, and the response if a package becomes vulnerable. These artifacts are more useful when they stay connected to tickets, repositories, services, and changes rather than becoming static documents.

Implementation review then verifies that the code matches the approved design. Look for server-side authorization on every object access, schema validation before business logic, safe deserialization, controlled merges, secure cookie attributes, rate limits, and explicit logging. Dangerous APIs such as eval, dynamic module loading, unsafe DOM sinks, and unrestricted lifecycle scripts deserve clear blocking rules. Dependency alerts should be triaged by exploitability and exposure. A vulnerable package used only in an isolated build tool is different from one reachable through a public request path, although both still require ownership and a remediation decision.

Policy-as-Code makes those expectations repeatable. Teams can require a CSRF mechanism on cookie-authenticated state changes, reject unsafe dynamic execution, require approved authentication components, check dependency allowlists, and block missing authorization evidence in selected handlers. Merge blocking should be reserved for controls with clear security meaning and an agreed exception process. Otherwise, teams may bypass the policy or learn to ignore noisy findings.

Post-deployment verification closes the loop. Confirm that security logs arrive centrally, CSP reports are useful, alerts fire for suspicious access, dependency changes are visible, and deployed configuration matches the reviewed design. Incident response remains necessary because no static rule, platform, or review process catches every failure.

DevArmor can be relevant for this operating model because it maintains living threat models, brings security design reviews into tools such as GitHub, Jira, Google Docs, VS Code, Cursor, and MCP, and maps approved decisions to pull-request Policy-as-Code checks. Its implementation verification can help connect design requirements to code changes and deployments. It doesn't replace secure coding, testing, dependency ownership, monitoring, or incident response. Teams can also review enterprise security guardrails when aligning these controls with broader governance needs.

Prioritize remediation by exploit impact, exposure, affected data, privilege required, and ease of detection, not by scanner severity alone. A public unauthenticated deserialization path, a cross-tenant object access flaw, and a compromised production dependency deserve immediate ownership. A low-impact development-only finding may need tracking and scheduled maintenance instead. That prioritization turns security from a backlog of warnings into a set of enforceable engineering decisions.


DevArmor offers continuous threat modeling, security design reviews, implementation verification, and Policy-as-Code checks for software delivery workflows. Use it to connect JavaScript security requirements with pull requests and deployment decisions, then visit DevArmor to evaluate how it fits your team's review and governance process.

Table of Contents

Subscribe