mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-02-27 12:22:43 -08:00
update
This commit is contained in:
201
.agents/skills/security-best-practices/LICENSE.txt
Normal file
201
.agents/skills/security-best-practices/LICENSE.txt
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf of
|
||||
any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don\'t include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
86
.agents/skills/security-best-practices/SKILL.md
Normal file
86
.agents/skills/security-best-practices/SKILL.md
Normal file
@@ -0,0 +1,86 @@
|
||||
---
|
||||
name: "security-best-practices"
|
||||
description: "Perform language and framework specific security best-practice reviews and suggest improvements. Trigger only when the user explicitly requests security best practices guidance, a security review/report, or secure-by-default coding help. Trigger only for supported languages (python, javascript/typescript, go). Do not trigger for general code review, debugging, or non-security tasks."
|
||||
---
|
||||
|
||||
# Security Best Practices
|
||||
|
||||
## Overview
|
||||
|
||||
This skill provides a description of how to identify the language and frameworks used by the current context, and then to load information from this skill's references directory about the security best practices for this language and or frameworks.
|
||||
|
||||
This information, if present, can be used to write new secure by default code, or to passively detect major issues within existing code, or (if requested by the user) provide a vulnerability report and suggest fixes.
|
||||
|
||||
## Workflow
|
||||
|
||||
The initial step for this skill is to identify ALL languages and ALL frameworks which you are being asked to use or already exist in the scope of the project you are working in. Focus on the primary core frameworks. Often you will want to identify both frontend and backend languages and frameworks.
|
||||
|
||||
Then check this skill's references directory to see if there are any relevant documentation for the language and or frameworks. Make sure you read ALL reference files which relate to the specific framework or language. The format of the filenames is `<language>-<framework>-<stack>-security.md`. You should also check if there is a `<language>-general-<stack>-security.md` which is agnostic to the framework you may be using.
|
||||
|
||||
If working on a web application which includes a frontend and a backend, make sure you have checked for reference documents for BOTH the frontend and backend!
|
||||
|
||||
If you are asked to make a web app which will include both a frontend and backend, but the frontend framework is not specified, also check out `javascript-general-web-frontend-security.md`. It is important that you understand how to secure both the frontend and backend.
|
||||
|
||||
If no relevant information is available in the skill's references directory, think a little bit about what you know about the language, the framework, and all well known security best practices for it. If you are unsure you can try to search online for documentation on security best practices.
|
||||
|
||||
From there it can operate in a few ways.
|
||||
|
||||
1. The primary mode is to just use the information to write secure by default code from this point forward. This is useful for starting a new project or when writing new code.
|
||||
|
||||
2. The secondary mode is to passively detect vulnerabilities while working in the project and writing code for the user. Critical or very important vulnerabilities or major issues going against security guidance can be flagged and the user can be told about them. This passive mode should focus on the largest impact vulnerabilities and secure defaults.
|
||||
|
||||
3. The user can ask for a security report or to improve the security of the codebase. In this case a full report should be produced describe anyways the project fails to follow security best practices guidance. The report should be prioritized and have clear sections of severity and urgency. Then offer to start working on fixes for these issues. See #fixes below.
|
||||
|
||||
## Workflow Decision Tree
|
||||
|
||||
- If the language/framework is unclear, inspect the repo to determine it and list your evidence.
|
||||
- If matching guidance exists in `references/`, load only the relevant files and follow their instructions.
|
||||
- If no matching guidance exists, consider if you know any well known security best practices for the chosen language and or frameworks, but if asked to generate a report, let the user know that concrete guidance is not available (you can still generate the report or detect for sure critical vulnerabilities)
|
||||
|
||||
# Overrides
|
||||
|
||||
While these references contain the security best practices for languages and frameworks, customers may have cases where they need to bypass or override these practices. Pay attention to specific rules and instructions in the project's documentation and prompt files which may require you to override certain best practices. When overriding a best practice, you MAY report it to the user, but do not fight with them. If a security best practice needs to be bypassed / ignored for some project specific reason, you can also suggest to add documentation about this to the project so it is clear why the best practice is not being followed and to follow that bypass in the future.
|
||||
|
||||
# Report Format
|
||||
|
||||
When producing a report, you should write the report as a markdown file in `security_best_practices_report.md` or some other location if provided by the user. You can ask the user where they would like the report to be written to.
|
||||
|
||||
The report should have a short executive summary at the top.
|
||||
|
||||
The report should be clearly delineated into multiple sections based on severity of the vulnerability. The report should focus on the most critical findings as these have the highest impact for the user. All findings should be noted with an numeric ID to make them easier to reference.
|
||||
|
||||
For critical findings include a one sentence impact statement.
|
||||
|
||||
Once the report is written, also report it to the user directly, although you may be less verbose. You can offer to explain any of the findings or the reasons behind the security best practices guidance if the user wants more info on any findings.
|
||||
|
||||
Important: When referencing code in the report, make sure to find and include line numbers for the code you are referencing.
|
||||
|
||||
After you write the report file, summarize the findings to the user.
|
||||
|
||||
Also tell the user where the final report was written to
|
||||
|
||||
# Fixes
|
||||
|
||||
If you produced a report, let the user read the report and ask to begin performing fixes.
|
||||
|
||||
If you passively found a critical finding, notify the user and ask if they would like you to fix this finding.
|
||||
|
||||
When producing fixes, focus on fixing a single finding at a time. The fixes should have concise clear comments explaining that the new code is based on the specific security best practice, and perhaps a very short reason why it would be dangerous to not do it in this way.
|
||||
|
||||
Always consider if the changes you want to make will impact the functionality of the user's code. Consider if the changes may cause regressions with how the project works currently. It is often the case that insecure code is relied on for other reasons (and this is why insecure code lives on for so long). Avoid breaking the user's project as this may make them not want to apply security fixes in the future. It is better to write a well thought out, well informed by the rest of the project, fix, then a quick slapdash change.
|
||||
|
||||
Always follow any normal change or commit flow the user has configured. If making git commits, provide clear commit messages explaining this is to align with security best practices. Try to avoid bunching a number of unrelated findings into a single commit.
|
||||
|
||||
Always follow any normal testing flows the user has configured (if any) to confirm that your changes are not introducing regressions. Consider the second order impacts the changes may have and inform the user before making them if there are any.
|
||||
|
||||
# General Security Advice
|
||||
|
||||
Below is a few bits of secure coding advice that applies to almost any language or framework.
|
||||
|
||||
### Avoid Using Incrementing IDs for Public IDs of Resources
|
||||
|
||||
When assigning an ID for some resource, which will then be used by exposed to the internet, avoid using small auto-incrementing IDs. Use longer, random UUID4 or random hex string instead. This will prevent users from learning the quantity of a resource and being able to guess resource IDs.
|
||||
|
||||
### A note on TLS
|
||||
|
||||
While TLS is important for production deployments, most development work will be with TLS disabled or provided by some out-of-scope TLS proxy. Due to this, be very careful about not reporting lack of TLS as a security issue. Also be very careful around use of "secure" cookies. They should only be set if the application will actually be over TLS. If they are set on non-TLS applications (such as when deployed for local dev or testing), it will break the application. You can provide a env or other flag to override setting secure as a way to keep it off until on a TLS production deployment. Additionally avoid recommending HSTS. It is dangerous to use without full understanding of the lasting impacts (can cause major outages and user lockout) and it is not generally recommended for the scope of projects being reviewed by codex.
|
||||
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Security Best Practices"
|
||||
short_description: "Security reviews and secure-by-default guidance"
|
||||
default_prompt: "Review this codebase for security best practices and suggest secure-by-default improvements."
|
||||
@@ -0,0 +1,826 @@
|
||||
# Go (Golang) Security Spec (Go 1.25.x, Standard Library, net/http)
|
||||
|
||||
This document is designed as a **security spec** that supports:
|
||||
1) **Secure-by-default code generation** for new Go code.
|
||||
2) **Security review / vulnerability hunting** in existing Go code (passive “notice issues while working” and active “scan the repo and report findings”).
|
||||
|
||||
It is intentionally written as a set of **normative requirements** (“MUST/SHOULD/MAY”) plus **audit rules** (what bad patterns look like, how to detect them, and how to fix/mitigate them).
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)
|
||||
|
||||
- MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session cookies, JWTs, database URLs with credentials, signing keys, client secrets).
|
||||
- MUST NOT “fix” security by disabling protections (e.g., `InsecureSkipVerify`, `GOSUMDB=off` for public modules, wildcard CORS + credentials, removing auth checks, disabling CSRF defenses on cookie-auth apps).
|
||||
- MUST provide **evidence-based findings** during audits: cite file paths, code snippets, build/deploy configs, and concrete values that justify the claim.
|
||||
- MUST treat uncertainty honestly: if a control might exist in infrastructure (reverse proxy, WAF, service mesh, platform config), report it as “not visible in app code; verify at runtime/config.”
|
||||
- MUST keep fixes minimal, correct, and production-safe; avoid introducing breaking changes without warning (especially around auth/session flows, and proxies).
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 1) Operating modes
|
||||
|
||||
### 1.1 Generation mode (default)
|
||||
When asked to write new Go code or modify existing code:
|
||||
- MUST follow every **MUST** requirement in this spec.
|
||||
- SHOULD follow every **SHOULD** requirement unless the user explicitly says otherwise.
|
||||
- MUST prefer safe-by-default APIs and proven libraries over custom security code.
|
||||
- MUST avoid introducing new risky sinks (shell execution, dynamic template execution, serving user files as HTML, unsafe redirects, weak crypto, unbounded parsing, etc.).
|
||||
|
||||
### 1.2 Passive review mode (always on while editing)
|
||||
While working anywhere in a Go repo (even if the user did not ask for a security scan):
|
||||
- MUST “notice” violations of this spec in touched/nearby code.
|
||||
- SHOULD mention issues as they come up, with a brief explanation + safe fix.
|
||||
|
||||
### 1.3 Active audit mode (explicit scan request)
|
||||
When the user asks to “scan”, “audit”, or “hunt for vulns”:
|
||||
- MUST systematically search the codebase for violations of this spec.
|
||||
- MUST output findings in a structured format (see §2.3).
|
||||
|
||||
Recommended audit order:
|
||||
1) Build/deploy entrypoints: `main.go`, `cmd/*`, Dockerfiles, Kubernetes manifests, systemd units, CI workflows.
|
||||
2) Go toolchain & dependency policy: Go version, modules, `go.mod/go.sum`, proxy/sumdb settings, govulncheck usage.
|
||||
3) Secret management and config loading (env, files, secret stores) + logging patterns.
|
||||
4) HTTP server configuration (timeouts, body limits, proxy trust, security headers).
|
||||
5) AuthN/AuthZ boundaries, session/cookie settings, token validation.
|
||||
6) CSRF protections for cookie-authenticated state-changing endpoints.
|
||||
7) Template usage and output encoding (XSS), and any “render template from string” behavior (SSTI).
|
||||
8) File handling (uploads/downloads/path traversal/temp files), static file serving.
|
||||
9) Injection sinks: SQL, OS command execution, SSRF/outbound fetch, open redirects.
|
||||
10) Concurrency/resource exhaustion (unbounded goroutines/queues, missing timeouts/contexts).
|
||||
11) Use of `unsafe` / `cgo` / `reflect` in security-sensitive paths.
|
||||
12) Debug/diagnostic endpoints (pprof/expvar/metrics) exposure.
|
||||
13) Cryptography usage (randomness, password hashing).
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 2) Definitions and review guidance
|
||||
|
||||
### 2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)
|
||||
Examples include:
|
||||
- `*http.Request` fields: `r.URL.Path`, `r.URL.RawQuery`, `r.Form`, `r.PostForm`, headers, cookies, `r.Body`
|
||||
- Path parameters from routers (including values extracted from URL paths)
|
||||
- JSON/XML/YAML bodies, multipart form parts, uploaded files
|
||||
- Any data from external systems (webhooks, third-party APIs, message queues)
|
||||
- Any persisted user content (DB rows) that originated from users
|
||||
- Configuration values that might be attacker-influenced in some deployments (headers set by upstream proxies, environment variables in multi-tenant systems)
|
||||
|
||||
### 2.2 State-changing request
|
||||
A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook send), or initiate privileged actions.
|
||||
|
||||
### 2.3 Required audit finding format
|
||||
For each issue found, output:
|
||||
|
||||
- Rule ID:
|
||||
- Severity: Critical / High / Medium / Low
|
||||
- Location: file path + function/handler name + line(s)
|
||||
- Evidence: the exact code/config snippet
|
||||
- Impact: what could go wrong, who can exploit it
|
||||
- Fix: safe change (prefer minimal diff)
|
||||
- Mitigation: defense-in-depth if immediate fix is hard
|
||||
- False positive notes: what to verify if uncertain (edge configs, proxy behavior, auth assumptions)
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 3) Secure baseline: minimum production configuration (MUST in production)
|
||||
|
||||
This is the smallest “production baseline” that prevents common Go misconfigurations.
|
||||
|
||||
### 3.1 Toolchain, patching, and dependency hygiene (MUST)
|
||||
- MUST run a supported Go major version and keep to the latest patch releases.
|
||||
- MUST treat Go standard library patch releases as security-relevant (many security fixes land in stdlib components like `net/http`, `crypto/*`, parsing packages).
|
||||
- MUST use Go modules with committed `go.mod` and `go.sum`.
|
||||
- MUST NOT disable module authenticity mechanisms for public modules (checksum DB) unless you have a controlled, documented replacement.
|
||||
- MUST run `govulncheck` (source scan and/or binary scan) in CI and address findings.
|
||||
|
||||
### 3.2 HTTP server baseline (MUST for network-facing services)
|
||||
If the program serves HTTP (directly or via a framework built on `net/http`):
|
||||
- MUST configure an `http.Server` with explicit timeouts and header limits.
|
||||
- MUST set request body size limits (global and per-route as needed).
|
||||
- MUST avoid exposing diagnostic endpoints (pprof/expvar) publicly.
|
||||
- SHOULD set a consistent set of security headers (or verify they are set at the edge).
|
||||
- MUST set cookie security attributes for any cookies you issue.
|
||||
- SHOULD implement rate limiting and abuse controls for auth and expensive endpoints.
|
||||
|
||||
Illustrative baseline skeleton (adjust to your project):
|
||||
- Create a dedicated mux (avoid implicit global defaults unless intentionally managed).
|
||||
- Wrap handlers with: panic-safe error handling, request ID, logging, auth, and limits.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 4) Rules (generation + audit)
|
||||
|
||||
Each rule contains: required practice, insecure patterns, detection hints, and remediation.
|
||||
|
||||
### GO-DEPLOY-001: Keep the Go toolchain and standard library updated (security releases)
|
||||
Severity: Medium
|
||||
|
||||
NOTE: Upgrading dependencies and the core Go version can break projects in unexpected ways. Focus on only security-critical dependencies and if noticed, let the user know rather than upgrading automatically.
|
||||
|
||||
Required:
|
||||
- MUST run a supported Go major release and apply patch releases promptly.
|
||||
- SHOULD treat patch releases as security-relevant, even if your application code didn’t change.
|
||||
|
||||
Insecure patterns:
|
||||
- Production builds pinned to old Go versions without a patching process.
|
||||
- Docker images like `golang:1.xx` or custom base images that are not updated regularly.
|
||||
- CI pipelines that intentionally suppress Go updates.
|
||||
|
||||
Detection hints:
|
||||
- Inspect CI (`.github/workflows`, `gitlab-ci.yml`, etc.) for `go-version:` or toolchain setup.
|
||||
- Inspect Dockerfiles for `FROM golang:` tags.
|
||||
- Inspect `go.mod` `go` directive and any toolchain pinning.
|
||||
|
||||
Fix:
|
||||
- Upgrade to the latest patch of a supported Go version.
|
||||
- Add an automated check (CI) that fails when Go is below an approved minimum.
|
||||
|
||||
Notes:
|
||||
- Go publishes regular minor releases that frequently include security fixes across standard library packages.
|
||||
|
||||
---
|
||||
|
||||
### GO-SUPPLY-001: Go module authenticity MUST NOT be disabled for public dependencies
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
- MUST keep module checksum verification enabled for public modules.
|
||||
- SHOULD commit `go.sum` and treat changes as security-sensitive.
|
||||
- MUST NOT use insecure module fetching settings for public modules.
|
||||
- MAY configure private module behavior using `GOPRIVATE`/`GONOSUMDB` for private repos, but must do so narrowly and intentionally.
|
||||
|
||||
Insecure patterns:
|
||||
- `GOSUMDB=off` in CI or production build environments for public modules.
|
||||
- `GONOSUMDB=*` or overly broad patterns that effectively disable verification.
|
||||
- `GOINSECURE=*` or broad `GOINSECURE` patterns for public modules.
|
||||
- `GOPROXY=direct` everywhere without a clear policy.
|
||||
|
||||
Detection hints:
|
||||
- Search build configs for `GOSUMDB`, `GONOSUMDB`, `GOINSECURE`, `GOPROXY`, `GOPRIVATE`.
|
||||
- Look for documentation/scripts that recommend disabling checksum DB “to make builds work”.
|
||||
|
||||
Fix:
|
||||
- Restore defaults for public module verification.
|
||||
- For private modules:
|
||||
- Set `GOPRIVATE=your.private.domain/*`
|
||||
- Configure an internal proxy or direct fetching, and restrict `GONOSUMDB` to private patterns only.
|
||||
|
||||
Notes:
|
||||
- Disabling checksum verification removes an important integrity layer against targeted or compromised upstream delivery.
|
||||
|
||||
---
|
||||
|
||||
### GO-CONFIG-001: Secrets must be externalized and never logged or committed
|
||||
Severity: High (Critical if credentials are committed)
|
||||
|
||||
Required:
|
||||
- MUST load secrets from environment variables, secret managers, or secure config files with restricted permissions.
|
||||
- MUST NOT hard-code secrets in Go source, test fixtures that may reach production, or build args.
|
||||
- MUST NOT log secrets or full credential-bearing connection strings.
|
||||
- SHOULD fail closed in production if required secrets are missing.
|
||||
|
||||
Insecure patterns:
|
||||
- String constants containing tokens/keys/passwords.
|
||||
- `.env` files or config files with secrets committed to repo.
|
||||
- Logging `os.Environ()`, dumping full configs, or printing DSNs.
|
||||
|
||||
Detection hints:
|
||||
- Search for suspicious literals (`API_KEY`, `SECRET`, `PASSWORD`, `Authorization:`).
|
||||
- Inspect config loaders and logging statements.
|
||||
- Inspect CI logs or debug print paths.
|
||||
|
||||
Fix:
|
||||
- Move secrets to a secret store / environment variables.
|
||||
- Redact sensitive fields in logs.
|
||||
- Add secret scanning to CI and pre-commit.
|
||||
|
||||
---
|
||||
|
||||
### GO-HTTP-001: HTTP servers MUST set timeouts and MaxHeaderBytes
|
||||
Severity: High (DoS risk)
|
||||
|
||||
Required:
|
||||
- MUST set: `ReadHeaderTimeout`, and SHOULD set `ReadTimeout`, `WriteTimeout`, `IdleTimeout` as appropriate for the service.
|
||||
- MUST set `MaxHeaderBytes` to a justified limit for your application.
|
||||
- MUST NOT rely on default zero-values for timeouts in production for internet-facing servers.
|
||||
|
||||
Insecure patterns:
|
||||
- `http.ListenAndServe(":8080", handler)` with a default `http.Server` (no explicit timeouts).
|
||||
- `&http.Server{}` with timeouts left at zero.
|
||||
- Missing `MaxHeaderBytes`.
|
||||
|
||||
Detection hints:
|
||||
- Search for `http.ListenAndServe(`, `ListenAndServeTLS(`, `Server{` and inspect configured fields.
|
||||
- Check for reverse proxies; even with a proxy, app-level timeouts still matter.
|
||||
|
||||
Fix:
|
||||
- Use `http.Server{ReadHeaderTimeout: ..., ReadTimeout: ..., WriteTimeout: ..., IdleTimeout: ..., MaxHeaderBytes: ...}`.
|
||||
- Calibrate timeouts per endpoint type (streaming vs JSON APIs).
|
||||
|
||||
Notes:
|
||||
- Net/http documents that these timeouts exist and that zero/negative values mean “no timeout”; production services should choose explicit values.
|
||||
|
||||
---
|
||||
|
||||
### GO-HTTP-002: Request body and multipart parsing MUST be size-bounded
|
||||
Severity: Medium (DoS risk; can be High for upload-heavy apps)
|
||||
|
||||
Required:
|
||||
- MUST enforce a global maximum request body size for endpoints that accept bodies.
|
||||
- MUST enforce strict multipart upload limits and avoid unbounded form parsing.
|
||||
- SHOULD enforce per-route limits when some endpoints legitimately need larger bodies.
|
||||
- SHOULD set upstream (proxy) limits as defense-in-depth.
|
||||
|
||||
Insecure patterns:
|
||||
- Reading `r.Body` with `io.ReadAll(r.Body)` without a size cap.
|
||||
- Calling `r.ParseMultipartForm(...)` with overly large limits (or forgetting size controls).
|
||||
- Accepting file uploads with no limits on file size, number of parts, or total body size.
|
||||
|
||||
Detection hints:
|
||||
- Search for `io.ReadAll(r.Body)`, `json.NewDecoder(r.Body)`, `ParseMultipartForm`, `FormFile`, `multipart`.
|
||||
- Look for missing `http.MaxBytesReader` or equivalent per-handler limiting.
|
||||
- Look for “upload” endpoints and check limits.
|
||||
|
||||
Fix:
|
||||
- Wrap request bodies with `http.MaxBytesReader(w, r.Body, maxBytes)` before parsing.
|
||||
- For multipart, set conservative limits and validate file sizes/part counts explicitly.
|
||||
- Set proxy limits (e.g., at ingress) in addition to app limits.
|
||||
|
||||
Notes:
|
||||
- There are known vulnerability classes and advisories related to excessive resource consumption in multipart/form parsing; treat unbounded parsing as a security issue.
|
||||
|
||||
---
|
||||
|
||||
### GO-DEPLOY-002: Diagnostic endpoints (pprof/expvar/metrics) MUST NOT be publicly exposed
|
||||
Severity: High
|
||||
|
||||
NOTE: This only applies to production configurations. These endpoints are often used for debug or dev endpoints. If found, confirm that it would be reachable from the actual production deployment.
|
||||
|
||||
Required:
|
||||
- MUST NOT expose `net/http/pprof` handlers on a public internet-facing listener without strong access controls.
|
||||
- SHOULD run diagnostics on a separate, internal-only listener (loopback/VPC-only) and require auth.
|
||||
- MUST review what diagnostic endpoints reveal (stack traces, memory, command lines, environment, internal URLs).
|
||||
|
||||
Insecure patterns:
|
||||
- Side-effect import `import _ "net/http/pprof"` in a server binary with a public mux.
|
||||
- `/debug/pprof/*` reachable without auth.
|
||||
- `/debug/vars` (expvar) reachable without auth.
|
||||
|
||||
Detection hints:
|
||||
- Search for `net/http/pprof` imports (including blank imports).
|
||||
- Search for route prefixes `/debug/pprof`, `/debug/vars`.
|
||||
- Check whether `http.DefaultServeMux` is used and whether any debug handlers register globally.
|
||||
|
||||
Fix:
|
||||
- Remove diagnostics from production builds, or bind them to an internal-only listener.
|
||||
- Add strong authentication/authorization (and ideally network-level restrictions).
|
||||
|
||||
Notes:
|
||||
- pprof is typically imported for its side effect of registering HTTP handlers under `/debug/pprof/`.
|
||||
|
||||
---
|
||||
|
||||
### GO-HTTP-003: Reverse proxy and forwarded header trust MUST be explicit
|
||||
Severity: High (auth, URL generation, logging/auditing correctness)
|
||||
|
||||
Required:
|
||||
- If behind a reverse proxy, MUST define which proxy is trusted and how client IP/scheme/host are derived.
|
||||
- MUST NOT trust `X-Forwarded-For`, `X-Forwarded-Proto`, `Forwarded`, or similar headers from the open internet.
|
||||
- MUST ensure “secure cookie” logic, redirects, and absolute URL generation do not rely on spoofable headers.
|
||||
|
||||
Insecure patterns:
|
||||
- Using `r.Header.Get("X-Forwarded-For")` as the client IP without validating the proxy boundary.
|
||||
- Deriving “is HTTPS” from `X-Forwarded-Proto` without confirming it came from a trusted proxy.
|
||||
- Using forwarded `Host` values for password reset links without allowlisting.
|
||||
|
||||
Detection hints:
|
||||
- Search for `X-Forwarded-For`, `X-Forwarded-Proto`, `Forwarded`, `Real-IP`, and any custom “client IP” helpers.
|
||||
- Inspect ingress/proxy configs; if not visible, mark as “verify at edge”.
|
||||
|
||||
Fix:
|
||||
- Enforce proxy trust at the edge and in app:
|
||||
- Accept forwarded headers only from known proxy IP ranges.
|
||||
- Prefer platform-provided mechanisms where available.
|
||||
- If generating external links, use a configured allowlisted canonical origin (not the request’s Host header).
|
||||
|
||||
---
|
||||
|
||||
### GO-HTTP-004: Security headers SHOULD be set (in app or at the edge)
|
||||
Severity: Medium
|
||||
|
||||
Required (typical web app serving browsers):
|
||||
- SHOULD set:
|
||||
- `Content-Security-Policy` (CSP) appropriate to the app. NOTE: It is most important to set the CSP's script-src. All other directives are not as important and can generally be excluded for the ease of development.
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
- Clickjacking protection (`X-Frame-Options` and/or CSP `frame-ancestors`)
|
||||
- `Referrer-Policy` and `Permissions-Policy` where appropriate
|
||||
- MUST ensure cookies have secure attributes (see GO-HTTP-005).
|
||||
|
||||
NOTE:
|
||||
- These headers may be set via reverse proxy/CDN; if not visible in app code, report as “verify at edge”.
|
||||
|
||||
Insecure patterns:
|
||||
- No security headers anywhere (app or edge) for a browser-facing app.
|
||||
- CSP missing for apps rendering untrusted content.
|
||||
|
||||
Detection hints:
|
||||
- Search for middleware setting headers: `w.Header().Set("Content-Security-Policy", ...)`, etc.
|
||||
- Search for reverse proxy config that sets headers.
|
||||
|
||||
Fix:
|
||||
- Add centralized header middleware in Go, or configure at the edge.
|
||||
- Keep CSP realistic; avoid `unsafe-inline` where possible.
|
||||
|
||||
---
|
||||
|
||||
### GO-HTTP-005: Cookies MUST use secure attributes in production
|
||||
Severity: Medium
|
||||
|
||||
Required (production, HTTPS):
|
||||
- MUST set `Secure` on cookies that carry auth/session state. IMPORTANT NOTE: Only set `Secure` in production environment when TLS is configured. When running in a local dev environment over HTTP, do not set `Secure` property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like `SESSION_COOKIE_SECURE` which can be used to disable `Secure` cookies when testing over HTTP.
|
||||
- MUST set `HttpOnly` on auth/session cookies.
|
||||
- SHOULD set `SameSite=Lax` by default (or `Strict` if compatible), and only use `None` when necessary (and only with `Secure`).
|
||||
- SHOULD set bounded lifetimes (`Max-Age`/`Expires`) appropriate to the app.
|
||||
|
||||
Insecure patterns:
|
||||
- Setting auth/session cookies without `Secure` in HTTPS deployments.
|
||||
- Cookies without `HttpOnly` for session identifiers.
|
||||
- `SameSite=None` for cookie-authenticated apps without a strong CSRF strategy.
|
||||
|
||||
Detection hints:
|
||||
- Search for `http.SetCookie`, `&http.Cookie{`, `Set-Cookie`.
|
||||
- Inspect cookie flags in auth/session code.
|
||||
|
||||
Fix:
|
||||
- Set the correct fields on `http.Cookie` and centralize cookie creation.
|
||||
|
||||
Notes:
|
||||
- SameSite is defense-in-depth and does not replace CSRF protections for cookie-auth apps.
|
||||
|
||||
---
|
||||
|
||||
### GO-HTTP-006: Cookie-authenticated state-changing endpoints MUST be CSRF-protected
|
||||
Severity: High
|
||||
|
||||
- IMPORTANT NOTE: If cookies are not used for auth (e.g., pure bearer token in Authorization header with no ambient cookies), CSRF is not a risk for those endpoints.
|
||||
|
||||
Required:
|
||||
- MUST protect all state-changing endpoints (POST/PUT/PATCH/DELETE) that rely on cookies for authentication.
|
||||
- SHOULD use a well-tested CSRF library/middleware rather than rolling your own.
|
||||
- MAY use additional defenses (Origin/Referer checks, Fetch Metadata, SameSite cookies), but tokens remain the primary defense for cookie-authenticated apps.
|
||||
If tokens are impractical, or for small applications:
|
||||
* MUST at a minimum require a custom header to be set and set the session cookie SESSION_COOKIE_SAMESITE=lax, as this is the strongest method besides requiring a form token, and may be much easier to implement.
|
||||
|
||||
|
||||
Insecure patterns:
|
||||
- Cookie-authenticated JSON endpoints that mutate state with no CSRF checks.
|
||||
- Using GET for state-changing actions.
|
||||
|
||||
Detection hints:
|
||||
- Enumerate all non-GET routes and identify auth mechanism.
|
||||
- Look for CSRF middleware usage; if absent, treat as suspicious in browser-facing apps.
|
||||
|
||||
Fix:
|
||||
- Add CSRF middleware and ensure it covers all state-changing routes.
|
||||
- If the service is an API intended for non-browser clients, avoid cookie auth; use Authorization headers.
|
||||
|
||||
---
|
||||
|
||||
### GO-HTTP-007: CORS must be explicit and least-privilege
|
||||
Severity: Medium (High if misconfigured with credentials)
|
||||
|
||||
Required:
|
||||
- If CORS is not needed, MUST keep it disabled.
|
||||
- If CORS is needed:
|
||||
- MUST allowlist trusted origins (do not reflect arbitrary origins)
|
||||
- MUST be careful with credentialed requests; do not combine broad origins with cookies
|
||||
- SHOULD restrict allowed methods/headers
|
||||
|
||||
Insecure patterns:
|
||||
- `Access-Control-Allow-Origin: *` paired with cookies (`Access-Control-Allow-Credentials: true`).
|
||||
- Reflecting `Origin` without validation.
|
||||
|
||||
Detection hints:
|
||||
- Search for `Access-Control-Allow-` header setting.
|
||||
- Search for CORS middleware configuration.
|
||||
|
||||
Fix:
|
||||
- Implement strict origin allowlists and minimal methods/headers.
|
||||
- Ensure cookie-auth endpoints are not exposed cross-origin unless required.
|
||||
|
||||
---
|
||||
|
||||
### GO-XSS-001: Use html/template and avoid bypassing auto-escaping with untrusted data
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
- MUST use `html/template` for HTML rendering (not `text/template`).
|
||||
- MUST NOT convert untrusted data into “trusted” template types (`template.HTML`, `template.JS`, `template.URL`, etc.).
|
||||
- SHOULD keep templates static and controlled by developers; treat dynamic templates as high risk.
|
||||
- MUST NOT serve user-uploaded HTML/JS as active content unless explicitly intended and safely sandboxed.
|
||||
|
||||
Insecure patterns:
|
||||
- `text/template` used to generate HTML.
|
||||
- Using `template.HTML(userInput)` or similar typed wrappers.
|
||||
- Directly writing unescaped user content into HTML responses.
|
||||
|
||||
Detection hints:
|
||||
- Search for `text/template`, `template.New(...).Parse(...)`, and typed wrappers like `template.HTML(`.
|
||||
- Inspect handlers that return HTML with string concatenation.
|
||||
|
||||
Fix:
|
||||
- Use `html/template` and pass untrusted data as data, not markup.
|
||||
- If you must allow limited HTML, use a vetted HTML sanitizer and still be careful with attributes/URLs.
|
||||
|
||||
---
|
||||
|
||||
### GO-SSTI-001: Never parse/execute templates from untrusted input (SSTI)
|
||||
Severity: Critical
|
||||
|
||||
Required:
|
||||
- MUST NOT call `template.Parse` / `template.ParseFiles` / `template.New(...).Parse(...)` on template text influenced by untrusted input.
|
||||
- MUST treat “user-defined templates” as a special high-risk design:
|
||||
- MUST use heavy sandboxing and strict allowlists
|
||||
- MUST isolate execution (process/container boundary) if truly required
|
||||
|
||||
Insecure patterns:
|
||||
- `tmpl := template.Must(template.New("x").Parse(r.FormValue("tmpl")))`
|
||||
- Reading templates from uploads / DB entries and executing them in the same trust domain as server code.
|
||||
|
||||
Detection hints:
|
||||
- Search for `.Parse(` and trace the origin of the template string.
|
||||
- Look for “custom email templates”, “user theming templates”, etc.
|
||||
|
||||
Fix:
|
||||
- Replace with safe substitution mechanisms (no code execution).
|
||||
- If templates must be user-controlled, isolate and sandbox aggressively.
|
||||
|
||||
---
|
||||
|
||||
### GO-PATH-001: Prevent path traversal and unsafe file serving
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
- MUST NOT pass user-controlled paths to `os.Open`, `os.ReadFile`, `http.ServeFile`, or `http.FileServer` without strict validation and base-dir enforcement.
|
||||
- MUST treat `..`, absolute paths, and OS-specific path tricks as hostile input.
|
||||
- SHOULD store user uploads outside any static web root; serve through controlled handlers.
|
||||
- MUST avoid directory listing for sensitive file trees.
|
||||
|
||||
Insecure patterns:
|
||||
- `http.ServeFile(w, r, r.URL.Query().Get("path"))`
|
||||
- `os.Open(filepath.Join(baseDir, userPath))` without checking that the result stays under `baseDir`
|
||||
- `http.FileServer(http.Dir("."))` serving the project root or user-writable directories
|
||||
|
||||
Detection hints:
|
||||
- Search for `ServeFile(`, `FileServer(`, `http.Dir(`, `os.Open(`, `ReadFile(`, `filepath.Join(`.
|
||||
- Trace whether path components come from request/DB.
|
||||
|
||||
Fix:
|
||||
- Use an allowlist of file identifiers (e.g., database IDs) mapped to server-side paths.
|
||||
- Enforce base directory containment after cleaning and joining.
|
||||
- Serve active formats as downloads (`Content-Disposition: attachment`) unless explicitly intended.
|
||||
|
||||
---
|
||||
|
||||
### GO-UPLOAD-001: File uploads must be validated, stored safely, and served safely
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
- MUST enforce upload size limits (app + edge).
|
||||
- MUST validate file type using allowlists and content checks (not only extensions).
|
||||
- MUST store uploads outside executable/static roots when possible.
|
||||
- SHOULD generate server-side filenames (random IDs) and avoid trusting original names.
|
||||
- MUST serve potentially active formats safely (download attachment) unless explicitly intended.
|
||||
|
||||
Insecure patterns:
|
||||
- Accepting arbitrary file types and serving them back inline.
|
||||
- Using user-supplied filename as storage path.
|
||||
- Missing size/type validation.
|
||||
|
||||
Detection hints:
|
||||
- Search for `multipart`, `FormFile`, `ParseMultipartForm`, `io.Copy` to disk.
|
||||
- Check where files are stored and how they are served.
|
||||
|
||||
Fix:
|
||||
- Implement allowlist validation + safe storage + safe serving.
|
||||
- Add scanning/quarantine workflows where applicable.
|
||||
|
||||
---
|
||||
|
||||
### GO-INJECT-001: Prevent SQL injection (parameterized queries / ORM)
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
- MUST use parameterized queries or an ORM that parameterizes under the hood.
|
||||
- MUST NOT build SQL by string concatenation / `fmt.Sprintf` / string interpolation with untrusted input.
|
||||
|
||||
Insecure patterns:
|
||||
- `fmt.Sprintf("SELECT ... WHERE id=%s", r.URL.Query().Get("id"))`
|
||||
- `query := "UPDATE users SET role='" + role + "' WHERE id=" + id`
|
||||
|
||||
Detection hints:
|
||||
- Grep for `SELECT`, `INSERT`, `UPDATE`, `DELETE` and check how query strings are built.
|
||||
- Trace untrusted data into `db.Query`, `db.Exec`, `QueryRow`, etc.
|
||||
|
||||
Fix:
|
||||
- Replace with placeholders (`?`, `$1`, etc.) and pass parameters separately.
|
||||
- Validate and type-check IDs before use.
|
||||
|
||||
---
|
||||
|
||||
### GO-INJECT-002: Prevent OS command injection; avoid shelling out with untrusted input
|
||||
Severity: Critical to High (depends on exposure)
|
||||
|
||||
Required:
|
||||
- MUST avoid executing external commands with attacker-controlled strings.
|
||||
- If subprocess is necessary:
|
||||
- MUST use `exec.CommandContext` with an argument list (not `sh -c`).
|
||||
- MUST NOT pass untrusted input to a shell (`bash -c`, `sh -c`, PowerShell).
|
||||
- SHOULD use strict allowlists for any variable component (subcommand, flags, filenames).
|
||||
- MUST assume CLI tools may interpret attacker-controlled args as flags or special values.
|
||||
|
||||
Insecure patterns:
|
||||
- `exec.Command("sh", "-c", userString)`
|
||||
- `exec.Command("bash", "-c", fmt.Sprintf("tool %s", user))`
|
||||
- Calling the shell to get glob expansion for user-supplied globs.
|
||||
|
||||
Detection hints:
|
||||
- Search for `os/exec`, `exec.Command(`, `CommandContext(`, `"sh"`, `"bash"`, `"-c"`.
|
||||
- Trace untrusted input into command name/args.
|
||||
|
||||
Fix:
|
||||
- Use library APIs instead of subprocesses.
|
||||
- Hardcode command and allowlist/validate args.
|
||||
- If a shell is unavoidable, escape robustly and treat as high risk (prefer avoiding).
|
||||
|
||||
Notes:
|
||||
- The Go `os/exec` package intentionally does invoke a shell; introducing `sh -c` reintroduces shell injection hazards.
|
||||
|
||||
---
|
||||
|
||||
### GO-SSRF-001: Prevent SSRF in outbound HTTP requests
|
||||
Severity: Medium (High in cloud/LAN environments)
|
||||
|
||||
- Note: For small stand alone projects this is less important. It is most important when deploying into an LAN or with other services listening on the same server.
|
||||
|
||||
Required:
|
||||
- MUST treat outbound requests to user-provided URLs as high risk.
|
||||
- SHOULD allowlist hosts/domains for any user-influenced URL fetch.
|
||||
- SHOULD block access to localhost/private IP ranges/link-local addresses and cloud metadata endpoints.
|
||||
- MUST restrict schemes to `http`/`https` (no `file:`, `gopher:`, etc.).
|
||||
- MUST set client timeouts and restrict redirects.
|
||||
|
||||
Insecure patterns:
|
||||
- `http.Get(r.URL.Query().Get("url"))`
|
||||
- “URL preview” / “webhook test” endpoints that fetch arbitrary URLs.
|
||||
|
||||
Detection hints:
|
||||
- Search for `http.Get`, `client.Do`, and URL values derived from requests/DB.
|
||||
- Identify features that fetch remote resources.
|
||||
|
||||
Fix:
|
||||
- Parse URLs strictly; enforce scheme and allowlisted hostnames.
|
||||
- Resolve DNS and enforce IP-range restrictions (with care for DNS rebinding).
|
||||
- Set timeouts, disable redirects unless needed, and cap response sizes.
|
||||
|
||||
---
|
||||
|
||||
### GO-HTTPCLIENT-001: Outbound HTTP clients MUST set timeouts and close bodies
|
||||
Severity: High (DoS and resource exhaustion)
|
||||
|
||||
Required:
|
||||
- MUST set an overall timeout on `http.Client` usage (or equivalent per-request deadlines via context + transport timeouts).
|
||||
- MUST ensure `resp.Body.Close()` is called for all successful requests (typically `defer resp.Body.Close()` immediately after error check).
|
||||
- SHOULD limit response body reads (do not `io.ReadAll` unbounded responses).
|
||||
- SHOULD restrict redirects for security-sensitive fetches (SSRF, auth flows).
|
||||
|
||||
Insecure patterns:
|
||||
- Using `http.DefaultClient` / `http.Get` for user-influenced destinations with no timeout policy.
|
||||
- Missing `defer resp.Body.Close()` leading to resource leaks.
|
||||
- `io.ReadAll(resp.Body)` with no limit.
|
||||
|
||||
Detection hints:
|
||||
- Search for `http.Get(`, `http.Post(`, `client := &http.Client{}` without `Timeout`, `client.Do(` and missing closes.
|
||||
- Search for `io.ReadAll(resp.Body)`.
|
||||
|
||||
Fix:
|
||||
- Use a configured client with timeouts.
|
||||
- Always close response bodies.
|
||||
- Use bounded readers (`io.LimitReader`) for large/untrusted responses.
|
||||
|
||||
Notes:
|
||||
- The net/http package exposes `DefaultClient` as a zero-valued `http.Client`, which can easily lead to “no timeout” behavior unless configured.
|
||||
|
||||
---
|
||||
|
||||
### GO-REDIRECT-001: Prevent open redirects
|
||||
Severity: Medium (can be High with auth flows)
|
||||
|
||||
Required:
|
||||
- MUST validate redirect targets derived from untrusted input (`next`, `redirect`, `return_to`).
|
||||
- SHOULD prefer only same-site relative paths.
|
||||
- SHOULD fall back to a safe default on validation failure.
|
||||
|
||||
Insecure patterns:
|
||||
- `http.Redirect(w, r, r.URL.Query().Get("next"), http.StatusFound)` with no validation.
|
||||
|
||||
Detection hints:
|
||||
- Search for `http.Redirect(` and check origin of the location.
|
||||
|
||||
Fix:
|
||||
- Allowlist internal paths or known domains.
|
||||
- Reject absolute URLs unless explicitly needed and allowlisted.
|
||||
|
||||
---
|
||||
|
||||
### GO-CRYPTO-001: Cryptographic randomness MUST come from crypto/rand
|
||||
Severity: High (Critical if used for auth/session tokens or keys)
|
||||
|
||||
Required:
|
||||
- MUST use `crypto/rand` for:
|
||||
- session IDs, password reset tokens, API keys, CSRF tokens, nonces
|
||||
- encryption keys, signing keys, salts when required
|
||||
- MUST NOT use `math/rand` for any security-sensitive value.
|
||||
- SHOULD use built-in helpers that produce appropriately strong tokens when available.
|
||||
|
||||
Insecure patterns:
|
||||
- `math/rand.Seed(time.Now().UnixNano())` followed by token generation for auth or sessions.
|
||||
- Using UUIDv4-like constructs built from `math/rand`.
|
||||
|
||||
Detection hints:
|
||||
- Search for `math/rand`, `rand.Seed`, `rand.Intn` in code that touches auth/session/token flows.
|
||||
- Search for custom token generators.
|
||||
|
||||
Fix:
|
||||
- Switch to `crypto/rand` (`rand.Reader`, `rand.Read`, or secure token helpers).
|
||||
- Ensure sufficient entropy and use URL-safe encoding.
|
||||
|
||||
Notes:
|
||||
- The crypto/rand package provides secure randomness APIs and token generation helpers.
|
||||
|
||||
---
|
||||
|
||||
### GO-AUTH-001: Password storage MUST use adaptive hashing (bcrypt/argon2id) and safe comparisons
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
- MUST hash passwords using an adaptive password hashing function (bcrypt or argon2id).
|
||||
- MUST NOT store plaintext passwords or reversible encryption of passwords.
|
||||
- MUST compare secrets in constant time when relevant (tokens, MACs, API keys) to reduce timing leaks.
|
||||
- SHOULD ensure password policies do not exceed algorithm constraints (e.g., bcrypt has input length limits; handle long passphrases appropriately).
|
||||
|
||||
Insecure patterns:
|
||||
- `sha256(password)` stored as password hash.
|
||||
- Plaintext password storage.
|
||||
- Comparing secrets with `==` in timing-sensitive contexts.
|
||||
|
||||
Detection hints:
|
||||
- Search for `sha1`, `sha256`, `md5` used on passwords.
|
||||
- Search for `bcrypt`/`argon2` usage; if absent, suspect.
|
||||
- Search for `==` comparisons on tokens/API keys.
|
||||
|
||||
Fix:
|
||||
- Use `bcrypt.GenerateFromPassword` / `CompareHashAndPassword` or argon2id with recommended parameters.
|
||||
- Use constant-time compare helpers when comparing MACs/tokens.
|
||||
|
||||
Notes:
|
||||
- Go provides bcrypt in `golang.org/x/crypto/bcrypt`, and constant-time comparisons in `crypto/subtle`.
|
||||
|
||||
---
|
||||
|
||||
### GO-CONC-001: Data races and concurrency hazards MUST be treated as security-relevant
|
||||
Severity: Medium to High (depends on what races affect)
|
||||
|
||||
Required:
|
||||
- MUST run tests with the race detector (`go test -race`) in CI for security-sensitive services.
|
||||
- MUST fix detected races; do not suppress without deep justification.
|
||||
- SHOULD treat shared mutable state in handlers as high risk; enforce synchronization or avoid shared mutability.
|
||||
|
||||
Insecure patterns:
|
||||
- Global maps/slices mutated from multiple goroutines without a mutex.
|
||||
- Caches or auth/session state stored in globals without concurrency protection.
|
||||
- Racy access to authorization state (can lead to bypasses or inconsistent enforcement).
|
||||
|
||||
Detection hints:
|
||||
- Search for `var someMap = map[...]...` used in handlers.
|
||||
- Look for missing `sync.Mutex`, `sync.Map`, channels, or other synchronization.
|
||||
- Ensure CI includes `-race` and that it runs relevant tests.
|
||||
|
||||
Fix:
|
||||
- Add proper synchronization or redesign to avoid shared mutable state.
|
||||
- Add race tests and run them continuously.
|
||||
|
||||
Notes:
|
||||
- The Go race detector only finds races that occur in executed code paths; improve test coverage and run realistic workloads with `-race` where feasible.
|
||||
|
||||
---
|
||||
|
||||
### GO-UNSAFE-001: Use of unsafe/cgo MUST be minimized and audited like memory-unsafe code
|
||||
Severity: High (Critical in high-risk code paths)
|
||||
|
||||
Required:
|
||||
- SHOULD avoid importing `unsafe` in application code unless absolutely necessary.
|
||||
- If `unsafe` is used, MUST treat it as “manual memory safety” requiring careful review and test coverage.
|
||||
- If `cgo` is used, MUST treat the C/C++ boundary as memory-unsafe; apply secure coding practices on the C side and isolate where possible.
|
||||
|
||||
Insecure patterns:
|
||||
- Widespread `unsafe.Pointer` casts in parsing, serialization, auth, or network code.
|
||||
- `cgo` used for parsing or security boundaries without sandboxing.
|
||||
|
||||
Detection hints:
|
||||
- Search for `import "unsafe"`, `unsafe.Pointer`, `// #cgo`, `import "C"`.
|
||||
- Prioritize review where unsafe touches untrusted input.
|
||||
|
||||
Fix:
|
||||
- Replace unsafe/cgo usage with safe standard library alternatives where possible.
|
||||
- Isolate unsafe code in small, well-tested modules with fuzz/race tests.
|
||||
|
||||
Notes:
|
||||
- The unsafe package explicitly provides operations that step around Go’s type safety guarantees.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 5) Practical scanning heuristics (how to “hunt”)
|
||||
|
||||
When actively scanning, use these high-signal patterns:
|
||||
|
||||
Toolchain & dependencies:
|
||||
- `FROM golang:` (Dockerfiles), `go-version:` (CI), `toolchain go` (go.mod), pinned old versions
|
||||
- `GOSUMDB=off`, `GOINSECURE`, `GONOSUMDB`, `GOPROXY=direct`
|
||||
- `replace` directives in `go.mod` to forks/paths
|
||||
- `govulncheck` missing in CI
|
||||
|
||||
HTTP server hardening:
|
||||
- `http.ListenAndServe(`, `ListenAndServeTLS(`, `&http.Server{` with missing timeouts
|
||||
- `ReadHeaderTimeout: 0`, `ReadTimeout: 0`, `WriteTimeout: 0`, `IdleTimeout: 0`, missing `MaxHeaderBytes`
|
||||
|
||||
Body parsing / DoS:
|
||||
- `io.ReadAll(r.Body)`, `json.NewDecoder(r.Body)` without size cap
|
||||
- `ParseMultipartForm`, `FormFile`, `multipart.NewReader` without explicit limits
|
||||
- Missing `http.MaxBytesReader`
|
||||
|
||||
Debug exposure:
|
||||
- `import _ "net/http/pprof"`
|
||||
- `/debug/pprof`, `/debug/vars`
|
||||
|
||||
Templates / XSS / SSTI:
|
||||
- `text/template` used for HTML output
|
||||
- `template.HTML(`, `template.JS(`, `template.URL(` with user-controlled data
|
||||
- `.Parse(` on user-controlled strings
|
||||
|
||||
Files:
|
||||
- `http.ServeFile(` with user path
|
||||
- `http.FileServer(http.Dir(` pointing at repo root or uploads
|
||||
- `os.Open(filepath.Join(base, user))` without containment checks
|
||||
|
||||
Injection:
|
||||
- SQL building with `fmt.Sprintf`, string concatenation near `db.Query/Exec`
|
||||
- `exec.Command("sh","-c", ...)`, `exec.Command("bash","-c", ...)`
|
||||
|
||||
SSRF / outbound HTTP:
|
||||
- `http.Get(userURL)`, `client.Do(req)` where URL comes from request/DB
|
||||
- Missing client timeout, missing `resp.Body.Close()`, unbounded `io.ReadAll(resp.Body)`
|
||||
|
||||
Crypto:
|
||||
- `math/rand` in token/session generation
|
||||
- `InsecureSkipVerify: true`
|
||||
- Password hashing with `sha256`/`md5` instead of bcrypt/argon2
|
||||
|
||||
Concurrency:
|
||||
- Shared maps/slices mutated from handlers without locks
|
||||
- CI lacking `go test -race`
|
||||
|
||||
Always try to confirm:
|
||||
- data origin (untrusted vs trusted)
|
||||
- sink type (template/SQL/subprocess/files/http)
|
||||
- protective controls present (limits, validation, allowlists, middleware, network controls)
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 6) Sources (accessed 2026-01-28)
|
||||
|
||||
Primary Go documentation:
|
||||
- Go Security Policy — https://go.dev/doc/security/policy
|
||||
- Go Release History (security fixes in patch releases) — https://go.dev/doc/devel/release
|
||||
- Go 1.25 Release Notes — https://go.dev/doc/go1.25
|
||||
- net/http (server timeouts, MaxHeaderBytes, DefaultClient) — https://pkg.go.dev/net/http
|
||||
- html/template (auto-escaping and trusted-template assumptions) — https://pkg.go.dev/html/template
|
||||
- crypto/tls (MinVersion defaults, InsecureSkipVerify warnings) — https://pkg.go.dev/crypto/tls
|
||||
- crypto/rand (secure randomness, token helpers) — https://pkg.go.dev/crypto/rand
|
||||
- crypto/subtle (constant-time comparisons) — https://pkg.go.dev/crypto/subtle
|
||||
- os/exec (no shell by default; command execution guidance) — https://pkg.go.dev/os/exec
|
||||
- unsafe (bypasses type safety) — https://go.dev/src/unsafe/unsafe.go
|
||||
- net/http/pprof (debug endpoints) — https://pkg.go.dev/net/http/pprof
|
||||
- cmd/go (module authentication via go.sum/checksum DB; env vars like GOINSECURE) — https://pkg.go.dev/cmd/go
|
||||
- Module Mirror and Checksum Database Launched (Go blog) — https://go.dev/blog/module-mirror-launch
|
||||
- govulncheck documentation — https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck
|
||||
- Go Race Detector documentation — https://go.dev/doc/articles/race_detector
|
||||
- bcrypt (password hashing) — https://pkg.go.dev/golang.org/x/crypto/bcrypt
|
||||
- Go vulnerability entry example (multipart resource consumption) — https://pkg.go.dev/vuln/GO-2023-1569
|
||||
|
||||
OWASP Cheat Sheet Series (general web security):
|
||||
- Session Management — https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
|
||||
- CSRF Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
|
||||
- SSRF Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
|
||||
- XSS Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
|
||||
- HTTP Security Response Headers — https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,747 @@
|
||||
# Frontend JavaScript/TypeScript Web Security Spec (Vanilla Browser JS/TS, Modern Browsers)
|
||||
|
||||
This document is designed as a **security spec** that supports:
|
||||
|
||||
1. **Secure-by-default code generation** for new frontend JavaScript/TypeScript (no specific framework assumed).
|
||||
2. **Security review / vulnerability hunting** in existing frontend code (passive “notice issues while working” and active “scan the repo and report findings”).
|
||||
|
||||
It is intentionally written as a set of **normative requirements** (“MUST/SHOULD/MAY”) plus **audit rules** (what bad patterns look like, how to detect them, and how to fix/mitigate them).
|
||||
|
||||
---
|
||||
|
||||
## 0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)
|
||||
|
||||
* MUST NOT request, output, log, hard-code, or commit secrets (API keys intended to be secret, private keys, passwords, OAuth refresh tokens, session tokens, cookies).
|
||||
Notes:
|
||||
|
||||
* Frontend code is inherently observable by end users. If a value must remain secret, it must not be in browser-delivered code.
|
||||
* If the project uses “public” keys (e.g., publishable analytics keys), they MUST be treated as non-secret and scoped accordingly.
|
||||
|
||||
* MUST NOT “fix” security by disabling protections (e.g., weakening CSP with `unsafe-inline`/`unsafe-eval` without justification, removing origin checks for `postMessage`, switching to `innerHTML` for convenience, accepting arbitrary redirects/URLs, or turning off sanitization).
|
||||
|
||||
* MUST provide **evidence-based findings** during audits: cite file paths, code snippets, and relevant HTML/CSP/config values that justify the claim.
|
||||
|
||||
* MUST treat uncertainty honestly:
|
||||
|
||||
* Security headers (CSP, frame-ancestors, etc.) might be set by server/edge/CDN rather than in repo code. If not visible, report as “not visible here; verify at runtime/edge config.” (Also note that `<meta http-equiv=...>` only simulates a subset of headers; don’t assume other security headers exist just because a meta tag exists.) ([MDN Web Docs][1])
|
||||
|
||||
---
|
||||
|
||||
## 1) Operating modes
|
||||
|
||||
### 1.1 Generation mode (default)
|
||||
|
||||
When asked to write new frontend JS/TS code or modify existing code:
|
||||
|
||||
* MUST follow every **MUST** requirement in this spec.
|
||||
* SHOULD follow every **SHOULD** requirement unless the user explicitly says otherwise.
|
||||
* MUST prefer safe-by-default browser APIs and proven libraries over custom security code (especially for HTML sanitization).
|
||||
* MUST avoid introducing new risky sinks (DOM XSS injection sinks like `innerHTML`, navigation to `javascript:` URLs, dynamic code execution via `eval`/`Function`, unsafe `postMessage`, unsafe third-party script loading, etc.). ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
### 1.2 Passive review mode (always on while editing)
|
||||
|
||||
While working anywhere in a frontend repo (even if the user did not ask for a security scan):
|
||||
|
||||
* MUST “notice” violations of this spec in touched/nearby code.
|
||||
* SHOULD mention issues as they come up, with a brief explanation + safe fix.
|
||||
|
||||
### 1.3 Active audit mode (explicit scan request)
|
||||
|
||||
When the user asks to “scan”, “audit”, or “hunt for vulns”:
|
||||
|
||||
* MUST systematically search the codebase for violations of this spec.
|
||||
* MUST output findings in a structured format (see §2.3).
|
||||
|
||||
Recommended audit order:
|
||||
|
||||
1. HTML entrypoints (`index.html`, server-rendered templates), script/style includes, and any CSP delivery (header vs meta). ([W3C][3])
|
||||
2. DOM XSS sinks (`innerHTML`, `document.write`, `insertAdjacentHTML`, event-handler attributes) and their data sources (URL params/hash, storage, postMessage, API responses). ([OWASP Cheat Sheet Series][2])
|
||||
3. Navigation/redirect handling (`window.location*`, link targets, URL allowlists) including `javascript:` URL hazards. ([MDN Web Docs][4])
|
||||
4. Cross-origin communication (`postMessage`, iframe embed patterns, sandboxing). ([MDN Web Docs][5])
|
||||
5. Storage of sensitive data (localStorage/sessionStorage) and assumptions about trust. ([OWASP Cheat Sheet Series][6])
|
||||
6. Third-party scripts / tag managers / CDNs, and integrity controls (SRI) and policy controls (CSP). ([OWASP Cheat Sheet Series][7])
|
||||
7. DOM clobbering gadgets and unsafe reliance on `window`/`document` named properties. ([OWASP Cheat Sheet Series][8])
|
||||
|
||||
---
|
||||
|
||||
## 2) Definitions and review guidance
|
||||
|
||||
### 2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)
|
||||
|
||||
Examples include:
|
||||
|
||||
* URL-derived data: `location.href`, `location.search`, `location.hash`, `document.baseURI`, `new URLSearchParams(location.search)`, routing fragments. ([OWASP Cheat Sheet Series][2])
|
||||
* DOM content that may include user-controlled markup (comments, profiles, CMS content, markdown-to-HTML output, etc.), especially if inserted dynamically. ([OWASP Cheat Sheet Series][2])
|
||||
* `postMessage` event data (`event.data`) and metadata (`event.origin`) from other windows/frames. ([MDN Web Docs][5])
|
||||
* Browser storage: `localStorage`, `sessionStorage`, IndexedDB (contents can be attacker-influenced via XSS or local machine access; never treat as “trusted”). ([OWASP Cheat Sheet Series][6])
|
||||
* Any data returned from network calls (even if from “your API”), because it may contain stored attacker content that becomes dangerous only when inserted into the DOM. ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
### 2.2 Dangerous sink (DOM XSS / code execution sink)
|
||||
|
||||
A sink is any API/operation that can execute script or interpret attacker-controlled strings as HTML/JS/URL in a security-sensitive way. High-signal sinks include:
|
||||
|
||||
* HTML parsing / insertion: `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `document.write`, `document.writeln`. ([OWASP Cheat Sheet Series][2])
|
||||
* Dynamic code execution: `eval`, `new Function`, `setTimeout("...")`, `setInterval("...")`. ([MDN Web Docs][10])
|
||||
* Navigation to script-bearing URLs (e.g., `javascript:`) via setters like `Location.href`/`window.location` (and via link `href` if attacker-controlled). ([MDN Web Docs][4])
|
||||
* Setting event handler attributes from strings, e.g. `setAttribute("onclick", "...")`. ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
### 2.3 Required audit finding format
|
||||
|
||||
For each issue found, output:
|
||||
|
||||
* Rule ID:
|
||||
* Severity: Critical / High / Medium / Low
|
||||
* Location: file path + function/class/module + line(s)
|
||||
* Evidence: the exact code/config snippet
|
||||
* Impact: what could go wrong, who can exploit it
|
||||
* Fix: safe change (prefer minimal diff)
|
||||
* Mitigation: defense-in-depth if immediate fix is hard
|
||||
* False positive notes: what to verify if uncertain
|
||||
|
||||
---
|
||||
|
||||
## 3) Secure baseline: minimum production configuration (MUST in production)
|
||||
|
||||
This is the smallest baseline that prevents common frontend JS/TS security misconfigurations. Some items are “in repo” (HTML/JS) and some may live at the server/edge.
|
||||
|
||||
### 3.1 Content Security Policy (CSP) baseline (SHOULD; MUST for high-risk apps)
|
||||
|
||||
* SHOULD deliver CSP via HTTP response headers when possible.
|
||||
* MAY deliver CSP via an HTML `<meta http-equiv="Content-Security-Policy" ...>` tag when you cannot set headers (e.g., purely static hosting constraints). ([MDN Web Docs][1])
|
||||
* If using CSP via `<meta http-equiv>`, MUST understand the limitations:
|
||||
|
||||
* The policy only applies to content that follows the meta element (so it must appear very early, before any scripts/resources you want governed). ([W3C][3])
|
||||
* The following directives are **not supported** in a meta-delivered policy and will be ignored: `report-uri`, `frame-ancestors`, and `sandbox`. ([W3C][3])
|
||||
* “Report-only” CSP cannot be set via a meta element. ([W3C][3])
|
||||
|
||||
Practical baseline goals:
|
||||
|
||||
* Avoid script sources `unsafe-inline` and `unsafe-eval` (they significantly weaken CSP’s value against XSS). ([MDN Web Docs][10])
|
||||
* Prefer nonce- or hash-based script policies if you need inline scripts. ([MDN Web Docs][10])
|
||||
* Consider enabling Trusted Types enforcement where feasible. ([MDN Web Docs][11])
|
||||
|
||||
### 3.2 Third-party scripts baseline (SHOULD)
|
||||
|
||||
* SHOULD minimize third-party script execution and treat it as equivalent privilege to first-party JS (it runs with your origin’s privileges). ([OWASP Cheat Sheet Series][7])
|
||||
* SHOULD use Subresource Integrity (SRI) for third-party scripts/styles loaded from CDNs. ([MDN Web Docs][12])
|
||||
|
||||
### 3.3 Cross-window communication baseline (SHOULD)
|
||||
|
||||
* SHOULD restrict `postMessage` communications to explicit origins, and validate both origin and message shape. ([MDN Web Docs][5])
|
||||
|
||||
---
|
||||
|
||||
## 4) Rules (generation + audit)
|
||||
|
||||
Each rule contains: required practice, insecure patterns, detection hints, and remediation.
|
||||
|
||||
### JS-XSS-001: Do not inject untrusted HTML into the DOM (avoid `innerHTML` and friends)
|
||||
|
||||
Severity: Critical if you can prove attacker-controlled input can reach these APIs; otherwise Medium
|
||||
|
||||
|
||||
Required:
|
||||
|
||||
* MUST treat `innerHTML`, `outerHTML`, and `insertAdjacentHTML` as dangerous sinks when their input can contain untrusted data. ([OWASP Cheat Sheet Series][2])
|
||||
* MUST prefer safe DOM APIs that do not parse HTML:
|
||||
|
||||
* `textContent` for text. ([OWASP Cheat Sheet Series][2])
|
||||
* `document.createElement`, `appendChild`, `setAttribute` for non-event-handler attributes. ([OWASP Cheat Sheet Series][2])
|
||||
* If HTML insertion is truly required, SHOULD sanitize with a well-reviewed HTML sanitizer and strongly consider enforcing Trusted Types to confine usage to audited code paths. ([MDN Web Docs][11])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `el.innerHTML = userInput`
|
||||
* `el.insertAdjacentHTML('beforeend', userInput)`
|
||||
* `el.outerHTML = userInput`
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for: `.innerHTML`, `.outerHTML`, `insertAdjacentHTML(`.
|
||||
* Trace the origin of inserted string: URL params/hash, postMessage, storage, API responses, DOM attributes. ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
Fix:
|
||||
|
||||
* Replace with `textContent` for plain text. ([OWASP Cheat Sheet Series][2])
|
||||
* For structured UI, build DOM nodes explicitly.
|
||||
* For “rich text” requirements:
|
||||
|
||||
* Sanitize using an allowlist-based sanitizer.
|
||||
* Prefer returning safe “components” instead of arbitrary HTML strings.
|
||||
* Use Trusted Types enforcement to ensure only `TrustedHTML` reaches sinks where supported. ([MDN Web Docs][11])
|
||||
|
||||
Mitigation:
|
||||
|
||||
* Deploy a strict CSP and consider Trusted Types enforcement (`require-trusted-types-for 'script'`). ([MDN Web Docs][10])
|
||||
|
||||
False positive notes:
|
||||
|
||||
* If the string is provably constant or fully generated from trusted constants, it may be safe. Still prefer safer APIs.
|
||||
|
||||
---
|
||||
|
||||
### JS-XSS-002: Avoid `document.write` / `document.writeln` (XSS + document clobbering hazards)
|
||||
|
||||
Severity: Critical if you can prove attacker-controlled input can reach these APIs; otherwise Medium
|
||||
|
||||
Required:
|
||||
|
||||
* MUST avoid `document.write()` and `document.writeln()` in production code (they are XSS vectors and can be abused with crafted HTML even if some browsers block injected `<script>` in certain situations). ([MDN Web Docs][13])
|
||||
* If legacy use is unavoidable, MUST ensure no untrusted input reaches these APIs and SHOULD enforce Trusted Types (`TrustedHTML`) where supported. ([MDN Web Docs][14])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `document.write(userInput)`
|
||||
* `document.writeln(getParam('q'))`
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `document.write(`, `document.writeln(`. ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
Fix:
|
||||
|
||||
* Replace with DOM manipulation (`createElement`, `appendChild`) or safe text insertion (`textContent`). ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
Mitigation:
|
||||
|
||||
* Strict CSP + Trusted Types enforcement reduces blast radius if a sink remains. ([MDN Web Docs][10])
|
||||
|
||||
---
|
||||
|
||||
### JS-XSS-003: Do not use string-to-code execution (`eval`, `new Function`, string timeouts)
|
||||
|
||||
Severity: Critical if you can prove attacker-controlled input can reach these APIs; otherwise Medium
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT pass untrusted data to:
|
||||
|
||||
* `eval()`
|
||||
* `new Function(...)`
|
||||
* `setTimeout("...")` / `setInterval("...")` with string arguments ([MDN Web Docs][10])
|
||||
* SHOULD avoid these APIs entirely in modern frontend code; refactor to non-eval logic. ([MDN Web Docs][10])
|
||||
* MUST NOT “fix CSP breakage” by adding `unsafe-eval` unless there is a documented, reviewed justification and compensating controls. ([MDN Web Docs][10])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `eval(userInput)`
|
||||
* `new Function("return " + userInput)()`
|
||||
* `setTimeout(userInput, 0)` where userInput is a string
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `eval(`, `new Function`, `setTimeout("`, `setInterval("`.
|
||||
* Also search for construction of code strings used later.
|
||||
|
||||
Fix:
|
||||
|
||||
* Replace dynamic code with:
|
||||
|
||||
* structured data + explicit branching/handlers,
|
||||
* JSON parsing (`JSON.parse`) instead of `eval` for JSON. ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
Mitigation:
|
||||
|
||||
* CSP that blocks `eval()`-like APIs by default, and avoid `unsafe-eval`. ([MDN Web Docs][10])
|
||||
* Consider Trusted Types for controlled cases, but treat it as a hardening layer, not a license to keep eval patterns. ([MDN Web Docs][10])
|
||||
|
||||
---
|
||||
|
||||
### JS-XSS-004: Do not set event handler attributes from strings (e.g., `setAttribute("onclick", "...")`)
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT use `setAttribute("on…", string)` or similar patterns with untrusted data; this coerces strings into executable code in the event-handler context. ([OWASP Cheat Sheet Series][2])
|
||||
* SHOULD prefer `addEventListener` with function references.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `el.setAttribute("onclick", userInput)`
|
||||
* `el.onclick = userControlledString` (string assignment)
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `.setAttribute("on`, `.onclick =`, `.onmouseover =`, etc.
|
||||
* Trace whether RHS can be influenced by URL/hash/storage/postMessage. ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
Fix:
|
||||
|
||||
* Replace with `addEventListener("click", () => { ... })`.
|
||||
* If dynamic dispatch is needed, use an allowlisted mapping from identifiers to functions (no string eval). ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
---
|
||||
|
||||
### JS-URL-001: Sanitize and allowlist URLs before navigation (especially `window.location` / `location.replace`)
|
||||
|
||||
Severity: Low (High if you can prove an attacker can fully control the URL)
|
||||
|
||||
IMPORTANT: This can cause a lot of false positives. Please perform extra analysis to determine if the url is fully attacker controlled. If not fully attacker controlled, then this is informational at best.
|
||||
|
||||
NOTE: It may be important functionality to be able to redirect to any given url. If that is the goal of the feature, then at a minimum, ensure it checks the schema even if the origin is allowed to be anything.
|
||||
|
||||
Required:
|
||||
|
||||
* MUST treat any assignment to navigation targets as security-sensitive:
|
||||
|
||||
* `window.location = ...`
|
||||
* `location.href = ...`
|
||||
* `location.assign(...)`
|
||||
* `location.replace(...)` ([MDN Web Docs][4])
|
||||
* MUST prevent navigation to `javascript:` URLs (and generally other script-bearing/active schemes), especially when input is derived from URL params, storage, or messages. ([MDN Web Docs][4]). Only allow `http:` and `https:`.
|
||||
* SHOULD validate/allowlist the destination. A safe baseline is:
|
||||
|
||||
* Allow only same-origin relative paths, OR
|
||||
* Allow only a strict allowlist of origins and protocols (typically `https:` and optionally `http:` for localhost dev). ([OWASP Cheat Sheet Series][8])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `location.replace(getParam("next"))`
|
||||
* `window.location = userSuppliedUrl`
|
||||
* `location.assign(window.redirectTo || "/")` where `redirectTo` can be clobbered or attacker-set ([OWASP Cheat Sheet Series][8])
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `window.location`, `location.href`, `location.assign`, `location.replace`.
|
||||
* Search for common redirect parameters: `next`, `returnTo`, `redirect`, `url`, `continue`.
|
||||
* Search for `javascript:` literal usage. ([MDN Web Docs][4])
|
||||
|
||||
Fix:
|
||||
|
||||
* Parse and validate with `new URL(value, location.origin)` and then enforce:
|
||||
|
||||
* `url.protocol` in `{ "https:" }` (and only include `http:` in explicit dev-only code paths),
|
||||
* `url.origin` equals `location.origin` for internal redirects, or in a strict allowlist for external redirects,
|
||||
* optionally allow only specific path prefixes. ([MDN Web Docs][4])
|
||||
* If validation fails, navigate to a safe default (home/dashboard).
|
||||
|
||||
Mitigation:
|
||||
|
||||
* Deploy strict CSP and Trusted Types enforcement to reduce the impact of DOM XSS sinks, but note that Trusted Types do not prevent every possible unsafe navigation scenario on their own. ([W3C][15])
|
||||
|
||||
False positive notes:
|
||||
|
||||
IMPORTANT: This can cause a lot of false positives. Please perform extra analysis to determine if the url is fully attacker controlled. If not fully attacker controlled, then this is informational at best.
|
||||
|
||||
* Some apps intentionally support external redirects (SSO, payment flows). Those MUST be allowlisted and documented.
|
||||
|
||||
---
|
||||
|
||||
### JS-URL-002: Sanitize URLs before inserting into DOM URL contexts (`href`, `src`, etc.)
|
||||
|
||||
Severity: Low (High if you can prove an attacker can fully control the URL)
|
||||
|
||||
IMPORTANT: This can cause a lot of false positives. Please perform extra analysis to determine if the url is fully attacker controlled. If not fully attacker controlled, then this is informational at best.
|
||||
|
||||
Required:
|
||||
|
||||
* MUST treat setting URL-bearing DOM attributes/properties as security-sensitive, especially:
|
||||
|
||||
* `a.href`, `img.src`, `script.src`, `iframe.src`, `form.action`, `link.href`.
|
||||
* MUST prevent script-bearing schemes (`javascript:` and other active schemes) when values can be attacker-influenced. ([MDN Web Docs][4])
|
||||
* SHOULD prefer setting properties (e.g., `a.href = url.toString()`) after parsing and validation, rather than string concatenation.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `link.href = getParam("u")`
|
||||
* `el.setAttribute("href", userInput)` without validation
|
||||
* constructing URLs via concatenation with untrusted pieces
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `.href =`, `.src =`, `.action =`, `setAttribute("href"`, `setAttribute("src"`.
|
||||
* Search for `javascript:` / `data:` usage in URLs. ([MDN Web Docs][4])
|
||||
|
||||
IMPORTANT: This can cause a lot of false positives. Please perform extra analysis to determine if the url is fully attacker controlled. If not fully attacker controlled, then this is informational at best.
|
||||
|
||||
Fix:
|
||||
|
||||
* Use `new URL(...)` and validate:
|
||||
|
||||
* protocol allowlist
|
||||
* avoid passing user-provided values into `<script src>` at all (treat as code execution). ([OWASP Cheat Sheet Series][8])
|
||||
|
||||
---
|
||||
|
||||
### JS-CSP-001: Use CSP; meta delivery is allowed
|
||||
|
||||
Severity: Medium to High (depends on threat model; High when handling untrusted content)
|
||||
|
||||
NOTE: It is most important to set the CSP's script-src. All other directives are not as important and can generally be excluded for the ease of development.
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD deploy a CSP as a major defense-in-depth against XSS. ([MDN Web Docs][10])
|
||||
* MAY provide CSP via `<meta http-equiv="Content-Security-Policy" ...>` when headers are not available. ([MDN Web Docs][1])
|
||||
* If CSP is delivered via meta, MUST:
|
||||
|
||||
* place it early (before scripts/resources you want governed), and
|
||||
* not rely on unsupported directives in meta policies (`report-uri`, `frame-ancestors`, `sandbox`). ([W3C][3])
|
||||
* MUST avoid adding `unsafe-inline` as a “quick fix” for CSP issues unless explicitly required and reviewed (it defeats much of CSP’s purpose). ([MDN Web Docs][10])
|
||||
* MUST avoid adding `unsafe-eval` unless explicitly required and reviewed (it allows eval-like APIs that are commonly abused). ([MDN Web Docs][10])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* No CSP present anywhere (repo HTML or server/edge) for an app that renders untrusted content.
|
||||
* CSP includes `script-src 'unsafe-inline'` and/or `script-src 'unsafe-eval'` without strong justification. ([MDN Web Docs][10])
|
||||
* CSP delivered via meta but includes `frame-ancestors` (it will be ignored in meta). ([W3C][3])
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search HTML for `<meta http-equiv="Content-Security-Policy"`.
|
||||
* Search server/edge configs for `Content-Security-Policy` header.
|
||||
* If CSP is only in meta, check it appears before any `<script>` tags you want governed. ([W3C][3])
|
||||
|
||||
Fix:
|
||||
|
||||
* Prefer header-delivered CSP at the server/edge.
|
||||
* If constrained to meta, keep a strong allowlist CSP and document the limitations; implement clickjacking protections (e.g., `frame-ancestors`) at the server/edge, not in meta. ([W3C][3])
|
||||
|
||||
---
|
||||
|
||||
### JS-CSP-002: Prefer strict CSP (nonces/hashes); avoid inline/eval patterns in code
|
||||
|
||||
Severity: Medium
|
||||
|
||||
NOTE: It is most important to set the CSP's script-src. All other directives are not as important and can generally be excluded for the ease of development.
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD design frontend code to work under a strict CSP:
|
||||
|
||||
* avoid inline scripts and inline event handlers,
|
||||
* avoid eval-like APIs (see JS-XSS-003),
|
||||
* allow scripts via nonce or hash when needed. ([MDN Web Docs][10])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Large amounts of inline script blocks and inline `onclick="..."` handlers.
|
||||
* Libraries that require `unsafe-eval`.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `<script>` blocks with inline code, `onclick="`, `onload="`, etc.
|
||||
* Search for CSP directives containing `unsafe-inline` or `unsafe-eval`. ([MDN Web Docs][10])
|
||||
|
||||
Fix:
|
||||
|
||||
* Move inline scripts into external JS files (same-origin).
|
||||
* Use nonces/hashes for any unavoidable inline blocks. ([MDN Web Docs][10])
|
||||
|
||||
---
|
||||
|
||||
### JS-TT-001: Use Trusted Types to reduce DOM XSS attack surface (where supported)
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD consider enabling Trusted Types enforcement with CSP `require-trusted-types-for 'script'` to make many DOM XSS sinks reject raw strings. ([MDN Web Docs][11])
|
||||
* If using Trusted Types, SHOULD also use the CSP `trusted-types` directive to restrict which policies can be created (reduces policy sprawl and improves auditability). ([MDN Web Docs][16])
|
||||
* MUST keep Trusted Types policy code small, heavily reviewed, and used as the only path to produce trusted values for sinks. ([W3C][15])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* “Trusted Types enabled” but policy simply returns input unchanged (no sanitization/validation).
|
||||
* Many ad-hoc policies created across the codebase without restriction.
|
||||
* Belief that Trusted Types alone prevents all unsafe navigations or all XSS classes. (It targets DOM injection sinks; it is not a universal sandbox.) ([W3C][15])
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for CSP directives: `require-trusted-types-for` and `trusted-types`.
|
||||
* Search code for `trustedTypes.createPolicy(` and inspect policy implementations. ([MDN Web Docs][11])
|
||||
|
||||
Fix:
|
||||
|
||||
* Add a small set of well-reviewed policies (e.g., `createHTML` that sanitizes).
|
||||
* Restrict allowed policies via `trusted-types <policyName...>`.
|
||||
* Migrate sinks to require `TrustedHTML` / `TrustedScriptURL` as appropriate. ([MDN Web Docs][11])
|
||||
|
||||
---
|
||||
|
||||
### JS-MSG-001: `postMessage` must use strict origin validation and explicit targetOrigin
|
||||
|
||||
Severity: Medium (High if dangerous behavior can be triggered via postMessage)
|
||||
|
||||
Required:
|
||||
|
||||
* When sending messages, MUST set an explicit `targetOrigin` (not `*`) to avoid sending data to an unexpected origin after redirects or window origin changes. ([MDN Web Docs][5])
|
||||
* When receiving messages, MUST:
|
||||
|
||||
* Validate `event.origin` exactly against an allowlist of expected origins (no substring matching). ([OWASP Cheat Sheet Series][6])
|
||||
* Consider validating `event.source` (expected window reference) when applicable. ([MDN Web Docs][5])
|
||||
* Validate `event.data` structure (schema/shape) and treat it purely as data (never evaluate it as code and never insert into DOM with `innerHTML`). ([OWASP Cheat Sheet Series][6])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `otherWindow.postMessage(payload, "*")`
|
||||
* `window.addEventListener("message", (e) => { doSomething(e.data) })` with no `origin` check
|
||||
* `if (e.origin.includes("trusted.com"))` (substring checks)
|
||||
* `el.innerHTML = e.data` ([OWASP Cheat Sheet Series][6])
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `postMessage(`, `addEventListener("message"`, `onmessage =`.
|
||||
* Audit all handlers for explicit allowlist checks on `event.origin`. ([OWASP Cheat Sheet Series][6])
|
||||
|
||||
Fix:
|
||||
|
||||
* Define an allowlist:
|
||||
|
||||
* `const ALLOWED = new Set(["https://app.example.com", "https://accounts.example.com"]);`
|
||||
NOTE: For ease of development, you can use the current page's origin `window.location.origin` as a safe default origin.
|
||||
* On receive:
|
||||
|
||||
* `if (!ALLOWED.has(event.origin)) return;`
|
||||
* Validate `event.data` with a strict schema and reject unknown/extra fields.
|
||||
* On send:
|
||||
|
||||
* use the exact expected origin string as `targetOrigin`. ([OWASP Cheat Sheet Series][6])
|
||||
|
||||
Mitigation:
|
||||
|
||||
* Combine with a strict CSP and avoid DOM sinks in message paths. ([MDN Web Docs][10])
|
||||
|
||||
---
|
||||
|
||||
### JS-STORAGE-001: Web Storage is not a safe place for secrets (and is attacker-influencable)
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT store sensitive secrets or session identifiers in `localStorage` (or `sessionStorage`) if compromise would matter; a single XSS can exfiltrate everything in storage. ([OWASP Cheat Sheet Series][6])
|
||||
* MUST treat values read from storage as untrusted input (attackers can load malicious values into storage via XSS). ([OWASP Cheat Sheet Series][6])
|
||||
* SHOULD prefer server-set cookies with `HttpOnly` for session identifiers (JS cannot set `HttpOnly`, so avoid storing session IDs in JS-accessible storage). ([OWASP Cheat Sheet Series][6])
|
||||
* SHOULD avoid hosting multiple unrelated apps on the same origin if they rely on storage separation (storage is origin-wide). ([OWASP Cheat Sheet Series][6])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `localStorage.setItem("access_token", token)`
|
||||
* `localStorage.setItem("session", sessionId)`
|
||||
* Assuming `localStorage` is “trusted because same-origin.”
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `localStorage.getItem`, `localStorage.setItem`, `sessionStorage.*`.
|
||||
* Flag storage keys named `token`, `jwt`, `session`, `auth`, `refresh`. ([OWASP Cheat Sheet Series][6])
|
||||
|
||||
Fix:
|
||||
|
||||
* Use server-managed sessions or short-lived tokens delivered and rotated securely, with careful XSS defenses (CSP/Trusted Types) and minimal JS exposure.
|
||||
* If storage must be used for non-sensitive state, keep it non-auth and validate/escape before use.
|
||||
|
||||
---
|
||||
|
||||
### JS-SUPPLY-001: Third-party JavaScript is a major supply-chain risk; minimize and control it
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* MUST treat third-party JS as equivalent to first-party JS in privilege (it can execute arbitrary code in your origin and access DOM data). ([OWASP Cheat Sheet Series][7])
|
||||
* SHOULD minimize third-party scripts and prefer:
|
||||
|
||||
* self-hosting / script mirroring,
|
||||
* strict CSP allowlists,
|
||||
* SRI for any CDN-hosted scripts,
|
||||
* ongoing monitoring for unexpected changes. ([OWASP Cheat Sheet Series][7])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Loading arbitrary remote scripts from many vendors without review.
|
||||
* Using tag managers that can dynamically inject scripts with no integrity controls.
|
||||
* Allowing scripts from broad wildcards in CSP (e.g., `script-src *`). ([MDN Web Docs][10])
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search HTML for `<script src="https://...">` and `tag manager` snippets.
|
||||
* Search CSP `script-src` sources for wildcards or overly broad domains.
|
||||
* Search for dynamic script injection: `document.createElement("script")`, `script.src = ...`, `appendChild(script)`. ([OWASP Cheat Sheet Series][8])
|
||||
|
||||
Fix:
|
||||
|
||||
* Remove unnecessary third-party tags.
|
||||
* Self-host or mirror scripts where possible.
|
||||
* Lock down CSP `script-src` to the smallest set of trusted sources.
|
||||
* Add SRI for CDN scripts/styles. ([OWASP Cheat Sheet Series][7])
|
||||
|
||||
---
|
||||
|
||||
### JS-SRI-001: Use Subresource Integrity (SRI) for third-party scripts/styles
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD use SRI to ensure browsers only load third-party resources if they match an expected cryptographic hash. ([MDN Web Docs][12])
|
||||
* MUST update SRI hashes whenever the underlying resource changes (pin versions; avoid “latest” URLs).
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `<script src="https://cdn.example.com/lib.js"></script>` with no `integrity`.
|
||||
* Loading `latest` or unpinned third-party resources.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `<script src="https://` and `<link rel="stylesheet" href="https://` without `integrity=`.
|
||||
* Check whether `integrity` is present and uses strong hashes (sha256/384/512 are typical). ([MDN Web Docs][12])
|
||||
|
||||
Fix:
|
||||
|
||||
* Add `integrity="sha384-..."` (or appropriate) and ensure proper CORS mode where needed.
|
||||
* Prefer self-hosting critical libraries.
|
||||
|
||||
---
|
||||
|
||||
### FS-DOMC-001: Prevent DOM clobbering (avoid relying on `window`/`document` named properties)
|
||||
|
||||
Severity: Medium to High (can become Critical if it enables script loading or `javascript:` navigation)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT rely on implicit global variables or `window.someName` / `document.someName` lookups that can be clobbered by injected HTML elements with matching `id`/`name`. ([OWASP Cheat Sheet Series][8])
|
||||
* MUST avoid patterns like `let x = window.redirectTo || "/safe"; location.assign(x);` where `redirectTo` could be clobbered to an `<a>` element whose `href` is attacker-controlled (including `javascript:`). ([OWASP Cheat Sheet Series][8])
|
||||
* SHOULD use explicit variable declarations, local scope, and explicit DOM queries (`getElementById`) rather than named property access. ([OWASP Cheat Sheet Series][8])
|
||||
* If the app inserts user-controlled markup (even sanitized), SHOULD ensure sanitization strategies consider `id`/`name` collisions. ([OWASP Cheat Sheet Series][8])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `const cfg = window.config || {};` used for security-sensitive URLs.
|
||||
* `const redirect = window.redirectTo || "/"; location.assign(redirect);` ([OWASP Cheat Sheet Series][8])
|
||||
* Loading scripts from `window.*` config values without strict validation.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `window.` and `document.` used as config stores (especially `||` fallback patterns).
|
||||
* Search for usage of `location.assign/replace` with variables that come from `window`/`document` properties.
|
||||
* Search for dynamic script creation (`createElement('script')`) where `.src` comes from a non-local variable. ([OWASP Cheat Sheet Series][8])
|
||||
|
||||
Fix:
|
||||
|
||||
* Store config in module-scoped constants (not on `window`/`document`) and pass it explicitly.
|
||||
* Validate any URL-like config with protocol/origin allowlists (see FEJS-URL-001). ([OWASP Cheat Sheet Series][8])
|
||||
* Consider hardening: sanitization, CSP, and (in limited cases) freezing sensitive objects, but treat these as defense-in-depth, not a substitute for safe coding patterns. ([OWASP Cheat Sheet Series][8])
|
||||
|
||||
---
|
||||
|
||||
## 5) Practical scanning heuristics (how to “hunt”)
|
||||
|
||||
When actively scanning, use these high-signal patterns:
|
||||
|
||||
* DOM XSS sinks:
|
||||
|
||||
* `.innerHTML`, `.outerHTML`, `insertAdjacentHTML(`
|
||||
* `document.write(`, `document.writeln(` ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
* Dangerous navigation / URL sinks:
|
||||
|
||||
* `window.location`, `location.href`, `location.assign`, `location.replace`
|
||||
* `javascript:` literals (and other suspicious schemes like `data:text/html`) ([MDN Web Docs][4])
|
||||
|
||||
* String-to-code execution:
|
||||
|
||||
* `eval(`, `new Function`, `setTimeout("`, `setInterval("` ([MDN Web Docs][10])
|
||||
|
||||
* Event-handler string injection:
|
||||
|
||||
* `.setAttribute("on`, `.onclick =`, `.onload =` with strings ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
* `postMessage`:
|
||||
|
||||
* `postMessage(` with `"*"` as targetOrigin
|
||||
* `addEventListener("message"` without strict `event.origin` allowlist checks ([MDN Web Docs][5])
|
||||
|
||||
* Storage:
|
||||
|
||||
* `localStorage.setItem(` / `getItem(`, `sessionStorage.*`
|
||||
* keys containing `token`, `jwt`, `session`, `auth`, `refresh` ([OWASP Cheat Sheet Series][6])
|
||||
|
||||
* CSP and related:
|
||||
|
||||
* `Content-Security-Policy` header config (server/edge)
|
||||
* `<meta http-equiv="Content-Security-Policy" ...>`
|
||||
* CSP containing `unsafe-inline` or `unsafe-eval`
|
||||
* `require-trusted-types-for` / `trusted-types` directives ([MDN Web Docs][1])
|
||||
|
||||
* Third-party scripts:
|
||||
|
||||
* `<script src="https://...">` without `integrity=`
|
||||
* Tag manager snippets and dynamic script injection code paths ([MDN Web Docs][12])
|
||||
|
||||
|
||||
* DOM clobbering gadgets:
|
||||
|
||||
* `window.<name> || ...` and `document.<name> || ...` patterns
|
||||
* security-sensitive usage of `window`/`document` properties as config sources ([OWASP Cheat Sheet Series][8])
|
||||
|
||||
Always try to confirm:
|
||||
|
||||
* data origin (untrusted vs trusted),
|
||||
* sink type (HTML parse, navigation, code execution, message handling, storage),
|
||||
* protective controls present (CSP, Trusted Types, sanitizers, strict allowlists, schema validation).
|
||||
|
||||
---
|
||||
|
||||
## 6) Sources (accessed 2026-01-27)
|
||||
|
||||
Primary standards / platform docs:
|
||||
|
||||
* W3C Content Security Policy Level 2 (HTML `<meta>` delivery restrictions; unsupported directives in meta CSP): `https://www.w3.org/TR/CSP2/` ([W3C][3])
|
||||
* MDN: CSP Guide (strict CSP, nonces/hashes, `unsafe-inline`/`unsafe-eval`, eval blocking): `https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP` ([MDN Web Docs][10])
|
||||
* MDN: `<meta http-equiv>` (CSP via meta and warning about meta-based security headers): `https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta/http-equiv` ([MDN Web Docs][1])
|
||||
* MDN: `frame-ancestors` (and note it’s not supported in `<meta>`): `https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-ancestors` ([MDN Web Docs][18])
|
||||
|
||||
DOM XSS and dangerous sinks:
|
||||
|
||||
* OWASP: DOM Based XSS Prevention Cheat Sheet (dangerous sinks + safe patterns like `textContent`): `https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][2])
|
||||
* MDN: `innerHTML` (security considerations): `https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML` ([MDN Web Docs][19])
|
||||
* MDN: `insertAdjacentHTML` (security considerations): `https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML` ([MDN Web Docs][20])
|
||||
* MDN: `document.write()` / `document.writeln()` (security considerations): `https://developer.mozilla.org/en-US/docs/Web/API/Document/write` and `https://developer.mozilla.org/en-US/docs/Web/API/Document/writeln` ([MDN Web Docs][13])
|
||||
|
||||
URL scheme hazards:
|
||||
|
||||
* MDN: `javascript:` URLs (execution on navigation; discouraged; references `window.location`): `https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/javascript` ([MDN Web Docs][4])
|
||||
|
||||
Trusted Types:
|
||||
|
||||
* W3C: Trusted Types spec (DOM XSS sinks include `Element.innerHTML` and `Location.href` setters; goals and limitations): `https://www.w3.org/TR/trusted-types/` ([W3C][15])
|
||||
* MDN: `require-trusted-types-for` directive: `https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for` ([MDN Web Docs][11])
|
||||
* MDN: `trusted-types` directive: `https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/trusted-types` ([MDN Web Docs][16])
|
||||
|
||||
Cross-window messaging:
|
||||
|
||||
* MDN: `window.postMessage` (security guidance: specify targetOrigin; validate origin): `https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage` ([MDN Web Docs][5])
|
||||
* OWASP: HTML5 Security Cheat Sheet (Web Messaging guidance: explicit origin, strict checks, no `innerHTML`): `https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][6])
|
||||
|
||||
Third-party scripts and integrity:
|
||||
|
||||
* OWASP: Third Party JavaScript Management Cheat Sheet (risks and mitigations including SRI/mirroring): `https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Javascript_Management_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][7])
|
||||
* MDN: Subresource Integrity overview: `https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity` ([MDN Web Docs][12])
|
||||
* W3C: Subresource Integrity spec: `https://www.w3.org/TR/sri-2/` ([W3C][21])
|
||||
|
||||
DOM clobbering:
|
||||
|
||||
* OWASP: DOM Clobbering Prevention Cheat Sheet (named property access risk; example attacks involving `location.assign` and `javascript:`): `https://cheatsheetseries.owasp.org/cheatsheets/DOM_Clobbering_Prevention_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][8])
|
||||
|
||||
[1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta/http-equiv "https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta/http-equiv"
|
||||
[2]: https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html "https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html"
|
||||
[3]: https://www.w3.org/TR/CSP2/ "Content Security Policy Level 2"
|
||||
[4]: https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/javascript "javascript: URLs - URIs | MDN"
|
||||
[5]: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage "https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage"
|
||||
[6]: https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html "https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html"
|
||||
[7]: https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Javascript_Management_Cheat_Sheet.html "https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Javascript_Management_Cheat_Sheet.html"
|
||||
[8]: https://cheatsheetseries.owasp.org/cheatsheets/DOM_Clobbering_Prevention_Cheat_Sheet.html "https://cheatsheetseries.owasp.org/cheatsheets/DOM_Clobbering_Prevention_Cheat_Sheet.html"
|
||||
[9]: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/noopener "https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/noopener"
|
||||
[10]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP "https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP"
|
||||
[11]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for "https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for"
|
||||
[12]: https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity "https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity"
|
||||
[13]: https://developer.mozilla.org/en-US/docs/Web/API/Document/write "https://developer.mozilla.org/en-US/docs/Web/API/Document/write"
|
||||
[14]: https://developer.mozilla.org/en-US/docs/Web/API/Document/writeln "https://developer.mozilla.org/en-US/docs/Web/API/Document/writeln"
|
||||
[15]: https://www.w3.org/TR/trusted-types/ "https://www.w3.org/TR/trusted-types/"
|
||||
[16]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/trusted-types "https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/trusted-types"
|
||||
[18]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-ancestors "https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-ancestors"
|
||||
[19]: https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML "https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML"
|
||||
[20]: https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML "https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML"
|
||||
[21]: https://www.w3.org/TR/sri-2/ "https://www.w3.org/TR/sri-2/"
|
||||
@@ -0,0 +1,678 @@
|
||||
# jQuery Frontend Security Spec (jQuery 4.0.x, modern browsers)
|
||||
|
||||
This document is designed as a **security spec** that supports:
|
||||
|
||||
1. **Secure-by-default code generation** for new jQuery-based frontend code.
|
||||
2. **Security review / vulnerability hunting** in existing jQuery-based code (passive “notice issues while working” and active “scan the repo and report findings”).
|
||||
|
||||
It is intentionally written as a set of **normative requirements** (“MUST/SHOULD/MAY”) plus **audit rules** (what bad patterns look like, how to detect them, and how to fix/mitigate them).
|
||||
|
||||
---
|
||||
|
||||
## 0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)
|
||||
|
||||
* MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session tokens, refresh tokens, CSRF tokens, session cookies).
|
||||
* MUST treat the browser as an attacker-controlled environment:
|
||||
|
||||
* Frontend checks (UI gating, “disable button”, hidden fields, client-side validation) MUST NOT be treated as authorization or a security boundary.
|
||||
* Server-side authorization and validation MUST exist even if frontend is “correct”.
|
||||
* MUST NOT “fix” security by disabling protections (e.g., relaxing CSP to allow `unsafe-inline`, enabling JSONP “because it works”, adding broad CORS, disabling sanitization, suppressing security checks).
|
||||
* MUST provide evidence-based findings during audits: cite file paths, code snippets, and relevant configuration values.
|
||||
* MUST treat uncertainty honestly: if a protection might exist at the edge (CDN/WAF/reverse proxy headers like CSP), report it as “not visible in repo; verify at runtime/config”.
|
||||
|
||||
---
|
||||
|
||||
## 1) Operating modes
|
||||
|
||||
### 1.1 Generation mode (default)
|
||||
|
||||
When asked to write new jQuery code or modify existing jQuery code:
|
||||
|
||||
* MUST follow every **MUST** requirement in this spec.
|
||||
* SHOULD follow every **SHOULD** requirement unless the user explicitly says otherwise.
|
||||
* MUST prefer safe-by-default patterns: text insertion, DOM node construction, allowlists, and proven sanitization libraries over custom escaping.
|
||||
* MUST avoid introducing new risky sinks (HTML string building, dynamic script loading, JSONP, inline script/event-handler attributes, unsafe URL assignment, unsafe object merging).
|
||||
|
||||
### 1.2 Passive review mode (always on while editing)
|
||||
|
||||
While working anywhere in a repo that uses jQuery (even if the user did not ask for a security scan):
|
||||
|
||||
* MUST “notice” violations of this spec in touched/nearby code.
|
||||
* SHOULD mention issues as they come up, with a brief explanation + safe fix.
|
||||
|
||||
### 1.3 Active audit mode (explicit scan request)
|
||||
|
||||
When the user asks to “scan”, “audit”, or “hunt for vulns”:
|
||||
|
||||
* MUST systematically search the codebase for violations of this spec.
|
||||
* MUST output findings in the structured format (see §2.3).
|
||||
|
||||
Recommended audit order:
|
||||
|
||||
1. jQuery sourcing, versions, and dependency hygiene (script tags, lockfiles, CDN usage, SRI).
|
||||
2. CSP / Trusted Types / security headers posture (in repo and at runtime if observable).
|
||||
3. DOM XSS: untrusted sources → jQuery sinks (`.html`, `.append`, `$("<…>")`, `.load`, etc.).
|
||||
4. Script execution sinks: JSONP, `dataType:"script"`, `$.getScript`, dynamic `<script>` insertion.
|
||||
5. URL/attribute assignment (`href`, `src`, `style`, `on*` attributes).
|
||||
6. Prototype pollution / unsafe object merging (`$.extend` patterns).
|
||||
7. AJAX auth patterns + CSRF for cookie-based sessions.
|
||||
8. Third-party plugins and untrusted content rendering paths (comments, WYSIWYG, markdown-to-HTML).
|
||||
|
||||
---
|
||||
|
||||
## 2) Definitions and review guidance
|
||||
|
||||
### 2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)
|
||||
|
||||
Examples include:
|
||||
|
||||
* Any data from the server that originates from users (user profiles, comments, “display name”, rich text, filenames).
|
||||
* Data from third-party APIs or services.
|
||||
* Browser-controlled sources:
|
||||
|
||||
* `location.href`, `location.search`, `location.hash`
|
||||
* `document.URL`, `document.baseURI`, `document.referrer`
|
||||
* `window.name`
|
||||
* `localStorage` / `sessionStorage`
|
||||
* `postMessage` event data (unless strict origin and schema validation exists)
|
||||
* Any DOM content that could have been injected previously (stored XSS)
|
||||
|
||||
### 2.2 High-risk “sinks” in jQuery contexts
|
||||
|
||||
A sink is a code path where untrusted input can become interpreted as executable code or HTML.
|
||||
|
||||
Key jQuery sink categories:
|
||||
|
||||
* HTML insertion / parsing:
|
||||
|
||||
* DOM manipulation methods that accept HTML strings such as `.html()`, `.append()`, and related methods (see CVE notes below). ([NVD][1])
|
||||
* `$(htmlString)` (when the argument can be interpreted as HTML markup).
|
||||
* `jQuery.parseHTML(html, …, keepScripts)` especially with `keepScripts=true`. ([jQuery API][2])
|
||||
* `.load(url)` (loads HTML into DOM; has special script execution behavior). ([jQuery API][3])
|
||||
* Script execution / dynamic code loading:
|
||||
|
||||
* `$.getScript()` / `$.ajax({ dataType: "script" })` (executes fetched JavaScript). ([jQuery API][4])
|
||||
* JSONP (`dataType: "jsonp"` or implicit JSONP behavior) (executes remote JavaScript as a response). ([jQuery API][5])
|
||||
* `eval`, `new Function`, `setTimeout("…")`, `setInterval("…")`, `$.globalEval` (if present)
|
||||
* Dangerous attribute assignment:
|
||||
|
||||
* Assigning untrusted strings to `href`, `src`, `srcdoc`, `style`, or event-handler attributes (`onload`, `onclick`, etc.)
|
||||
* `javascript:` URLs are particularly dangerous and discouraged. ([MDN Web Docs][6])
|
||||
|
||||
### 2.3 Required audit finding format
|
||||
|
||||
For each issue found, output:
|
||||
|
||||
* Rule ID:
|
||||
* Severity: Critical / High / Medium / Low
|
||||
* Location: file path + function/component + line(s)
|
||||
* Evidence: the exact code/config snippet
|
||||
* Impact: what could go wrong, who can exploit it
|
||||
* Fix: safe change (prefer minimal diff)
|
||||
* Mitigation: defense-in-depth if immediate fix is hard
|
||||
* False positive notes: what to verify if uncertain
|
||||
|
||||
---
|
||||
|
||||
## 3) Secure baseline: minimum production configuration (MUST in production)
|
||||
|
||||
This is the smallest “production baseline” that prevents common jQuery-related security failures.
|
||||
|
||||
### 3.1 Use a supported, patched jQuery version (MUST)
|
||||
|
||||
* MUST use a supported jQuery major version and keep it updated.
|
||||
* As of 2026-01-27, the jQuery project ships jQuery 4.0.0 as the latest major release. ([blog.jquery.com][7])
|
||||
* If you must support very old browsers (notably IE < 11), jQuery 4 does not support them and you may need to stay on jQuery 3.x; treat this as a higher risk posture and patch aggressively. ([blog.jquery.com][7])
|
||||
|
||||
### 3.2 Load jQuery safely (MUST)
|
||||
|
||||
* MUST load jQuery only from:
|
||||
|
||||
* Your own build pipeline (bundled via npm/yarn + lockfile), or
|
||||
* The official jQuery CDN / a trusted CDN with Subresource Integrity (SRI) enabled.
|
||||
* If loading from a CDN, SHOULD use SRI (`integrity`) and correct `crossorigin` settings; the jQuery project explicitly supports and recommends SRI on its CDN. (Retrieved from [jquery.com][8])
|
||||
|
||||
### 3.3 CSP + Trusted Types (SHOULD, and MUST where available/required by policy)
|
||||
|
||||
* SHOULD deploy a Content Security Policy (CSP) that reduces XSS impact (especially `script-src` restrictions and avoiding `unsafe-inline`). If not done through HTTP server, this can be done through the `<meta http-equiv="Content-Security-Policy" content="...">` tag. ([OWASP Cheat Sheet Series][9]) NOTE: It is most important to set the CSP's script-src. All other directives are not as important and can generally be excluded for the ease of development.
|
||||
* SHOULD consider Trusted Types as a strong defense-in-depth against DOM XSS. ([W3C][10])
|
||||
* If you deploy the CSP directive `require-trusted-types-for`, then code MUST route DOM-injection through Trusted Types policies. ([MDN Web Docs][11])
|
||||
* Note: jQuery 4.0 explicitly added Trusted Types support so that TrustedHTML can be used with jQuery manipulation methods without violating `require-trusted-types-for`. ([blog.jquery.com][7])
|
||||
|
||||
### 3.4 Security headers and cookie posture (defense in depth; SHOULD)
|
||||
|
||||
Even though these are typically set server-side, they materially reduce the blast radius of jQuery-related mistakes. However if the context is only the frontend web application, these cannot be acted on.
|
||||
|
||||
* SHOULD set common security headers (CSP, `X-Content-Type-Options: nosniff`, clickjacking protection via `frame-ancestors` / `X-Frame-Options`, `Referrer-Policy`). ([OWASP Cheat Sheet Series][12])
|
||||
* SHOULD avoid storing long-lived secrets/tokens in places accessible to JavaScript (like `localStorage`) unless the threat model explicitly accepts “XSS == account takeover”. This is not jQuery-specific, but jQuery-heavy DOM manipulation increases the chance of DOM XSS regressions; reduce the payoff.
|
||||
|
||||
---
|
||||
|
||||
## 4) Rules (generation + audit)
|
||||
|
||||
Each rule contains: required practice, insecure patterns, detection hints, and remediation.
|
||||
|
||||
### JQ-SUPPLY-001: jQuery MUST be patched; do not run known vulnerable versions
|
||||
|
||||
Severity: Medium (High if internet-facing app AND version is known-vulnerable)
|
||||
|
||||
NOTE: Before performing an upgrade, get concent from the user and try to understand if they have reasons to keep it back. Upgrading can break applications in unexpected ways. Report and recommend upgrades rather than just performing them.
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT use jQuery versions with known high-impact vulnerabilities when a patched version exists.
|
||||
* MUST upgrade past:
|
||||
|
||||
* CVE-2019-11358 (prototype pollution in jQuery before 3.4.0). ([NVD][13])
|
||||
* CVE-2020-11022 / CVE-2020-11023 (XSS risks in DOM manipulation methods when handling untrusted HTML; patched in 3.5.0). ([NVD][1])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Script tags or package manifests referencing old jQuery (e.g., `jquery-1.*`, `jquery-2.*`, `jquery-3.3.*`, `jquery-3.4.*`, `jquery-3.4.1`, etc.).
|
||||
* Bundled vendor directories containing old minified jQuery without an upgrade path.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search HTML/templates for `jquery-` and parse version strings.
|
||||
* Check `package.json`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`.
|
||||
* Check `vendor/`, `public/`, `static/`, `assets/`, `wwwroot/` for `jquery*.js`.
|
||||
|
||||
Fix:
|
||||
|
||||
* Upgrade to current jQuery (prefer latest stable major; as of 2026-01-27, 4.0.0 is current). ([blog.jquery.com][7])
|
||||
* If upgrade is constrained, at minimum upgrade beyond the CVE thresholds and add compensating controls (strong CSP, strict sanitization, remove risky APIs like JSONP, remove deep-extend of untrusted objects).
|
||||
|
||||
Notes:
|
||||
|
||||
* If a product requirement forces old versions, report as “accepted risk requiring compensating controls”.
|
||||
|
||||
---
|
||||
|
||||
### JQ-SUPPLY-002: Third-party script loading SHOULD use integrity and trusted origins
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST load jQuery and plugins only from trusted origins.
|
||||
* If loaded from CDN, SHOULD use SRI (`integrity`) and correct `crossorigin` handling. ([jquery.com][8])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `<script src="https://…/jquery.min.js"></script>` with no `integrity`.
|
||||
* Loading jQuery from random third-party CDNs without an explicit trust decision.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Scan HTML for `<script src=` and check for `integrity=` + `crossorigin=`.
|
||||
* Identify dynamic script insertion with untrusted URLs (see JQ-EXEC-001).
|
||||
|
||||
Fix:
|
||||
|
||||
* Prefer bundling via npm + lockfile.
|
||||
* If using CDN, copy official script tag (jQuery CDN supports SRI). ([jquery.com][8])
|
||||
|
||||
Note: If unable to get the correct SRI tag, skip this step but tell the user. If you end up using the wrong one the app will not function. In that case remove it and inform the user.
|
||||
|
||||
---
|
||||
|
||||
### JQ-XSS-001: Untrusted data MUST NOT be inserted as HTML via jQuery DOM-manipulation methods
|
||||
|
||||
Severity: High (if attacker-controlled content reaches these sinks)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST treat any HTML string insertion as a code execution boundary.
|
||||
* MUST use safe alternatives for untrusted text:
|
||||
|
||||
* `.text(untrusted)` (text, not HTML). ([jQuery API][14])
|
||||
* `.val(untrusted)` for form fields. ([jQuery API][15])
|
||||
* Create elements and set text/attributes safely instead of concatenating HTML strings.
|
||||
|
||||
Insecure patterns (examples):
|
||||
|
||||
* `$(selector).html(untrusted)`
|
||||
* `$(selector).append(untrusted)`
|
||||
* `$(selector).before(untrusted)` / `.after(untrusted)` / `.replaceWith(untrusted)` / `.wrap(untrusted)` (and similar)
|
||||
* Building markup: `"<div>" + untrusted + "</div>"` then passing to jQuery
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Grep for: `.html(`, `.append(`, `.prepend(`, `.before(`, `.after(`, `.replaceWith(`, `.wrap(`, `.wrapAll(`, `.wrapInner(`
|
||||
* Trace dataflow into these calls from sources in §2.1.
|
||||
|
||||
Fix:
|
||||
|
||||
* Replace with `.text()` / `.val()` or node construction:
|
||||
|
||||
* `const $el = $("<span>").text(untrusted); container.append($el);`
|
||||
* If the output must contain limited markup, see JQ-XSS-002 (sanitization).
|
||||
|
||||
Notes:
|
||||
|
||||
* Older jQuery versions had additional edge cases even when attempting sanitization; patched in 3.5.0+. Still: never rely on “string sanitization” alone—prefer structured creation or proven sanitizers. ([GitHub][16])
|
||||
|
||||
---
|
||||
|
||||
### JQ-XSS-002: If rendering user-controlled HTML is required, it MUST be sanitized with a proven HTML sanitizer
|
||||
|
||||
Severity: Medium (High if rich HTML is attacker-controlled and sanitizer is weak/misconfigured)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT “roll your own” HTML sanitizer with regexes.
|
||||
* If user-controlled HTML must be displayed (e.g., rich text comments), MUST sanitize using a well-maintained HTML sanitizer and a restrictive allowlist.
|
||||
|
||||
* DOMPurify is a common choice; use conservative configuration and keep it updated. ([GitHub][17])
|
||||
* Where available, MAY consider the browser HTML Sanitizer API (note: limited browser availability). ([MDN Web Docs][18])
|
||||
* SHOULD pair sanitization with CSP and, where feasible, Trusted Types for defense in depth. ([OWASP Cheat Sheet Series][9])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Regex-based “strip `<script>`” or “escape `<`” attempts followed by `.html()` insertion.
|
||||
* DOMPurify (or similar) configured to allow overly broad tags/attributes, or configuration that’s not reviewed.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for “sanitize” helper functions, regex replacing `<`/`>` patterns, or “allow all tags” configs.
|
||||
* Identify features that render user-generated “rich text” or “custom HTML”.
|
||||
* Check if sanitizer results are inserted with `.html()` or equivalent sinks.
|
||||
|
||||
Fix:
|
||||
|
||||
* Introduce a sanitizer with strict allowlist.
|
||||
* Centralize the “sanitize then inject” pattern into a single reviewed module.
|
||||
* Add regression tests covering representative malicious inputs (don’t store payloads in logs or telemetry).
|
||||
|
||||
False positive notes:
|
||||
|
||||
* If content is guaranteed trusted (e.g., compiled templates shipped by you), document the trust boundary and why it is not attacker-controlled.
|
||||
|
||||
---
|
||||
|
||||
### JQ-XSS-003: `$(untrustedString)` and `jQuery.parseHTML` MUST NOT process attacker-controlled markup
|
||||
|
||||
Severity: High (if attacker-controlled)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT pass attacker-controlled strings to `$()` when they might be interpreted as HTML.
|
||||
* MUST treat `jQuery.parseHTML(html, …, keepScripts)` as a high-risk primitive; keepScripts MUST be `false` for any untrusted input. ([jQuery API][2])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `const $node = $(untrusted);`
|
||||
* `$.parseHTML(untrusted, /* context */, true)` (scripts preserved)
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `$(` calls where the argument is not a static selector or static markup.
|
||||
* Search for `$.parseHTML(` and inspect the `keepScripts` argument.
|
||||
|
||||
Fix:
|
||||
|
||||
* Use DOM creation with constant tag names and `.text()` for untrusted values.
|
||||
* If parsing HTML is necessary, sanitize first (JQ-XSS-002) and keep scripts disabled.
|
||||
|
||||
---
|
||||
|
||||
### JQ-XSS-004: `.load()` MUST be treated as an HTML+script injection surface
|
||||
|
||||
Severity: Medium (High if URL/content is attacker-controlled)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT use `.load()` with attacker-controlled URLs or attacker-controlled HTML fragments.
|
||||
* MUST understand jQuery `.load()` script behavior:
|
||||
|
||||
* Without a selector in the URL, content is passed to `.html()` before scripts are removed, which can execute scripts. ([jQuery API][3])
|
||||
* SHOULD prefer `fetch()`/XHR to retrieve data, then render with safe DOM creation or sanitize explicitly.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `$("#target").load(untrustedUrl)`
|
||||
* `$("#target").load("/path?param=" + untrusted)`
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `.load(` across JS/TS files.
|
||||
* Identify whether a selector is appended to the URL (the behavior differs). ([jQuery API][3])
|
||||
* Trace whether the URL can be influenced by user input.
|
||||
|
||||
Fix:
|
||||
|
||||
* Replace `.load()` with:
|
||||
|
||||
* `fetch()` to retrieve JSON, then render via `.text()` / node construction, or
|
||||
* `fetch()` to retrieve HTML, sanitize it, then inject.
|
||||
* If `.load()` must remain, ensure the URL is constant or strictly allowlisted and the returned content is trusted.
|
||||
|
||||
---
|
||||
|
||||
### JQ-EXEC-001: Dynamic script execution and script fetching MUST NOT be reachable from untrusted input
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT fetch-and-execute scripts from untrusted or user-influenced URLs.
|
||||
* MUST treat these as code execution primitives:
|
||||
|
||||
* `$.getScript(url)` executes the fetched script in the global context. ([jQuery API][4])
|
||||
* `$.ajax({ dataType: "script" })` and other script-typed requests that execute responses.
|
||||
* SHOULD remove these patterns unless there is a strong, reviewed justification.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `$.getScript(untrustedUrl)`
|
||||
* `$.ajax({ url: untrustedUrl, dataType: "script" })`
|
||||
* Dynamic `<script src=...>` injection where `src` is derived from untrusted input.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `getScript(`, `dataType: "script"`, `globalEval`, `eval`, `new Function`.
|
||||
* Look for “plugin loader” or “theme loader” features that accept URLs.
|
||||
|
||||
Fix:
|
||||
|
||||
* Bundle scripts at build time.
|
||||
* If runtime-loading is required, restrict to allowlisted, versioned, integrity-checked assets (and ideally still avoid runtime code loading).
|
||||
|
||||
---
|
||||
|
||||
### JQ-AJAX-001: JSONP MUST be disabled unless the endpoint is fully trusted (and even then, avoid)
|
||||
|
||||
Severity: Medium (High if attacker can influence URL/endpoint)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT use JSONP for untrusted endpoints because it executes JavaScript responses.
|
||||
* When using `$.ajax`, MUST explicitly disable JSONP for non-fully-trusted targets; jQuery’s own docs recommend setting `jsonp: false` “for security reasons” if you don’t trust the target. ([jQuery API][5])
|
||||
* SHOULD prefer CORS with JSON (`dataType: "json"`) and explicit origin allowlists server-side.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `dataType: "jsonp"`
|
||||
* URLs containing `callback=?` or patterns that trigger JSONP behavior. callback arguments are historically XSS vectors.
|
||||
* `$.get(untrustedUrl)` without pinning `dataType` and disabling JSONP (risk depends on options and jQuery behavior)
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `jsonp`, `dataType: "jsonp"`, `callback=?`.
|
||||
* Search for cross-domain AJAX where the URL is not hard-coded or allowlisted.
|
||||
|
||||
Fix:
|
||||
|
||||
* Use JSON over HTTPS with CORS configured server-side.
|
||||
* Set:
|
||||
|
||||
* `dataType: "json"`
|
||||
* `jsonp: false` (defense in depth when URL might be ambiguous) ([jQuery API][5])
|
||||
|
||||
---
|
||||
|
||||
### JQ-AJAX-002: State-changing AJAX requests using cookie auth MUST be CSRF-protected
|
||||
|
||||
Severity: High
|
||||
|
||||
NOTE: This only matters when using cookie based auth. If the request use Authorization header, there is no CSRF potential.
|
||||
|
||||
Required:
|
||||
|
||||
* If authentication uses cookies, MUST protect state-changing requests (POST/PUT/PATCH/DELETE) against CSRF.
|
||||
* SHOULD use server-verified CSRF tokens; for AJAX calls, tokens are commonly sent in a custom header. ([OWASP Cheat Sheet Series][19])
|
||||
* MUST NOT treat “it’s an AJAX request” as CSRF protection by itself.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `$.post("/transfer", {...})` or `$.ajax({ method: "POST", ... })` with cookie auth and no CSRF token/header.
|
||||
* “CSRF protection” that only checks for `X-Requested-With` (defense-in-depth only, not primary).
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Enumerate state-changing AJAX calls and locate whether they include CSRF tokens.
|
||||
* Identify how the server expects CSRF validation (meta tag, cookie-to-header double submit, synchronizer token, etc.).
|
||||
|
||||
Fix:
|
||||
|
||||
* Add CSRF token inclusion in a centralized place, e.g., `$.ajaxSetup({ headers: { "X-CSRF-Token": token } })`, and ensure server verifies.
|
||||
* Follow OWASP CSRF guidance for token properties and validation. ([OWASP Cheat Sheet Series][19])
|
||||
|
||||
False positive notes:
|
||||
|
||||
* If auth is not cookie-based (e.g., Authorization header bearer token) CSRF risk is different; verify actual auth mechanism.
|
||||
|
||||
---
|
||||
|
||||
### JQ-ATTR-001: Untrusted values MUST NOT be written into dangerous attributes without validation/allowlisting
|
||||
|
||||
Severity: Low (High for events like onclick)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST validate/allowlist URLs written into `href`, `src`, `action`, etc.
|
||||
* MUST block dangerous schemes; `javascript:` URLs are discouraged because they can execute code. ([MDN Web Docs][6])
|
||||
* MUST NOT set event-handler attributes (`onclick`, `onerror`, etc.) from strings.
|
||||
* SHOULD avoid writing untrusted strings into `style` attributes; prefer toggling predefined CSS classes.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `$("a").attr("href", untrustedUrl)`
|
||||
* `$("img").attr("src", untrustedUrl)`
|
||||
* `$(el).attr("style", untrustedCss)`
|
||||
* `$(el).attr("onclick", untrustedJs)`
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `.attr("href"`, `.attr("src"`, `.attr("style"`, `.prop("href"`, `.prop("src"`.
|
||||
* Trace whether inputs come from URL params, server JSON, DOM, or storage.
|
||||
|
||||
Fix:
|
||||
|
||||
* Parse and validate URLs with `new URL(value, location.origin)` and allowlist protocols (`https:` etc.) and hostnames when needed.
|
||||
* For navigation targets, prefer relative paths you construct rather than full URLs.
|
||||
* Replace `style` strings with `addClass/removeClass` using predefined class names.
|
||||
|
||||
---
|
||||
|
||||
### JQ-SELECTOR-001: User-controlled selector fragments MUST be escaped with `jQuery.escapeSelector`
|
||||
|
||||
Severity: Medium (can become High if it enables wrong-element selection in security-relevant UI)
|
||||
|
||||
Required:
|
||||
|
||||
* If you must select by an ID/class that can contain special CSS characters, SHOULD use `jQuery.escapeSelector()` (available in jQuery 3.0+). ([jQuery API][20])
|
||||
* MUST NOT concatenate raw attacker-controlled strings into selector expressions.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `$("#" + untrustedId)`
|
||||
* `$("[data-id='" + untrusted + "']")` (especially without strict quoting/escaping)
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `"#" +`, `". " +`, or template strings used inside `$(` selectors.
|
||||
* Look for “select by user-supplied id”.
|
||||
|
||||
Fix:
|
||||
|
||||
* `$("#" + $.escapeSelector(untrustedId))` ([jQuery API][20])
|
||||
* Prefer stable internal IDs over user-derived selectors.
|
||||
|
||||
Notes:
|
||||
|
||||
* This is often “robustness”, but it can become security-relevant if incorrect selection causes UI to reveal/modify the wrong data or skip security-related prompts.
|
||||
|
||||
---
|
||||
|
||||
### JQ-PROTOTYPE-001: Do not deep-merge untrusted objects; prevent prototype pollution
|
||||
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT deep-merge (`$.extend(true, …)`) attacker-controlled objects into application objects without filtering dangerous keys.
|
||||
* MUST ensure jQuery is >= 3.4.0 to avoid CVE-2019-11358 prototype pollution behavior. ([NVD][13])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `$.extend(true, target, untrustedObj)`
|
||||
* `$.extend(true, {}, defaults, untrustedObj)` where untrustedObj comes from URL/JSON/storage
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `$.extend(true` and inspect sources of merged objects.
|
||||
* Search for “merge options” / “apply config” patterns using untrusted JSON.
|
||||
|
||||
Fix:
|
||||
|
||||
* Prefer:
|
||||
|
||||
* Shallow merges with an allowlisted set of keys, or
|
||||
* A safe merge helper that explicitly rejects `__proto__`, `prototype`, `constructor`, and nested occurrences.
|
||||
* Keep jQuery patched.
|
||||
|
||||
---
|
||||
|
||||
### JQ-CSP-001: CSP and Trusted Types SHOULD be used to make DOM XSS harder to introduce and exploit
|
||||
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD deploy CSP as defense-in-depth against XSS. ([OWASP Cheat Sheet Series][9])
|
||||
* If enabling Trusted Types (`require-trusted-types-for`), MUST ensure DOM injection goes through Trusted Types policies. ([MDN Web Docs][11])
|
||||
* When using jQuery 4, SHOULD take advantage of its Trusted Types support (TrustedHTML inputs). ([blog.jquery.com][7])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* “Fixing” a jQuery feature by weakening CSP (`script-src 'unsafe-inline'` / `'unsafe-eval'`) without a compensating plan.
|
||||
* No CSP on applications that render user content or manipulate DOM heavily.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Look for CSP headers (server configs, framework middleware, meta tags).
|
||||
* If not visible in repo, flag as “verify at edge/runtime”.
|
||||
|
||||
Fix:
|
||||
|
||||
* Add CSP incrementally; start by eliminating inline scripts and inline event handlers, then tighten `script-src`.
|
||||
* Add Trusted Types where supported and feasible.
|
||||
|
||||
---
|
||||
|
||||
## 5) Practical scanning heuristics (how to “hunt”)
|
||||
|
||||
When actively scanning, use these high-signal patterns:
|
||||
|
||||
* jQuery version / sourcing:
|
||||
|
||||
* `jquery-*.js` in `vendor/` or `static/`
|
||||
* `package.json` dependency `jquery` pinned to old versions
|
||||
* CDN script tags lacking `integrity`/`crossorigin` ([jquery.com][8])
|
||||
* HTML injection sinks (DOM XSS):
|
||||
|
||||
* `.html(`, `.append(`, `.prepend(`, `.before(`, `.after(`, `.replaceWith(`, `.wrap(`
|
||||
* `$(` where argument might be HTML / template strings
|
||||
* `$.parseHTML(` especially with `keepScripts=true` ([jQuery API][2])
|
||||
* `.load(` (and whether selector is appended; script behavior differs) ([jQuery API][3])
|
||||
* Script execution / dynamic code:
|
||||
|
||||
* `$.getScript(`, `dataType: "script"` ([jQuery API][4])
|
||||
* `dataType: "jsonp"` or `jsonp:` usage; `callback=?` patterns ([jQuery API][5])
|
||||
* `eval`, `new Function`, `setTimeout("…")`, `$.globalEval`
|
||||
* Dangerous attribute writes:
|
||||
|
||||
* `.attr("href", …)`, `.attr("src", …)`, `.attr("style", …)`
|
||||
* Any assignment of `javascript:`-like schemes or suspicious URL construction ([MDN Web Docs][6])
|
||||
* Selector construction:
|
||||
|
||||
* `$("#" + user)` and similar; fix via `$.escapeSelector` ([jQuery API][20])
|
||||
* Prototype pollution:
|
||||
|
||||
* `$.extend(true, …, userObj)`; ensure jQuery >= 3.4.0 and filter dangerous keys ([NVD][13])
|
||||
* CSRF posture for AJAX:
|
||||
|
||||
* `$.post(` / `$.ajax({ method: ... })` with cookies and no CSRF token/header ([OWASP Cheat Sheet Series][19])
|
||||
* Defense-in-depth:
|
||||
|
||||
* Absence of CSP/security headers in configs (or not visible; require runtime verification) ([OWASP Cheat Sheet Series][12])
|
||||
|
||||
Always try to confirm:
|
||||
|
||||
* data origin (untrusted vs trusted)
|
||||
* sink type (HTML insertion / script execution / attribute / selector / object merge)
|
||||
* protective controls present (sanitizer, allowlists, CSP, Trusted Types, CSRF validation)
|
||||
|
||||
---
|
||||
|
||||
## 6) Sources (accessed 2026-01-27)
|
||||
|
||||
Primary jQuery project documentation and release notes:
|
||||
|
||||
* jQuery 4.0.0 release notes (Trusted Types/CSP changes; version info): `https://blog.jquery.com/2026/01/17/jquery-4-0-0/`. ([blog.jquery.com][7])
|
||||
* Download jQuery (latest version info; CDN + SRI guidance): `https://jquery.com/download/`. ([jquery.com][8])
|
||||
* jQuery API: `.html()`: `https://api.jquery.com/html/`. ([jQuery API][21])
|
||||
* jQuery API: `.text()`: `https://api.jquery.com/text/`. ([jQuery API][14])
|
||||
* jQuery API: `.append()`: `https://api.jquery.com/append/`. ([jQuery API][22])
|
||||
* jQuery API: `.load()` (script execution behavior): `https://api.jquery.com/load/`. ([jQuery API][3])
|
||||
* jQuery API: `jQuery.parseHTML(…, keepScripts)`: `https://api.jquery.com/jQuery.parseHTML/`. ([jQuery API][2])
|
||||
* jQuery API: `$.ajax()` (`jsonp: false` security note): `https://api.jquery.com/jQuery.ajax/`. ([jQuery API][5])
|
||||
* jQuery API: `$.getScript()` (executes script): `https://api.jquery.com/jQuery.getScript/`. ([jQuery API][4])
|
||||
* jQuery API: `jQuery.escapeSelector()`: `https://api.jquery.com/jQuery.escapeSelector/`. ([jQuery API][20])
|
||||
|
||||
jQuery vulnerabilities / advisories:
|
||||
|
||||
* NVD CVE-2019-11358 (prototype pollution; jQuery < 3.4.0): `https://nvd.nist.gov/vuln/detail/CVE-2019-11358`. ([NVD][13])
|
||||
* NVD CVE-2020-11022 (XSS risk in DOM manipulation methods; patched in 3.5.0): `https://nvd.nist.gov/vuln/detail/CVE-2020-11022`. ([NVD][1])
|
||||
* NVD CVE-2020-11023 (XSS risk involving `<option>`; patched in 3.5.0): `https://nvd.nist.gov/vuln/detail/CVE-2020-11023`. ([NVD][23])
|
||||
* GitHub Security Advisory GHSA-gxr4-xjj5-5px2 (jQuery htmlPrefilter XSS; patched in 3.5.0): `https://github.com/jquery/jquery/security/advisories/GHSA-gxr4-xjj5-5px2`. ([GitHub][16])
|
||||
|
||||
OWASP Cheat Sheet Series (web app security foundations relevant to jQuery usage):
|
||||
|
||||
* XSS Prevention: `https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html`. ([OWASP Cheat Sheet Series][24])
|
||||
* DOM-based XSS Prevention: `https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html`. ([OWASP Cheat Sheet Series][25])
|
||||
* CSRF Prevention: `https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html`. ([OWASP Cheat Sheet Series][19])
|
||||
* HTTP Security Headers: `https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html`. ([OWASP Cheat Sheet Series][12])
|
||||
* Content Security Policy Cheat Sheet: `https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html`. ([OWASP Cheat Sheet Series][9])
|
||||
|
||||
Browser/platform references (SRI, CSP, Trusted Types, and dangerous URL schemes):
|
||||
|
||||
* MDN: Subresource Integrity (SRI): `https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity`. ([MDN Web Docs][26])
|
||||
* W3C: SRI specification: `https://www.w3.org/TR/sri-2/`. ([W3C][27])
|
||||
* MDN: CSP guide: `https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP`. ([MDN Web Docs][28])
|
||||
* MDN: `require-trusted-types-for` directive: `https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for`. ([MDN Web Docs][11])
|
||||
* MDN: Trusted Types API: `https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API`. ([MDN Web Docs][29])
|
||||
* W3C: Trusted Types specification: `https://www.w3.org/TR/trusted-types/`. ([W3C][10])
|
||||
* MDN: `javascript:` URL scheme warning: `https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/javascript`. ([MDN Web Docs][6])
|
||||
* DOMPurify project documentation: `https://github.com/cure53/DOMPurify`. ([GitHub][17])
|
||||
|
||||
[1]: https://nvd.nist.gov/vuln/detail/cve-2020-11022?utm_source=chatgpt.com "CVE-2020-11022 Detail - NVD"
|
||||
[2]: https://api.jquery.com/jQuery.parseHTML/?utm_source=chatgpt.com "jQuery.parseHTML()"
|
||||
[3]: https://api.jquery.com/load/?utm_source=chatgpt.com ".load() | jQuery API Documentation"
|
||||
[4]: https://api.jquery.com/jQuery.getScript/?utm_source=chatgpt.com "jQuery.getScript()"
|
||||
[5]: https://api.jquery.com/jQuery.ajax/?utm_source=chatgpt.com "jQuery.ajax()"
|
||||
[6]: https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/javascript?utm_source=chatgpt.com "javascript: URLs - URIs - MDN Web Docs"
|
||||
[7]: https://blog.jquery.com/2026/01/17/jquery-4-0-0/ "jQuery 4.0.0 | Official jQuery Blog"
|
||||
[8]: https://jquery.com/download/ "Download jQuery | jQuery"
|
||||
[9]: https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html?utm_source=chatgpt.com "Content Security Policy - OWASP Cheat Sheet Series"
|
||||
[10]: https://www.w3.org/TR/trusted-types/?utm_source=chatgpt.com "Trusted Types"
|
||||
[11]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for?utm_source=chatgpt.com "Content-Security-Policy: require-trusted-types-for directive"
|
||||
[12]: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html?utm_source=chatgpt.com "HTTP Security Response Headers Cheat Sheet"
|
||||
[13]: https://nvd.nist.gov/vuln/detail/cve-2019-11358?utm_source=chatgpt.com "CVE-2019-11358 Detail - NVD"
|
||||
[14]: https://api.jquery.com/text/?utm_source=chatgpt.com ".text() | jQuery API Documentation"
|
||||
[15]: https://api.jquery.com/val/?utm_source=chatgpt.com ".val() | jQuery API Documentation"
|
||||
[16]: https://github.com/jquery/jquery/security/advisories/GHSA-gxr4-xjj5-5px2 "Potential XSS vulnerability in jQuery.htmlPrefilter and related methods · Advisory · jquery/jquery · GitHub"
|
||||
[17]: https://github.com/cure53/DOMPurify?utm_source=chatgpt.com "DOMPurify - a DOM-only, super-fast, uber-tolerant XSS ..."
|
||||
[18]: https://developer.mozilla.org/en-US/docs/Web/API/HTML_Sanitizer_API?utm_source=chatgpt.com "HTML Sanitizer API - MDN Web Docs"
|
||||
[19]: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html?utm_source=chatgpt.com "Cross-Site Request Forgery Prevention Cheat Sheet"
|
||||
[20]: https://api.jquery.com/jQuery.escapeSelector/?utm_source=chatgpt.com "jQuery.escapeSelector()"
|
||||
[21]: https://api.jquery.com/html/?utm_source=chatgpt.com ".html() | jQuery API Documentation"
|
||||
[22]: https://api.jquery.com/append/?utm_source=chatgpt.com ".append() | jQuery API Documentation"
|
||||
[23]: https://nvd.nist.gov/vuln/detail/cve-2020-11023?utm_source=chatgpt.com "CVE-2020-11023 Detail - NVD"
|
||||
[24]: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html?utm_source=chatgpt.com "Cross Site Scripting Prevention - OWASP Cheat Sheet Series"
|
||||
[25]: https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html?utm_source=chatgpt.com "DOM based XSS Prevention Cheat Sheet"
|
||||
[26]: https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity?utm_source=chatgpt.com "Subresource Integrity - Security - MDN Web Docs"
|
||||
[27]: https://www.w3.org/TR/sri-2/?utm_source=chatgpt.com "Subresource Integrity"
|
||||
[28]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP?utm_source=chatgpt.com "Content Security Policy (CSP) - HTTP - MDN Web Docs"
|
||||
[29]: https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API?utm_source=chatgpt.com "Trusted Types API - MDN Web Docs"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,990 @@
|
||||
# React (JavaScript/TypeScript) Web Security Spec (React 19.x, TypeScript 5.x)
|
||||
|
||||
This document is designed as a **security spec** that supports:
|
||||
|
||||
1. **Secure-by-default code generation** for new React code.
|
||||
2. **Security review / vulnerability hunting** in existing React code (passive “notice issues while working” and active “scan the repo and report findings”).
|
||||
|
||||
It is intentionally written as a set of **normative requirements** (“MUST/SHOULD/MAY”) plus **audit rules** (what bad patterns look like, how to detect them, and how to fix/mitigate them).
|
||||
|
||||
---
|
||||
|
||||
## 0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)
|
||||
|
||||
* MUST NOT request, output, log, or commit secrets (API keys, OAuth client secrets, private keys, session cookies, JWTs, signing keys).
|
||||
|
||||
* Frontend note: anything shipped to the browser is observable by end users and attackers (view-source, devtools, proxies); never treat client code or “env vars in the bundle” as secret. ([create-react-app.dev][1])
|
||||
* MUST NOT “fix” security by disabling protections (e.g., turning off CSP to “make it work”, adding `unsafe-inline`/`unsafe-eval` without a documented, constrained plan, disabling CSRF protections when using cookies, widening CORS, skipping sanitization, or “temporary” bypasses that ship). ([OWASP Cheat Sheet Series][2])
|
||||
* MUST provide **evidence-based findings** during audits: cite file paths, code snippets, and configuration values that justify the claim.
|
||||
* MUST treat uncertainty honestly: if a protection might exist in infra (CDN/WAF/reverse proxy), report it as “not visible in app code; verify via runtime headers / edge config”.
|
||||
* MUST assume any data that crosses a trust boundary (URL, storage, network, postMessage, third-party scripts) can be attacker-influenced unless proven otherwise (see §2.1).
|
||||
|
||||
---
|
||||
|
||||
## 1) Operating modes
|
||||
|
||||
### 1.1 Generation mode (default)
|
||||
|
||||
When asked to write new React code or modify existing code:
|
||||
|
||||
* MUST follow every **MUST** requirement in this spec.
|
||||
* SHOULD follow every **SHOULD** requirement unless the user explicitly says otherwise.
|
||||
* MUST prefer safe-by-default APIs and proven libraries over custom security code.
|
||||
* MUST avoid introducing new risky sinks (raw HTML insertion, direct DOM sinks like `innerHTML`, dynamic code execution, untrusted redirects/navigation, third‑party script injection, unsafe token storage, etc.). ([MDN Web Docs][3])
|
||||
|
||||
### 1.2 Passive review mode (always on while editing)
|
||||
|
||||
While working anywhere in a React repo (even if the user did not ask for a security scan):
|
||||
|
||||
* MUST “notice” violations of this spec in touched/nearby code.
|
||||
* SHOULD mention issues as they come up, with a brief explanation + safe fix.
|
||||
|
||||
### 1.3 Active audit mode (explicit scan request)
|
||||
|
||||
When the user asks to “scan”, “audit”, or “hunt for vulns”:
|
||||
|
||||
* MUST systematically search the codebase for violations of this spec.
|
||||
* MUST output findings in a structured format (see §2.3).
|
||||
|
||||
Recommended audit order:
|
||||
|
||||
1. App entrypoints, build tooling (Vite/Webpack/CRA/Next), deployment configs, CDN/static hosting config.
|
||||
2. Secrets & configuration exposure (env vars, runtime config injection, source maps).
|
||||
3. Rendering of untrusted data (XSS/DOM XSS), especially `dangerouslySetInnerHTML`, markdown/HTML renderers, URL attributes.
|
||||
4. Direct DOM usage and dangerous JS execution (`innerHTML`, `eval`, `new Function`, `document.write`, etc.).
|
||||
5. Auth & session patterns (token storage, cookies, CSRF interactions, OAuth flows).
|
||||
6. Network layer (axios/fetch wrappers, dynamic base URLs, credentialed requests, data exfil risks).
|
||||
7. Navigation & redirect handling (open redirects, `window.location`, `target=_blank`, `window.open`).
|
||||
8. Third-party scripts/tags/analytics and integrity controls (CSP, SRI).
|
||||
9. Service worker/PWA behavior (HTTPS, caching rules, update strategy).
|
||||
10. Security headers posture (CSP, clickjacking, nosniff, referrer policy) in app or at the edge. ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
---
|
||||
|
||||
## 2) Definitions and review guidance
|
||||
|
||||
### 2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)
|
||||
|
||||
Examples include:
|
||||
|
||||
* URL-derived data: `window.location`, query params, hash fragments, route params.
|
||||
* Any data from browser storage: `localStorage`, `sessionStorage`, `IndexedDB` (including data previously written by the app—because XSS or extensions can tamper with it). ([OWASP Cheat Sheet Series][4])
|
||||
* Any data from cross-window messaging: `window.postMessage` payloads. ([OWASP Cheat Sheet Series][4])
|
||||
* Any data from remote APIs, webhooks proxied to the client, GraphQL responses, CMS content, feature flag services.
|
||||
* Any persisted user content (profiles, comments, rich text, markdown) rendered in the UI.
|
||||
* Any data produced by third-party scripts or tag managers (treat as untrusted unless strongly controlled). ([OWASP Cheat Sheet Series][5])
|
||||
|
||||
### 2.2 State-changing request (frontend perspective)
|
||||
|
||||
A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook), or initiate privileged actions.
|
||||
|
||||
Frontend-specific note:
|
||||
|
||||
* State changes are often triggered by `fetch/axios` calls or form submissions. If authentication is cookie-based, these calls can be CSRF-relevant (§4 REACT-CSRF-001). ([OWASP Cheat Sheet Series][6])
|
||||
|
||||
### 2.3 Required audit finding format
|
||||
|
||||
For each issue found, output:
|
||||
|
||||
* Rule ID:
|
||||
* Severity: Critical / High / Medium / Low
|
||||
* Location: file path + component/function + line(s)
|
||||
* Evidence: the exact code/config snippet
|
||||
* Impact: what could go wrong, who can exploit it
|
||||
* Fix: safe change (prefer minimal diff)
|
||||
* Mitigation: defense-in-depth if immediate fix is hard
|
||||
* False positive notes: what to verify if uncertain
|
||||
|
||||
---
|
||||
|
||||
## 3) Secure baseline: minimum production configuration (MUST in production)
|
||||
|
||||
This is the smallest “production baseline” that prevents common React frontend misconfigurations.
|
||||
|
||||
### 3.1 Production build and configuration hygiene (MUST)
|
||||
|
||||
* MUST ship a production build (minified, no dev-only overlays/tools, correct mode flags).
|
||||
* MUST ensure build-time configuration does not embed secrets into the shipped JS/HTML/CSS. Build-time “environment variables” are not secret; treat them as public. ([create-react-app.dev][1])
|
||||
* SHOULD treat source maps as sensitive operational artifacts:
|
||||
|
||||
* Either don’t publish them publicly, or publish them only where intended (e.g., behind auth or to an error-reporting provider), because they can reveal code structure and internal URLs.
|
||||
|
||||
### 3.2 Browser-enforced protections (SHOULD, but baseline expectation for modern apps)
|
||||
|
||||
* SHOULD deploy a CSP as defense-in-depth against XSS, and keep it compatible with your React build (avoid `unsafe-inline` and `unsafe-eval` unless strictly necessary and documented). ([OWASP Cheat Sheet Series][2])
|
||||
* SHOULD use Subresource Integrity (SRI) for any third-party script/style loaded from a CDN (or self-host instead). ([MDN Web Docs][7])
|
||||
* SHOULD enable clickjacking defenses via `frame-ancestors` (CSP) and/or `X-Frame-Options`, unless embedding is an explicit product requirement. ([MDN Web Docs][8])
|
||||
|
||||
### 3.3 High-risk features baseline (MUST if used)
|
||||
|
||||
* If rendering any user-provided HTML/markdown/rich text:
|
||||
|
||||
* MUST sanitize before insertion and avoid raw DOM sinks. ([OWASP Cheat Sheet Series][9])
|
||||
* If using service workers / PWA:
|
||||
|
||||
* MUST serve over HTTPS and implement a safe caching/update strategy (service workers are powerful request/response proxies). ([MDN Web Docs][10])
|
||||
|
||||
---
|
||||
|
||||
## 4) Rules (generation + audit)
|
||||
|
||||
Each rule contains: required practice, insecure patterns, detection hints, and remediation.
|
||||
|
||||
### REACT-CONFIG-001: Never embed secrets in the client bundle (env vars are public)
|
||||
|
||||
Severity: Critical (if secrets exposed)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT place secrets in React code, in `public/` assets, or in build-time environment variables intended for client consumption.
|
||||
* MUST assume any value available to the React app at runtime can be extracted by an attacker.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Using build-time env vars for secrets:
|
||||
|
||||
* `process.env.REACT_APP_*` containing private keys or credentials.
|
||||
* `import.meta.env.VITE_*` containing secrets.
|
||||
* Hard-coded secrets in JS/TS, `.env` committed, or secrets in `public/config.json` served to all users.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for:
|
||||
|
||||
* `REACT_APP_`, `VITE_`, `NEXT_PUBLIC_`, `process.env.`, `import.meta.env.`
|
||||
* `apiKey`, `secret`, `token`, `private`, `password`, `client_secret`
|
||||
* Inspect `public/` for runtime config JSON.
|
||||
|
||||
Fix:
|
||||
|
||||
* Move secrets server-side (API, BFF, serverless function).
|
||||
* Use a backend to mint short-lived, scoped tokens if the browser needs to call third-party APIs.
|
||||
|
||||
Notes:
|
||||
|
||||
* CRA explicitly warns not to store secrets and notes env vars are embedded into the build and visible to anyone inspecting files. ([create-react-app.dev][1])
|
||||
* Vite explicitly notes that variables exposed to client code end up in the client bundle and should not contain sensitive info. ([vitejs][11])
|
||||
|
||||
---
|
||||
|
||||
### REACT-XSS-001: Do not use `dangerouslySetInnerHTML` with untrusted content (sanitize or avoid)
|
||||
|
||||
Severity: High (Only if you can prove attacker-controlled HTML reaches it)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST avoid `dangerouslySetInnerHTML` unless absolutely necessary.
|
||||
* If it must be used:
|
||||
|
||||
* MUST sanitize untrusted HTML with a proven sanitizer (e.g., DOMPurify) and an allowlist-oriented configuration.
|
||||
* MUST keep the sanitization logic centralized and heavily reviewed.
|
||||
* SHOULD add a CSP and consider Trusted Types (see REACT-TT-001).
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `<div dangerouslySetInnerHTML={{ __html: userHtml }} />` where `userHtml` is from API/URL/storage.
|
||||
* “Sanitization” done with regexes, ad-hoc stripping, or incomplete allowlists.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Grep: `dangerouslySetInnerHTML`, `__html:`
|
||||
* Trace the origin of the HTML string (API/CMS/URL/localStorage).
|
||||
|
||||
Fix:
|
||||
|
||||
* Replace with safe rendering:
|
||||
|
||||
* Render structured data as React elements/components instead of HTML strings.
|
||||
* If rich text is required, sanitize with DOMPurify (or equivalent) and render the sanitized output.
|
||||
* Add CSP; remove dangerous sinks where possible.
|
||||
|
||||
Notes:
|
||||
|
||||
* React explicitly warns that `dangerouslySetInnerHTML` is dangerous and can introduce XSS if misused. ([React][12])
|
||||
* OWASP explicitly calls out React’s `dangerouslySetInnerHTML` without sanitization as a common framework “escape hatch” pitfall. ([OWASP Cheat Sheet Series][9])
|
||||
* DOMPurify describes itself as an XSS sanitizer for HTML/SVG/MathML. ([GitHub][13])
|
||||
|
||||
---
|
||||
|
||||
### REACT-XSS-002: Rely on React’s escaping-by-default behavior; do not bypass it
|
||||
|
||||
Severity: High (when bypassed)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST render untrusted strings via normal JSX interpolation (`{value}`) and React props, which are escaped by default.
|
||||
* MUST NOT build HTML strings from untrusted data and then inject them into the DOM via any means.
|
||||
* SHOULD treat any “escape hatch” as high risk and require review.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Converting untrusted text into HTML and injecting it:
|
||||
|
||||
* `element.innerHTML = userValue`
|
||||
* `document.write(userValue)`
|
||||
* `insertAdjacentHTML(..., userValue)`
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Grep for DOM sinks: `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `document.write`, `DOMParser`, `createContextualFragment`.
|
||||
|
||||
Fix:
|
||||
|
||||
* Render text content through React (JSX) so it is escaped.
|
||||
* If you truly need HTML, sanitize and apply REACT-XSS-001 + REACT-TT-001.
|
||||
|
||||
Notes:
|
||||
|
||||
* React documentation (JSX) states that React DOM escapes values embedded in JSX before rendering to help prevent injection attacks. ([React][14])
|
||||
|
||||
---
|
||||
|
||||
### REACT-DOM-001: Avoid DOM XSS injection sinks in React code (use safe alternatives)
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST avoid direct DOM injection sinks, even outside React rendering, unless strongly controlled.
|
||||
* If a DOM sink is required:
|
||||
|
||||
* MUST ensure inputs are trusted/validated/sanitized.
|
||||
* SHOULD enforce Trusted Types (REACT-TT-001).
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `someEl.innerHTML = untrusted`
|
||||
* `document.write(untrusted)`
|
||||
* `new DOMParser().parseFromString(untrusted, 'text/html')` followed by insertion
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Grep for: `innerHTML`, `outerHTML`, `document.write`, `DOMParser`, `Range().createContextualFragment`, `insertAdjacentHTML`
|
||||
|
||||
Fix:
|
||||
|
||||
* Prefer:
|
||||
|
||||
* `textContent` for text insertion.
|
||||
* React rendering rather than manual DOM manipulation.
|
||||
* A vetted sanitizer for any required HTML parsing.
|
||||
|
||||
Notes:
|
||||
|
||||
* Trusted Types documentation defines HTML sinks like `Element.innerHTML` and `document.write()` as injection sinks that can execute script when given attacker-controlled input. ([MDN Web Docs][3])
|
||||
* OWASP HTML5 guidance recommends using `textContent` instead of `innerHTML` for assigning untrusted data. ([OWASP Cheat Sheet Series][4])
|
||||
|
||||
---
|
||||
|
||||
### REACT-URL-001: Validate and constrain untrusted URLs used in `href`, `src`, navigation, and redirects
|
||||
|
||||
Severity: High Only when you can prove they are attacker controlled
|
||||
|
||||
Required:
|
||||
|
||||
* MUST treat any URL derived from untrusted input as dangerous.
|
||||
* MUST allowlist schemes and (when applicable) hosts:
|
||||
|
||||
* Typically allow only `https:` (and maybe `http:` for localhost/dev) and relative URLs for in-app navigation.
|
||||
* MUST explicitly block `javascript:` and dangerous `data:` uses unless you have specialized validation and a clear use case.
|
||||
* SHOULD prefer same-site relative paths (e.g., `/settings`) over absolute URLs.
|
||||
* MUST validate “returnTo/next/redirect” parameters (see REACT-REDIRECT-001).
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `<img src={userProvidedUrl}>...` (can be used for tracking / data exfil; also risky if used for scripts/iframes)
|
||||
* `window.location = next`
|
||||
* `navigate(next)` where `next` comes from query params without validation
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for:
|
||||
|
||||
* `href={`, `src={`, `window.location`, `location.href`, `window.open`, `navigate(`, `redirectTo`, `returnTo`, `next=`
|
||||
* Track whether the value is derived from URL/query/storage/API.
|
||||
|
||||
Fix:
|
||||
|
||||
* Implement a shared `safeUrl()` utility:
|
||||
|
||||
* Parse with `new URL(value, base)`
|
||||
* Enforce scheme allowlist and host allowlist (or enforce same-origin)
|
||||
* For redirects: allow only relative paths (starting with `/`) or a strict allowlist of absolute origins.
|
||||
* Fall back to a safe default when validation fails.
|
||||
|
||||
Notes:
|
||||
|
||||
* OWASP explicitly notes React’s `dangerouslySetInnerHTML` risk and also states React cannot safely handle `javascript:` or `data:` URLs without specialized validation. ([OWASP Cheat Sheet Series][9])
|
||||
|
||||
---
|
||||
|
||||
### REACT-MARKUP-001: Markdown / rich text rendering must be configured safely
|
||||
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
|
||||
* MUST assume markdown/rich text can be attacker-controlled if it comes from users or CMS.
|
||||
* MUST ensure raw HTML is not rendered unless sanitized.
|
||||
* SHOULD prefer markdown renderers that:
|
||||
|
||||
* Do not allow raw HTML by default, or
|
||||
* Can be configured to disallow raw HTML, or
|
||||
* Sanitize HTML output before rendering.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Markdown rendering with “raw HTML passthrough” enabled (e.g., options/plugins that allow HTML).
|
||||
* Rendering user-provided SVG/MathML/HTML inline without sanitization.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for common libraries and risky options:
|
||||
|
||||
* `marked`, `markdown-it`, `react-markdown`, `rehype-raw`, `sanitize: false`, `allowDangerousHtml`, etc.
|
||||
* Look for `dangerouslySetInnerHTML` used with “markdown output”.
|
||||
|
||||
Fix:
|
||||
|
||||
* Disable raw HTML passthrough.
|
||||
* Sanitize output with a proven sanitizer (e.g., DOMPurify) before rendering.
|
||||
|
||||
Notes:
|
||||
|
||||
* OWASP XSS guidance emphasizes that framework escape hatches require output encoding and/or HTML sanitization. ([OWASP Cheat Sheet Series][9])
|
||||
|
||||
---
|
||||
|
||||
### REACT-TT-001: Use Trusted Types (with CSP) to harden DOM XSS sinks where feasible
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD consider enabling Trusted Types in report-only mode first, then enforce once violations are addressed.
|
||||
* SHOULD centralize Trusted Types policies and treat them as high-risk code requiring review.
|
||||
* MUST NOT create permissive policies that simply “pass through” untrusted strings.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* A Trusted Types policy that returns the raw string without sanitization for HTML sinks.
|
||||
* Many scattered policies across the codebase (hard to audit).
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for:
|
||||
|
||||
* `trustedTypes.createPolicy`
|
||||
* CSP directives: `require-trusted-types-for`, `trusted-types`
|
||||
* Search for remaining DOM sinks (REACT-DOM-001).
|
||||
|
||||
Fix:
|
||||
|
||||
* Implement a small number of tightly scoped policies:
|
||||
|
||||
* HTML policy uses sanitizer (DOMPurify or equivalent).
|
||||
* Script URL policy uses strict allowlists.
|
||||
* Run in report-only mode, fix violations, then enforce.
|
||||
|
||||
Notes:
|
||||
|
||||
* MDN describes Trusted Types as a way to ensure input is transformed (commonly sanitized) before being passed to injection sinks, and highlights HTML sinks (`innerHTML`, `document.write`) and JS URL sinks (`script.src`). ([MDN Web Docs][3])
|
||||
* The W3C Trusted Types spec frames this as reducing DOM XSS risk by locking down sinks to typed values created by reviewed policies. ([W3C][15])
|
||||
|
||||
---
|
||||
|
||||
### REACT-CSP-001: Deploy and maintain a CSP as defense-in-depth (especially when rendering untrusted content)
|
||||
|
||||
Severity: Medium to High
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD deploy CSP in production; MUST do so for apps that render untrusted content or integrate third-party scripts.
|
||||
* SHOULD avoid `unsafe-inline` and `unsafe-eval` when possible.
|
||||
* SHOULD use CSP nonces/hashes for inline scripts if needed, and keep policy realistic.
|
||||
* SHOULD use CSP to require/encourage SRI where appropriate.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* No CSP at all on the app shell (SPA entry HTML).
|
||||
* CSP that relies on `unsafe-inline`/`unsafe-eval` broadly without justification.
|
||||
* `script-src *` or overly broad sources.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Look for CSP configuration:
|
||||
|
||||
* Server/CDN config, headers in `index.html` responses, or framework config.
|
||||
* If absent in repo, mark as “verify at edge”.
|
||||
|
||||
Fix:
|
||||
|
||||
* Add CSP via HTTP response headers (preferred).
|
||||
* Start with report-only to reduce breakage, then enforce.
|
||||
|
||||
Notes:
|
||||
|
||||
* OWASP describes CSP as “defense in depth” against XSS and notes it can help enforce SRI even on static sites, but should not be the only defense. ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
---
|
||||
|
||||
### REACT-SRI-001: Use Subresource Integrity (SRI) for third-party scripts and styles (or self-host)
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* MUST treat third-party JS as equivalent to running arbitrary code in your origin.
|
||||
* If loading from a CDN or third party:
|
||||
|
||||
* SHOULD use SRI (`integrity=...`) and `crossorigin` where applicable.
|
||||
* SHOULD pin exact versions (avoid “latest” URLs).
|
||||
* SHOULD prefer self-hosting for critical code.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `<script src="https://cdn.example.com/lib/latest.js"></script>` with no integrity.
|
||||
* Tag managers that dynamically load arbitrary scripts without governance.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search in `public/index.html`, templates, or SSR wrappers for:
|
||||
|
||||
* `<script src=`, `<link rel="stylesheet" href=`
|
||||
* Tag manager snippets (GTM, Segment, etc.)
|
||||
* Identify scripts loaded dynamically in runtime JS.
|
||||
|
||||
Fix:
|
||||
|
||||
* Add SRI hashes for stable third-party assets or self-host.
|
||||
* Apply governance controls for tag managers (see REACT-3P-001).
|
||||
|
||||
Notes:
|
||||
|
||||
* MDN describes SRI as a security feature enabling browsers to verify fetched resources (e.g., from a CDN) haven’t been manipulated by checking a cryptographic hash. ([MDN Web Docs][7])
|
||||
* OWASP CSP guidance notes CSP can enforce SRI and is useful even on static sites. ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
---
|
||||
|
||||
### REACT-3P-001: Third-party JavaScript and tag managers must be minimized and governed
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST minimize third-party scripts and treat each as a supply-chain risk.
|
||||
* MUST know exactly what third-party JS executes in your origin and why.
|
||||
* SHOULD implement governance:
|
||||
|
||||
* Review and pin versions (or mirror in-house).
|
||||
* Restrict data access (data-layer approach).
|
||||
* Use SRI and CSP; consider sandboxing untrusted UI in iframes where possible.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Unreviewed analytics/ads scripts running with full access to DOM, cookies, storage, and user data.
|
||||
* Tag managers that can be changed by non-engineering roles with no change control.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for common vendor snippets in HTML/JS:
|
||||
|
||||
* GTM, Segment, Hotjar, FullStory, etc.
|
||||
* Look for dynamic script insertion:
|
||||
|
||||
* `document.createElement('script')`, `.src = ...`, `.appendChild(script)`
|
||||
|
||||
Fix:
|
||||
|
||||
* Reduce to only necessary vendors.
|
||||
* Where feasible:
|
||||
|
||||
* Self-host or mirror scripts.
|
||||
* Use SRI.
|
||||
* Limit data exposure via a controlled data layer.
|
||||
|
||||
Notes:
|
||||
|
||||
* OWASP notes third-party JS server compromise can inject malicious JS, and highlights risks like arbitrary code execution and disclosure of sensitive info to third parties. ([OWASP Cheat Sheet Series][5])
|
||||
|
||||
---
|
||||
|
||||
### REACT-AUTH-001: Token and session handling must be resilient to XSS (avoid sensitive storage in Web Storage)
|
||||
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD avoid storing session identifiers or long-lived tokens in `localStorage` (and generally in Web Storage) because XSS can exfiltrate them.
|
||||
* If tokens must exist client-side:
|
||||
|
||||
* SHOULD prefer in-memory storage with short lifetimes and refresh mechanisms.
|
||||
* MUST scope and rotate tokens; avoid long-lived bearer tokens in persistent storage.
|
||||
* SHOULD prefer HTTPOnly cookies for session tokens when possible (requires CSRF strategy: see REACT-CSRF-001).
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `localStorage.setItem('token', ...)` / `sessionStorage.setItem('token', ...)` for auth tokens.
|
||||
* Persisting refresh tokens in `localStorage`.
|
||||
* Treating data from Web Storage as trusted.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Grep for: `localStorage.`, `sessionStorage.`, `setItem(`, `getItem(`, `token`, `jwt`, `refresh`
|
||||
* Search auth code for “remember me” storing tokens persistently.
|
||||
|
||||
Fix:
|
||||
|
||||
* Move to HTTPOnly cookies (server change) + CSRF protections, or use short-lived in-memory tokens.
|
||||
* Reduce token scope and lifetime.
|
||||
|
||||
Notes:
|
||||
|
||||
* OWASP HTML5 guidance recommends avoiding sensitive info and session identifiers in local storage and warns that a single XSS can steal all data in Web Storage. ([OWASP Cheat Sheet Series][4])
|
||||
* OAuth browser-based apps guidance discusses that tokens stored in persistent browser storage like localStorage can be accessible to malicious JS (e.g., via XSS). ([IETF Datatracker][16])
|
||||
|
||||
---
|
||||
|
||||
### REACT-CSRF-001: Cookie-authenticated, state-changing requests MUST be CSRF-protected
|
||||
|
||||
Severity: High
|
||||
|
||||
NOTE: If the application does not use cookie based auth (using Authentication header for example), then CSRF is not a concern.
|
||||
|
||||
Required:
|
||||
|
||||
* If the app relies on cookies for authentication:
|
||||
|
||||
* MUST protect state-changing requests (POST/PUT/PATCH/DELETE) against CSRF.
|
||||
* SHOULD include a CSRF token mechanism (synchronizer token or double-submit cookie) or other robust pattern appropriate to the backend.
|
||||
* SHOULD use SameSite cookies as defense-in-depth, not as the sole defense.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `fetch('/api/transfer', { method: 'POST', credentials: 'include' })` with no CSRF token/header, relying only on cookies.
|
||||
* Using GET for state-changing operations.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Enumerate state-changing network calls and check:
|
||||
|
||||
* Is `credentials: 'include'` or `withCredentials: true` used?
|
||||
* Is a CSRF token header included (e.g., `X-CSRF-Token`)?
|
||||
* Search for “csrf” utilities; if absent, treat as suspicious.
|
||||
|
||||
Fix:
|
||||
|
||||
* Add CSRF token flow:
|
||||
|
||||
* Fetch token from a safe endpoint and attach to state-changing requests.
|
||||
* Validate server-side.
|
||||
* Keep SameSite cookies and Origin/Referer validation as defense-in-depth.
|
||||
|
||||
Notes:
|
||||
|
||||
* OWASP CSRF guidance explains SameSite behavior (Lax/Strict/None) as a defense-in-depth technique and why Lax is often the usability/security balance, but it is not a complete substitute for CSRF protections. ([OWASP Cheat Sheet Series][6])
|
||||
|
||||
---
|
||||
|
||||
### REACT-AUTHZ-001: Do not rely on frontend-only authorization
|
||||
|
||||
Severity: High (only if used as primary protection)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST treat all frontend authorization checks as UX only.
|
||||
* MUST enforce authorization on the server for any protected resource or action.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* “Protected” actions hidden in UI but callable by API without server checks.
|
||||
* Client checks like `if (user.isAdmin) { showAdminPanel(); }` with no server-side enforcement.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Look for UI gating around sensitive actions and verify server endpoints enforce authorization.
|
||||
* In a frontend-only audit, report as “client checks are not security; verify backend”.
|
||||
|
||||
Fix:
|
||||
|
||||
* Add/confirm server-side authorization checks.
|
||||
* Keep frontend gating only as convenience.
|
||||
|
||||
Notes:
|
||||
|
||||
* This is a general web app security property; React cannot protect server resources by itself.
|
||||
|
||||
---
|
||||
|
||||
### REACT-NET-001: Prevent data exfiltration and credential leakage via dynamic outbound requests
|
||||
|
||||
Severity: Medium to High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST avoid making authenticated requests to attacker-controlled origins.
|
||||
* SHOULD avoid allowing user input to control request destination (scheme/host/port).
|
||||
* SHOULD centralize network clients (fetch/axios) with:
|
||||
|
||||
* fixed `baseURL` (or strict allowlist),
|
||||
* strict handling of redirects,
|
||||
* explicit `credentials` usage.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `fetch(userProvidedUrl, { credentials: 'include' })`
|
||||
* `axios.create({ baseURL: userProvidedBase })`
|
||||
* “URL fetch/preview” features in the client that hit arbitrary domains with sensitive headers.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `fetch(` / `axios(` where the first argument or `baseURL` is derived from:
|
||||
|
||||
* query params, localStorage, API responses, postMessage
|
||||
* Search for `credentials: 'include'`, `withCredentials: true`.
|
||||
|
||||
Fix:
|
||||
|
||||
* Enforce destination allowlists; disallow cross-origin requests unless explicitly required.
|
||||
* Strip credentials/Authorization headers for any non-allowlisted destination.
|
||||
|
||||
Notes:
|
||||
|
||||
* Even if the browser limits some cross-origin behavior, leaking tokens/headers to untrusted endpoints is still a common failure mode.
|
||||
|
||||
---
|
||||
|
||||
### REACT-REDIRECT-001: Prevent open redirects and untrusted navigation
|
||||
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
|
||||
* MUST validate redirect/navigation targets derived from untrusted input (`next`, `returnTo`, `redirect`).
|
||||
* SHOULD only allow same-site relative paths, or a strict allowlist of trusted origins for absolute URLs.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `window.location.href = new URLSearchParams(location.search).get('next')`
|
||||
* `navigate(next)` where `next` comes from query params.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for: `next`, `returnTo`, `redirect`, `window.location`, `navigate(`
|
||||
* Trace origin of the redirect target.
|
||||
|
||||
Fix:
|
||||
|
||||
* Only allow relative paths (`/^\/[^\s]*$/`) or allowlisted origins.
|
||||
* Fall back to a safe default (e.g., `/`) when invalid.
|
||||
|
||||
Notes:
|
||||
|
||||
* Open redirects are frequently used in phishing and can undermine SSO/OAuth flows.
|
||||
|
||||
---
|
||||
|
||||
### REACT-SW-001: Service workers are high-privilege; require HTTPS and safe caching/update rules
|
||||
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
|
||||
* MUST serve service workers over HTTPS (except `localhost` dev), and deploy only in secure contexts.
|
||||
* MUST avoid caching sensitive authenticated API responses unless explicitly designed and threat-modeled.
|
||||
* SHOULD implement safe update strategy (prompt reload, versioned caches, remove old caches on activate).
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Registering a service worker for an authenticated app and caching “everything” indiscriminately.
|
||||
* Long-lived caches containing PII or user-specific content shared across accounts.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for:
|
||||
|
||||
* `navigator.serviceWorker.register`
|
||||
* `workbox`, `precacheAndRoute`, custom `fetch` handlers
|
||||
* Inspect caching patterns (`caches.open`, `cache.put`, `respondWith`).
|
||||
|
||||
Fix:
|
||||
|
||||
* Restrict caching to static assets only (JS/CSS/images) unless you have a designed offline model.
|
||||
* Ensure cache keys are user-scoped if user-specific data must be cached.
|
||||
* Provide a clear update mechanism.
|
||||
|
||||
Notes:
|
||||
|
||||
* MDN notes service workers require HTTPS for security reasons and act like a proxy for requests/responses. ([MDN Web Docs][10])
|
||||
* “Secure contexts” exist to prevent MITM attackers from accessing powerful APIs; service workers are an example of such a powerful feature. ([MDN Web Docs][18])
|
||||
|
||||
---
|
||||
|
||||
### REACT-HEADERS-001: Ensure essential security headers are set for the React app shell (app or edge)
|
||||
|
||||
Severity: Medium
|
||||
|
||||
Required (typical SPA served from an origin):
|
||||
|
||||
* SHOULD set:
|
||||
|
||||
* CSP (`Content-Security-Policy`)
|
||||
* `X-Content-Type-Options: nosniff`
|
||||
* Clickjacking protection (`frame-ancestors` in CSP and/or `X-Frame-Options`)
|
||||
* `Referrer-Policy`
|
||||
* `Permissions-Policy` as appropriate
|
||||
* MUST ensure these are set somewhere (CDN/edge/server), even if not in repo.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* No security headers anywhere (app or edge).
|
||||
* CSP missing on apps that render untrusted content or use third-party scripts.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Check server/CDN config in repo (nginx, Cloudflare, Vercel config, etc.).
|
||||
* If absent, flag as “verify at runtime/edge”.
|
||||
|
||||
Fix:
|
||||
|
||||
* Set headers centrally at the edge.
|
||||
* Keep CSP realistic and iterative (report-only → enforce).
|
||||
|
||||
Notes:
|
||||
|
||||
* MDN clickjacking guidance discusses defenses including `X-Frame-Options` and CSP `frame-ancestors`. ([MDN Web Docs][8])
|
||||
* OWASP CSP guidance explains delivery via response headers and recommends headers as the preferred mechanism. ([OWASP Cheat Sheet Series][2])
|
||||
|
||||
---
|
||||
|
||||
### REACT-POSTMSG-001: `postMessage` must validate origin and treat payload as untrusted data
|
||||
|
||||
Severity: Medium to High (depends on what messages can do)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST specify exact `targetOrigin` when sending messages (not `*`) unless there is a strict reason.
|
||||
* MUST validate `event.origin` on receipt and validate message shape.
|
||||
* MUST NOT evaluate message data as code or insert it into the DOM as HTML.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `window.postMessage(data, '*')` to unknown targets.
|
||||
* Receiving:
|
||||
|
||||
* `window.addEventListener('message', (e) => { eval(e.data) })`
|
||||
* `element.innerHTML = e.data`
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search: `postMessage(`, `addEventListener('message'`
|
||||
* Check for origin checks and safe handling.
|
||||
|
||||
Fix:
|
||||
|
||||
* Add strict origin allowlists and schema validation (e.g., zod).
|
||||
* Treat message payload strictly as data; render safely via React.
|
||||
|
||||
Notes:
|
||||
|
||||
* OWASP HTML5 guidance recommends specifying expected origin for `postMessage`, checking sender origin, validating data, and avoiding eval/innerHTML with message content. ([OWASP Cheat Sheet Series][4])
|
||||
|
||||
---
|
||||
|
||||
### REACT-FILE-001: File uploads and previews must not create client-side active content vulnerabilities
|
||||
|
||||
Severity: Medium (can be High if stored-XSS possible)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST treat user-uploaded files and previews as potentially malicious.
|
||||
* MUST NOT render uploaded HTML/SVG/other active content inline unless sanitized and explicitly required.
|
||||
* SHOULD validate file types client-side for UX, but MUST rely on server-side validation for security.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Rendering user-uploaded HTML as content.
|
||||
* Inline rendering of untrusted SVG/HTML via `dangerouslySetInnerHTML` or `<iframe srcdoc=...>` without sanitization.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for upload components and preview logic:
|
||||
|
||||
* `input type="file"`, `FileReader`, `URL.createObjectURL`, `<iframe>`, `<object>`, `<embed>`.
|
||||
* Trace where uploaded content is later displayed.
|
||||
|
||||
Fix:
|
||||
|
||||
* Restrict accepted types, sanitize where needed, and prefer download/attachment flows for risky types.
|
||||
* Ensure server enforces the real policy (type checking, renaming, scanning, storing outside webroot).
|
||||
|
||||
Notes:
|
||||
|
||||
* OWASP file upload guidance highlights allowlisting extensions, validating file type, generating filenames, limiting size, storing outside webroot, and considering “client-side active content (XSS, CSRF, etc.)” when files are publicly retrievable. ([OWASP Cheat Sheet Series][19])
|
||||
|
||||
---
|
||||
|
||||
### REACT-SUPPLY-001: Dependency and supply-chain hygiene (frontend + build tooling)
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* MUST use a lockfile and enforce reproducible installs in CI.
|
||||
* SHOULD regularly audit dependencies and respond quickly to advisories for:
|
||||
|
||||
* React, react-dom, router libs, build tooling (Vite/Webpack), sanitizers, auth libs, etc.
|
||||
* SHOULD reduce exposure to install-time script attacks and typosquatting risk.
|
||||
|
||||
Audit focus:
|
||||
|
||||
* CI should use `npm ci` (or Yarn frozen lockfile / pnpm equivalent) to prevent drift.
|
||||
* Use vulnerability scanning (`npm audit`, GitHub Dependabot/alerts, etc.).
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* No lockfile or lockfile ignored in CI.
|
||||
* `npm install` in CI producing non-reproducible builds.
|
||||
* Unpinned or unreviewed high-risk deps; sudden major updates without review.
|
||||
* Blindly running install scripts from third-party packages.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Check for lockfiles: `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`.
|
||||
* Check CI scripts for `npm install` vs `npm ci`.
|
||||
* Search for `postinstall` scripts and suspicious build steps.
|
||||
|
||||
Fix:
|
||||
|
||||
* Use lockfile and enforce it in CI (e.g., `npm ci`).
|
||||
* Run audits regularly; pin/upgrade responsibly.
|
||||
* Consider restricting install scripts where feasible.
|
||||
|
||||
Notes:
|
||||
|
||||
* npm docs describe `npm audit` as submitting the project dependency tree to the registry to receive a report of known vulnerabilities and (optionally) applying remediations via `npm audit fix`, while noting some vulns require manual review. ([npm Docs][20])
|
||||
* npm docs describe `npm ci` as intended for automated/CI environments, requiring an existing lockfile and failing if `package.json` and lockfile do not match. ([npm Docs][21])
|
||||
* OWASP NPM security guidance recommends enforcing the lockfile and explicitly calls out `npm ci` / `yarn install --frozen-lockfile` to abort on inconsistencies, and highlights the risk of install-time scripts and the option to use `--ignore-scripts` to reduce attack surface. ([OWASP Cheat Sheet Series][22])
|
||||
|
||||
---
|
||||
|
||||
## 5) Practical scanning heuristics (how to “hunt”)
|
||||
|
||||
When actively scanning, use these high-signal patterns:
|
||||
|
||||
* Raw HTML / XSS escape hatches:
|
||||
|
||||
* `dangerouslySetInnerHTML`, `__html:`
|
||||
* Markdown HTML passthrough flags: `rehype-raw`, `allowDangerousHtml`, `sanitize: false`
|
||||
* DOM XSS sinks:
|
||||
|
||||
* `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `document.write`, `DOMParser`, `createContextualFragment`
|
||||
* Dangerous JS execution:
|
||||
|
||||
* `eval(`, `new Function(`, `setTimeout("`, `setInterval("`
|
||||
* Untrusted URL injection / navigation:
|
||||
|
||||
* `href={` / `src={` with untrusted values
|
||||
* `window.location`, `location.href`, `window.open`, `navigate(`
|
||||
* Query params: `next`, `returnTo`, `redirect`
|
||||
* Token/session risk:
|
||||
|
||||
* `localStorage.setItem`, `sessionStorage.setItem`, `getItem(` with `token`, `jwt`, `refresh`
|
||||
* Cookie/CSRF coupling:
|
||||
|
||||
* `credentials: 'include'`, `withCredentials: true` on state-changing requests without CSRF headers
|
||||
* Third-party scripts:
|
||||
|
||||
* `<script src=...>` in `public/index.html`
|
||||
* Tag manager snippets and dynamic script insertion
|
||||
* Service workers:
|
||||
|
||||
* `navigator.serviceWorker.register`, Workbox usage, custom `fetch` handlers
|
||||
* postMessage:
|
||||
|
||||
* `postMessage(` with `*`, missing `event.origin` checks
|
||||
* Supply chain:
|
||||
|
||||
* Missing lockfile, CI uses `npm install`, no audit step, risky postinstall scripts
|
||||
|
||||
Always try to confirm:
|
||||
|
||||
* data origin (untrusted vs trusted)
|
||||
* sink type (React escape hatch vs DOM sink vs navigation vs storage)
|
||||
* protective controls present (sanitization, allowlists, CSP/Trusted Types, CSRF tokens, headers, governance)
|
||||
|
||||
---
|
||||
|
||||
## 6) Sources (accessed 2026-01-26)
|
||||
|
||||
Primary React documentation:
|
||||
|
||||
* React 19 stable announcement — `https://react.dev/blog/2024/12/05/react-19` ([React][23])
|
||||
* React DOM docs: `dangerouslySetInnerHTML` warning — `https://react.dev/reference/react-dom/components/common#dangerouslysetting-the-inner-html` ([React][12])
|
||||
* React (legacy) JSX escaping statement — `https://legacy.reactjs.org/docs/introducing-jsx.html` ([React][14])
|
||||
|
||||
OWASP Cheat Sheet Series:
|
||||
|
||||
* Cross Site Scripting Prevention (framework escape hatches; React `dangerouslySetInnerHTML`; URL validation notes) — `https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][9])
|
||||
* Content Security Policy — `https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][2])
|
||||
* Cross-Site Request Forgery Prevention — `https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][6])
|
||||
* HTML5 Security (Web Storage, postMessage, tabnabbing, sandboxed frames) — `https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][4])
|
||||
* Third Party JavaScript Management — `https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Javascript_Management_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][5])
|
||||
* File Upload — `https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][19])
|
||||
* NPM Security best practices — `https://cheatsheetseries.owasp.org/cheatsheets/NPM_Security_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][22])
|
||||
|
||||
Browser / platform references (MDN, W3C):
|
||||
|
||||
* Trusted Types API — `https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API` ([MDN Web Docs][3])
|
||||
* W3C Trusted Types spec — `https://www.w3.org/TR/trusted-types/` ([W3C][15])
|
||||
* Subresource Integrity — `https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity` ([MDN Web Docs][7])
|
||||
* Clickjacking defenses overview — `https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Clickjacking` ([MDN Web Docs][8])
|
||||
* Using Service Workers (HTTPS requirement; proxy-like behavior) — `https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers` ([MDN Web Docs][10])
|
||||
* Secure contexts (powerful APIs restricted to HTTPS) — `https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Secure_Contexts` ([MDN Web Docs][18])
|
||||
* Link `rel` values (noopener/noreferrer) — `https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel` ([MDN Web Docs][17])
|
||||
|
||||
Build tooling / env exposure references:
|
||||
|
||||
* Create React App env variables warning — `https://create-react-app.dev/docs/adding-custom-environment-variables/` ([create-react-app.dev][1])
|
||||
* Vite env variables security notes — `https://vite.dev/guide/env-and-mode` ([vitejs][11])
|
||||
|
||||
Auth/token storage guidance:
|
||||
|
||||
* OAuth 2.0 for Browser-Based Apps (token storage discussion) — `https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps` ([IETF Datatracker][16])
|
||||
|
||||
Dependency tooling references:
|
||||
|
||||
* npm audit docs — `https://docs.npmjs.com/cli/v10/commands/npm-audit/` ([npm Docs][20])
|
||||
* npm ci docs — `https://docs.npmjs.com/cli/v10/commands/npm-ci/` ([npm Docs][21])
|
||||
|
||||
Sanitizer reference:
|
||||
|
||||
* DOMPurify — `https://github.com/cure53/DOMPurify` ([GitHub][13])
|
||||
|
||||
[1]: https://create-react-app.dev/docs/adding-custom-environment-variables/ "Adding Custom Environment Variables | Create React App"
|
||||
[2]: https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html "Content Security Policy - OWASP Cheat Sheet Series"
|
||||
[3]: https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API "Trusted Types API - Web APIs | MDN"
|
||||
[4]: https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html "HTML5 Security - OWASP Cheat Sheet Series"
|
||||
[5]: https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Javascript_Management_Cheat_Sheet.html "Third Party Javascript Management - OWASP Cheat Sheet Series"
|
||||
[6]: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html "Cross-Site Request Forgery Prevention - OWASP Cheat Sheet Series"
|
||||
[7]: https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity "Subresource Integrity - Security | MDN"
|
||||
[8]: https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Clickjacking "Clickjacking - Security | MDN"
|
||||
[9]: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html "Cross Site Scripting Prevention - OWASP Cheat Sheet Series"
|
||||
[10]: https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers "Using Service Workers - Web APIs | MDN"
|
||||
[11]: https://vite.dev/guide/env-and-mode "Env Variables and Modes | Vite"
|
||||
[12]: https://react.dev/reference/react-dom/components/common "Common components (e.g. <div>) – React"
|
||||
[13]: https://github.com/cure53/DOMPurify "GitHub - cure53/DOMPurify: DOMPurify - a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG. DOMPurify works with a secure default, but offers a lot of configurability and hooks. Demo:"
|
||||
[14]: https://legacy.reactjs.org/docs/introducing-jsx.html "Introducing JSX – React"
|
||||
[15]: https://www.w3.org/TR/trusted-types/ "Trusted Types"
|
||||
[16]: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps "
|
||||
|
||||
draft-ietf-oauth-browser-based-apps-26
|
||||
|
||||
"
|
||||
[17]: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel "HTML attribute: rel - HTML | MDN"
|
||||
[18]: https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Secure_Contexts "Secure contexts - Security | MDN"
|
||||
[19]: https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html "File Upload - OWASP Cheat Sheet Series"
|
||||
[20]: https://docs.npmjs.com/cli/v10/commands/npm-audit "npm-audit | npm Docs"
|
||||
[21]: https://docs.npmjs.com/cli/v10/commands/npm-ci "npm-ci | npm Docs"
|
||||
[22]: https://cheatsheetseries.owasp.org/cheatsheets/NPM_Security_Cheat_Sheet.html "NPM Security - OWASP Cheat Sheet Series"
|
||||
[23]: https://react.dev/blog/2024/12/05/react-19 "React v19 – React"
|
||||
@@ -0,0 +1,791 @@
|
||||
# Vue.js Web Security Spec (Vue 3.x, TypeScript/JavaScript, common tooling: Vite)
|
||||
|
||||
This document is designed as a **security spec** that supports:
|
||||
|
||||
1. **Secure-by-default code generation** for new Vue code.
|
||||
2. **Security review / vulnerability hunting** in existing Vue code (passive “notice issues while working” and active “scan the repo and report findings”).
|
||||
|
||||
It is intentionally written as a set of **normative requirements** (“MUST/SHOULD/MAY”) plus **audit rules** (what bad patterns look like, how to detect them, and how to fix/mitigate them).
|
||||
|
||||
---
|
||||
|
||||
## 0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)
|
||||
|
||||
* MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session cookies, auth tokens).
|
||||
* MUST NOT “fix” security by disabling protections (e.g., weakening CSP, turning on unsafe template compilation, using `v-html` as a shortcut, bypassing backend auth, or “just store the token in localStorage”).
|
||||
* MUST provide **evidence-based findings** during audits: cite file paths, code snippets, and configuration values that justify the claim.
|
||||
* MUST treat uncertainty honestly: if a protection might exist at the edge (CDN, reverse proxy, WAF, server headers), report it as “not visible in repo; verify runtime/infra config”.
|
||||
* MUST remember the frontend trust model: **any code shipped to browsers is attacker-readable and attacker-modifiable**. Secrets and “security enforcement” cannot rely on frontend-only logic.
|
||||
|
||||
---
|
||||
|
||||
## 1) Operating modes
|
||||
|
||||
### 1.1 Generation mode (default)
|
||||
|
||||
When asked to write new Vue code or modify existing code:
|
||||
|
||||
* MUST follow every **MUST** requirement in this spec.
|
||||
* SHOULD follow every **SHOULD** requirement unless the user explicitly says otherwise.
|
||||
* MUST prefer safe-by-default framework features and proven libraries over custom security code.
|
||||
* MUST avoid introducing new risky sinks (runtime template compilation, `v-html` / `innerHTML`, unsafe URL navigation, dynamic script injection, etc.). ([Vue.js][1])
|
||||
|
||||
### 1.2 Passive review mode (always on while editing)
|
||||
|
||||
While working anywhere in a Vue repo (even if the user did not ask for a security scan):
|
||||
|
||||
* MUST “notice” violations of this spec in touched/nearby code.
|
||||
* SHOULD mention issues as they come up, with a brief explanation + safe fix.
|
||||
|
||||
### 1.3 Active audit mode (explicit scan request)
|
||||
|
||||
When the user asks to “scan”, “audit”, or “hunt for vulns”:
|
||||
|
||||
* MUST systematically search the codebase for violations of this spec.
|
||||
* MUST output findings in a structured format (see §2.3).
|
||||
|
||||
Recommended audit order:
|
||||
|
||||
1. Build/deploy entrypoints and hosting config (Docker, CI, static hosting, SSR server).
|
||||
2. Secrets exposure (env usage, `.env*`, hard-coded keys). ([vitejs][2])
|
||||
3. XSS surface: templates, `v-html` / `innerHTML`, URL/style injection, DOM APIs. ([Vue.js][1])
|
||||
4. Auth/session handling in the browser (token storage, credentialed requests, CSRF integration). ([Vue.js][1])
|
||||
5. Routing/navigation (open redirects, “return_to/next”, unsafe external navigation). ([Vue.js][1])
|
||||
6. Third-party scripts and content (CDN assets, analytics, widgets, iframes). ([Vue.js][1])
|
||||
7. Security headers and browser hardening expectations (CSP, clickjacking). ([Vue.js][1])
|
||||
8. SSR-specific concerns (state serialization, template boundaries) when applicable. ([Vue.js][1])
|
||||
|
||||
---
|
||||
|
||||
## 2) Definitions and review guidance
|
||||
|
||||
### 2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)
|
||||
|
||||
In a Vue app, untrusted input includes (non-exhaustive):
|
||||
|
||||
* Anything from APIs: `fetch`, `axios`, GraphQL responses, webhooks, third-party SDKs.
|
||||
* Router-controlled data: `route.params`, `route.query`, `route.hash`, and anything derived from `window.location`.
|
||||
* User-controlled persisted content: DB-backed content displayed in the UI (comments, profiles, CMS content).
|
||||
* Browser-controlled storage: `localStorage`, `sessionStorage`, `IndexedDB`.
|
||||
* Cross-window messages: `postMessage` inputs.
|
||||
* Anything that can be influenced by an attacker through DOM clobbering or injected HTML (especially if Vue is mounted onto non-sterile DOM). ([Vue.js][1])
|
||||
|
||||
### 2.2 State-changing action (frontend perspective)
|
||||
|
||||
An action is state-changing if it can:
|
||||
|
||||
* Create/update/delete data via API calls.
|
||||
* Change authentication/session state (login, logout, refresh token).
|
||||
* Trigger privileged operations (payments, admin actions).
|
||||
* Cause side effects (sending emails, triggering webhooks, changing account settings).
|
||||
|
||||
### 2.3 Required audit finding format
|
||||
|
||||
For each issue found, output:
|
||||
|
||||
* Rule ID:
|
||||
* Severity: Critical / High / Medium / Low
|
||||
* Location: file path + component/function + line(s)
|
||||
* Evidence: the exact code/config snippet
|
||||
* Impact: what could go wrong, who can exploit it
|
||||
* Fix: safe change (prefer minimal diff)
|
||||
* Mitigation: defense-in-depth if immediate fix is hard
|
||||
* False positive notes: what to verify if uncertain
|
||||
|
||||
---
|
||||
|
||||
## 3) Secure baseline: minimum production configuration (MUST in production)
|
||||
|
||||
This is the smallest “production baseline” that prevents common Vue/front-end misconfigurations.
|
||||
|
||||
* MUST ship a **production build** (not a development build or dev server). ([Vue.js][3])
|
||||
* MUST NOT ship secrets in frontend bundles; treat all client-exposed env variables as public. ([vitejs][2])
|
||||
* MUST NOT render non-trusted templates or allow user-provided Vue templates (equivalent to arbitrary JS execution). ([Vue.js][1])
|
||||
* SHOULD avoid raw HTML injection (`v-html`, `innerHTML`) unless content is trusted or strongly sandboxed. ([Vue.js][1])
|
||||
* SHOULD deploy baseline security headers (especially CSP and clickjacking defenses) at the server/CDN layer. ([OWASP Cheat Sheet Series][4])
|
||||
* SHOULD use safe auth patterns (prefer HttpOnly cookies for session tokens; coordinate with backend on CSRF). ([Vue.js][1])
|
||||
|
||||
---
|
||||
|
||||
## 4) Rules (generation + audit)
|
||||
|
||||
Each rule contains: required practice, insecure patterns, detection hints, and remediation.
|
||||
|
||||
### VUE-DEPLOY-001: Do not run dev/preview servers in production
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT deploy the Vite/Vue dev server (`vite`, `npm run dev`, HMR) as the production server.
|
||||
* MUST NOT use `vite preview` as a production server. ([vitejs][5])
|
||||
* MUST build (`vite build`) and serve the built assets using a production-grade static server/CDN, or a production SSR server if you are doing SSR. ([vitejs][6])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Docker/Procfile/systemd running `vite`, `npm run dev`, or `vite preview` as the production entrypoint.
|
||||
* Publicly exposed HMR endpoints.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search: `vite`, `npm run dev`, `pnpm dev`, `yarn dev`, `vite preview`, `vue-cli-service serve`.
|
||||
* Check Docker `CMD`, `ENTRYPOINT`, CI deploy scripts, platform config.
|
||||
|
||||
Fix:
|
||||
|
||||
* Build artifacts with `vite build`.
|
||||
* Serve `dist/` with hardened hosting (CDN/static server) or integrate into your backend server as static assets.
|
||||
|
||||
Notes:
|
||||
|
||||
* Using dev/preview servers locally is fine; only flag if it is the production entrypoint.
|
||||
|
||||
---
|
||||
|
||||
### VUE-DEPLOY-002: Use Vue production builds and keep devtools off in production
|
||||
|
||||
Severity: Medium (High if production devtools/debug hooks are enabled)
|
||||
|
||||
Required:
|
||||
|
||||
* If loading Vue from CDN/self-host without a bundler, MUST use the `.prod.js` builds in production. ([Vue.js][3])
|
||||
* SHOULD ensure production bundles do not enable Vue devtools in production builds, and SHOULD not intentionally enable production devtools flags. ([Vue.js][7])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Production includes development build artifacts.
|
||||
* Explicitly enabling production devtools/diagnostic hooks.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search HTML for `vue.global.js` / non-`.prod.js` variants when using CDN builds.
|
||||
* Search build config for Vue feature flags like `__VUE_PROD_DEVTOOLS__`. ([Vue.js][7])
|
||||
|
||||
Fix:
|
||||
|
||||
* Switch to production build artifacts and ensure compile-time flags are configured for production.
|
||||
|
||||
---
|
||||
|
||||
### VUE-SECRETS-001: Never ship secrets in frontend code or env variables
|
||||
|
||||
Severity: High (Critical if real credentials are exposed)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST treat all frontend code and configuration as public.
|
||||
* MUST NOT embed secrets in:
|
||||
|
||||
* source code
|
||||
* `.env` files committed to repo
|
||||
* `import.meta.env.*` variables included in the bundle
|
||||
* MUST assume any env var that ends up in the client bundle is attacker-readable. ([vitejs][2])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `VITE_API_KEY=...` containing a true secret (not just a public identifier).
|
||||
* Hard-coded API keys, private tokens, service credentials, signing keys in JS/TS.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search: `VITE_`, `import.meta.env`, `.env`, `.env.production`, `.env.*.local`.
|
||||
* Grep for `API_KEY`, `SECRET`, `TOKEN`, `PRIVATE_KEY`, `BEGIN`, `sk-`, `AKIA`, etc.
|
||||
|
||||
Fix:
|
||||
|
||||
* Move secrets to backend/edge functions.
|
||||
* Use backend-minted short-lived tokens for the browser when needed.
|
||||
|
||||
Notes:
|
||||
|
||||
* Vite specifically warns that `.env.*.local` should be gitignored and that `VITE_*` vars end up in the client bundle, so they must not contain sensitive info. ([vitejs][2])
|
||||
|
||||
---
|
||||
|
||||
### VUE-SECRETS-002: Do not broaden Vite env exposure
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT configure Vite to expose all environment variables to the client.
|
||||
* SHOULD keep `envPrefix` strict and explicit.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Setting `envPrefix` to overly broad values (or `''`) to “make env vars work”.
|
||||
* Custom scripts that inject server secrets into global variables in HTML at build time.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Check `vite.config.*` for `envPrefix`.
|
||||
* Look for `define: { 'process.env': ... }` or manual injection into `window.__CONFIG__`.
|
||||
|
||||
Fix:
|
||||
|
||||
* Keep secrets server-side.
|
||||
* Only expose non-sensitive values intentionally designed to be public.
|
||||
|
||||
Notes:
|
||||
|
||||
* Vite’s docs explain that only prefixed variables are exposed and that exposed variables land in the client bundle. ([vitejs][2])
|
||||
|
||||
---
|
||||
|
||||
### VUE-XSS-001: Prefer Vue’s default escaping; avoid raw HTML injection
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST rely on Vue’s automatic escaping for text interpolation and attribute binding where possible. ([Vue.js][1])
|
||||
* MUST NOT render user-provided HTML via:
|
||||
|
||||
* `v-html`
|
||||
* `innerHTML` in render functions / JSX
|
||||
* direct DOM APIs (`element.innerHTML`, `insertAdjacentHTML`)
|
||||
unless the HTML is trusted or robustly sanitized and the risk is explicitly accepted. ([Vue.js][1])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `<div v-html="userProvidedHtml"></div>`
|
||||
* `h('div', { innerHTML: userProvidedHtml })`
|
||||
* `<div innerHTML={userProvidedHtml}></div>`
|
||||
* `el.innerHTML = untrusted`
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search: `v-html`, `innerHTML`, `insertAdjacentHTML`, `DOMParser`, `document.write`.
|
||||
|
||||
Fix:
|
||||
|
||||
* Render untrusted content as text (interpolation).
|
||||
* If HTML rendering is required (e.g., Markdown), sanitize with a well-maintained HTML sanitizer and apply defense-in-depth (CSP, Trusted Types). ([Vue.js][1])
|
||||
|
||||
Notes:
|
||||
|
||||
* Vue’s docs explicitly warn that user-provided HTML is never “100% safe” unless sandboxed or strictly self-only exposure. ([Vue.js][1])
|
||||
|
||||
---
|
||||
|
||||
### VUE-XSS-002: Never use non-trusted templates (client-side template/code injection)
|
||||
|
||||
Severity: Critical
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT use non-trusted content as a Vue component template.
|
||||
* MUST treat “user can write a Vue template” as “user can execute arbitrary JavaScript in your app”, and potentially in SSR contexts too. ([Vue.js][1])
|
||||
* SHOULD prefer the runtime-only build (templates compiled at build time) and avoid shipping the runtime compiler unless you have a vetted need.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `createApp({ template: '<div>' + userProvidedString + '</div>' }).mount(...)`
|
||||
* Storing templates in DB and compiling/rendering them in the browser.
|
||||
* Admin/CMS features that allow entering Vue template syntax.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search: `template:` where the value is not a static string.
|
||||
* Search: `@vue/compiler-dom`, `compile(`, “runtime compiler” build selection, dynamic SFC compilation.
|
||||
* Search for “template editor”, “custom template”, “theme HTML” features.
|
||||
|
||||
Fix:
|
||||
|
||||
* Treat templates as code: keep them developer-controlled.
|
||||
* If end-user customization is required, use a safe format (restricted Markdown subset) rendered via a sanitizer, or isolate in a sandboxed iframe.
|
||||
|
||||
---
|
||||
|
||||
### VUE-XSS-003: Do not mount Vue onto DOM that may contain user-provided server-rendered HTML
|
||||
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT mount Vue on nodes that may contain server-rendered and user-provided content (because attacker-controlled HTML that is “safe as HTML” may become unsafe as a Vue template). ([Vue.js][1])
|
||||
* SHOULD mount Vue into a “sterile” root element and render the app’s DOM from Vue-controlled templates/components.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Server renders user content into `#app`, then Vue mounts on `#app` and compiles/interprets that DOM as a template.
|
||||
* “Sprinkling Vue” on large server-rendered pages that include user-generated content.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Check server templates (e.g., Rails/Django/Express templates) for user HTML inserted inside the Vue mount root.
|
||||
* Look for `mount('#app')` where `#app` includes server-rendered UGC.
|
||||
|
||||
Fix:
|
||||
|
||||
* Move user-rendered HTML outside the Vue mount root, or render it in a safe way (text/sanitized HTML) from Vue components.
|
||||
|
||||
---
|
||||
|
||||
### VUE-XSS-004: Prevent URL injection in bindings and navigations
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST validate/sanitize any user-influenced URL before binding to navigation sinks (`href`, `src`, `action`, `window.location`, `window.open`, router navigation to external).
|
||||
* MUST specifically prevent `javascript:` URL execution in bindings like `<a :href="userProvidedUrl">`. ([Vue.js][1])
|
||||
* SHOULD validate protocol and destination (allowlist `https:` and expected hosts; allow `mailto:`/`tel:` only if intended).
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `<iframe :src="userProvidedUrl">`
|
||||
* `window.location = route.query.next`
|
||||
* `window.open(userProvidedUrl)`
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search: `:href=`, `:src=`, `window.location`, `location.href`, `window.open`, `router.push(` with untrusted input.
|
||||
* Look for `next`, `return_to`, `redirect` query params.
|
||||
|
||||
Fix:
|
||||
|
||||
* Prefer internal navigation via route names/paths you control.
|
||||
* For external URLs: parse with `new URL(...)`, allowlist protocol/host, reject `javascript:` and other dangerous schemes.
|
||||
* Sanitize and validate on the backend before storing user URLs (Vue docs explicitly recommend backend sanitization). ([Vue.js][1])
|
||||
|
||||
---
|
||||
|
||||
### VUE-XSS-005: Prevent style/CSS injection and UI redress
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT bind attacker-controlled CSS strings broadly (e.g., `:style="userProvidedStyles"`).
|
||||
* SHOULD use Vue’s style object syntax and only allow safe, specific properties if user customization is needed. ([Vue.js][1])
|
||||
* SHOULD isolate “user can control layout/CSS” features inside sandboxed iframes.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `:style="userProvidedStyles"` where styles are attacker-controlled.
|
||||
* Rendering user-provided `<style>` content (even if Vue blocks some patterns, don’t try to work around it).
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search: `:style="` bound to non-constant variables that originate from API/user content.
|
||||
* Search for “custom CSS”, “theme editor”, “profile CSS”.
|
||||
|
||||
Fix:
|
||||
|
||||
* Allowlist properties and values; avoid raw style strings.
|
||||
* Use sandboxed iframes for rich user customization.
|
||||
|
||||
---
|
||||
|
||||
### VUE-XSS-006: Never bind user-provided JavaScript into event handler attributes
|
||||
|
||||
Severity: Critical
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT bind attacker-provided strings into event handler attributes (e.g., `onclick`, `onfocus`, etc.).
|
||||
* MUST treat “user-provided JS” as unsafe unless sandboxed and self-only exposure is guaranteed. ([Vue.js][1])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `<div :onclick="userProvidedString">`
|
||||
* `<a :onmouseenter="userProvidedString">`
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search: `:on` followed by event attribute names (`:onclick`, `:onload`, etc.).
|
||||
* Search for `setAttribute('on` patterns.
|
||||
|
||||
Fix:
|
||||
|
||||
* Use real event listeners with developer-controlled handlers.
|
||||
* If you truly need user scripting, isolate it (sandboxed iframe + strict boundaries).
|
||||
|
||||
---
|
||||
|
||||
### VUE-ROUTER-001: Do not treat client-side route guards as authorization
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT rely on Vue Router guards, UI hiding, or client-side checks to enforce authorization.
|
||||
* MUST enforce authorization on the backend for every privileged action and sensitive data response. ([OWASP Cheat Sheet Series][8])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* “Admin route is protected because `beforeEach` checks `user.isAdmin`.”
|
||||
* Sensitive API endpoints that assume “the frontend won’t call this unless allowed.”
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search `router.beforeEach` for role-based gating and see if the backend is also enforcing.
|
||||
* Look for “security by route meta” patterns (`meta.requiresAdmin`) with no server corroboration.
|
||||
|
||||
Fix:
|
||||
|
||||
* Keep route guards as UX only (reduce accidental access), but enforce real checks server-side.
|
||||
|
||||
---
|
||||
|
||||
### VUE-ROUTER-002: Prevent open redirects and unsafe “return_to/next” handling
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* MUST validate redirect destinations derived from untrusted input (`next`, `return_to`, `redirect`).
|
||||
* SHOULD allow only same-site relative paths or an explicit allowlist of destinations.
|
||||
* MUST NOT allow non `http` / `https` protos (such as `javascript:`)
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `router.push(route.query.next as string)`
|
||||
* `window.location.href = route.query.redirect`
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `route.query.next`, `route.query.redirect`, `return_to`, `continue`, `callback`.
|
||||
* Trace the value into router/window navigation sinks.
|
||||
|
||||
Fix:
|
||||
|
||||
* Allow only relative paths starting with `/` (and reject `//host`, `javascript:`, etc.).
|
||||
* Prefer redirecting to named routes you control.
|
||||
|
||||
Notes:
|
||||
|
||||
* Even Vue’s docs note that sanitized URLs still may not guarantee safe destinations. ([Vue.js][1])
|
||||
|
||||
---
|
||||
|
||||
### VUE-AUTH-001: Token storage must assume XSS is possible
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* MUST assume any token accessible to JavaScript can be stolen via XSS.
|
||||
* SHOULD prefer HttpOnly cookies (set by the backend) for session tokens, combined with CSRF protections where relevant. ([Vue.js][1])
|
||||
* SHOULD avoid storing long-lived tokens (especially refresh tokens) in `localStorage`/`sessionStorage`.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `localStorage.setItem('token', ...)` for long-lived bearer tokens.
|
||||
* Storing refresh tokens in JS-accessible storage.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search: `localStorage`, `sessionStorage`, `indexedDB`, `persist`, `pinia-plugin-persistedstate`.
|
||||
* Identify whether stored values are auth/session material.
|
||||
|
||||
Fix:
|
||||
|
||||
* Prefer backend-managed sessions via HttpOnly cookies.
|
||||
* If bearer tokens are unavoidable, keep them short-lived, stored in memory, and rotate frequently; combine with strong XSS mitigations (CSP, Trusted Types, strict sanitization). ([OWASP Cheat Sheet Series][4])
|
||||
|
||||
---
|
||||
|
||||
### VUE-CSRF-001: Coordinate with the backend for CSRF when using cookies
|
||||
|
||||
Severity: High (for cookie-authenticated state-changing requests)
|
||||
|
||||
NOTE: If the application is not using cookie based authentication (for example if it passes an Authorization header), then CSRF is not a concern
|
||||
|
||||
Required:
|
||||
|
||||
* If API requests include cookies (`credentials: 'include'` / `withCredentials: true`) and cookies authenticate the user, MUST include CSRF protections coordinated with the backend (token/header patterns, Origin checks, SameSite cookies as defense-in-depth). ([Vue.js][1])
|
||||
* MUST NOT “solve CORS/CSRF errors” by disabling protections on the backend or using `mode: 'no-cors'` on the frontend.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `fetch(url, { credentials: 'include', method: 'POST', body: ... })` with no CSRF token/header usage anywhere.
|
||||
* Enabling cross-origin credentialed requests without strict origin allowlists (backend-side).
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search: `credentials: 'include'`, `withCredentials`, `xsrf`, `csrf`, `X-CSRF-Token`, `X-XSRF-TOKEN`.
|
||||
* Look at API wrapper modules for headers and cookie settings.
|
||||
|
||||
Fix:
|
||||
|
||||
* Implement backend-issued CSRF tokens and require them on state-changing requests.
|
||||
* Keep cookies `SameSite=Lax/Strict` where compatible and verify Origin/Referer where appropriate (backend-driven). ([OWASP Cheat Sheet Series][9])
|
||||
|
||||
Notes:
|
||||
|
||||
* Vue’s docs explicitly say CSRF is primarily backend-addressed but recommends coordinating on CSRF token submission. ([Vue.js][1])
|
||||
|
||||
---
|
||||
|
||||
### VUE-HTTP-001: Do not put secrets in URLs; avoid leaking sensitive data in navigation/logs
|
||||
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT place tokens/secrets in query strings or fragments (they leak via logs, referrers, browser history).
|
||||
* SHOULD avoid logging sensitive values to console in production.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `/?token=...`, `/#access_token=...` used beyond short-lived OAuth handoff.
|
||||
* `console.log(userSession)` that includes tokens/PII.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `token=` in router parsing, auth callback handlers, and analytics logs.
|
||||
* Search for `console.log(` around auth code.
|
||||
|
||||
Fix:
|
||||
|
||||
* Use Authorization headers or HttpOnly cookies.
|
||||
* Scrub logs; gate debug logs behind dev-only checks.
|
||||
|
||||
---
|
||||
|
||||
### VUE-HEADERS-001: Require security headers at the deployment layer
|
||||
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD deploy a CSP (`Content-Security-Policy`) suitable for your Vue app.
|
||||
* SHOULD deploy clickjacking defenses (CSP `frame-ancestors` and/or `X-Frame-Options`) unless intentional embedding is required.
|
||||
* SHOULD deploy `X-Content-Type-Options: nosniff`, plus other headers as appropriate (Referrer-Policy, Permissions-Policy). ([OWASP Cheat Sheet Series][4])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* No evidence of headers in server/CDN config for an app with UGC or rich HTML rendering.
|
||||
* CSP includes `unsafe-inline`/`unsafe-eval` without strong justification.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Look for hosting config: nginx, Netlify/Vercel headers config, CloudFront/Cloudflare rules.
|
||||
* If absent in repo, flag as “verify at edge”.
|
||||
|
||||
Fix:
|
||||
|
||||
* Set headers at the edge or in the server. Start with a conservative CSP and tighten.
|
||||
|
||||
---
|
||||
|
||||
### VUE-CSP-001: Use Trusted Types and DOM XSS hardening when feasible
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* For apps with significant DOM injection surface (rich text, plugins, `v-html`), SHOULD consider enabling Trusted Types to reduce DOM XSS risk. ([web.dev][10])
|
||||
* SHOULD treat Trusted Types as defense-in-depth, not a replacement for sanitization.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Frequent use of `innerHTML`/`v-html` without sanitization or CSP hardening.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search: `v-html`, `innerHTML`, `insertAdjacentHTML`.
|
||||
* Check CSP for `require-trusted-types-for 'script'` usage (if headers are in repo).
|
||||
|
||||
Fix:
|
||||
|
||||
* Reduce/centralize HTML injection, sanitize inputs, and add Trusted Types policies where appropriate.
|
||||
|
||||
---
|
||||
|
||||
### VUE-THIRDPARTY-001: Avoid dynamic third-party script injection; prefer static, vetted loading
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT inject `<script src="...">` where the URL is user-controlled.
|
||||
* SHOULD treat third-party widgets/analytics as supply-chain risk; load only from vetted, pinned sources.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `const s=document.createElement('script'); s.src = userProvidedUrl; ...`
|
||||
* “Plugin marketplace” that loads arbitrary remote scripts.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search: `createElement('script')`, `.src =`, `appendChild(script)`.
|
||||
* Search for “loadExternalScript”, “injectScript”, “cdnUrl”.
|
||||
|
||||
Fix:
|
||||
|
||||
* Bundle dependencies, or allowlist strict origins and enforce integrity (see SRI rule).
|
||||
* Consider sandboxed iframes for untrusted third-party UI.
|
||||
|
||||
---
|
||||
|
||||
### VUE-SRI-001: Use Subresource Integrity for CDN-hosted scripts/styles
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* If loading scripts/styles from a CDN, SHOULD use Subresource Integrity (`integrity` attribute) with appropriate `crossorigin` configuration. ([MDN Web Docs][11])
|
||||
* SHOULD prefer self-hosting or bundling over runtime CDN dependencies for security-critical code.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `<script src="https://cdn.example/...">` with no `integrity`.
|
||||
* Remote script URLs that can change content without version pinning.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search `index.html` and server templates for `https://` script/style tags.
|
||||
* Check for `integrity=`.
|
||||
|
||||
Fix:
|
||||
|
||||
* Add SRI hashes (and pin versions), or bundle assets with your build.
|
||||
|
||||
---
|
||||
|
||||
### VUE-SUPPLY-001: Dependency and patch hygiene is mandatory
|
||||
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD keep Vue and official companion libraries updated; Vue explicitly recommends using latest versions to remain as secure as possible. ([Vue.js][1])
|
||||
* MUST respond to security advisories promptly.
|
||||
* SHOULD pin dependencies and keep lockfiles committed (to reduce drift in production artifacts).
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Outdated major versions with known CVEs.
|
||||
* No lockfile in repo; wide semver ranges for critical deps.
|
||||
* Ignoring advisories for template/rendering/compiler packages.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Inspect `package.json`, lockfiles, CI install commands.
|
||||
* Search for `npm audit` disabled, “ignore vulnerabilities” scripts.
|
||||
|
||||
Fix:
|
||||
|
||||
* Upgrade dependencies and add regression tests around the impacted behavior.
|
||||
* Add dependency scanning in CI.
|
||||
|
||||
---
|
||||
|
||||
### VUE-SSR-001: SSR adds additional trust boundaries; treat state injection as XSS-sensitive
|
||||
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
|
||||
* When using SSR, MUST treat anything injected into the HTML document (initial state, serialized data, inline scripts) as XSS-sensitive.
|
||||
* MUST keep the “trusted templates only” rule even stricter, because unsafe templates can lead to server-side execution during rendering. ([Vue.js][1])
|
||||
* SHOULD follow Vue SSR documentation and best practices for SSR security. ([Vue.js][1])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Concatenating untrusted strings into SSR templates.
|
||||
* Injecting JSON into `<script>` blocks without robust escaping/serialization controls.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search server code for `__INITIAL_STATE__`, `window.__*STATE__`, template concatenation, and SSR render pipelines.
|
||||
* Trace untrusted data into those sinks.
|
||||
|
||||
Fix:
|
||||
|
||||
* Use safe serialization patterns recommended by your SSR stack.
|
||||
* Avoid rendering untrusted HTML; sanitize or isolate.
|
||||
|
||||
---
|
||||
|
||||
## 5) Practical scanning heuristics (how to “hunt”)
|
||||
|
||||
When actively scanning, use these high-signal patterns:
|
||||
|
||||
* Dev/preview servers in production:
|
||||
|
||||
* `npm run dev`, `vite`, `vite preview`, `vue-cli-service serve` ([vitejs][5])
|
||||
* Secrets exposure:
|
||||
|
||||
* `.env`, `.env.production`, `.env.*.local`, `VITE_`, `import.meta.env`, hard-coded `API_KEY` / `SECRET` ([vitejs][2])
|
||||
* XSS sinks:
|
||||
|
||||
* `v-html`, `innerHTML`, `insertAdjacentHTML`, `DOMParser`, `document.write` ([Vue.js][1])
|
||||
* Client-side template injection:
|
||||
|
||||
* `template:` concatenation, `compile(`, runtime compiler usage, mounting on non-sterile DOM ([Vue.js][1])
|
||||
* URL injection / open redirects:
|
||||
|
||||
* `:href="..."` / `:src="..."` from user data
|
||||
* `javascript:` occurrences
|
||||
* `route.query.next` / `redirect` / `return_to` flowing into `router.push` or `window.location` ([Vue.js][1])
|
||||
* Style injection:
|
||||
|
||||
* `:style="userProvidedStyles"` or user-driven theme CSS ([Vue.js][1])
|
||||
* Token storage:
|
||||
|
||||
* `localStorage.setItem('token'...)`, persisted auth stores, refresh tokens in JS-accessible storage
|
||||
* CSRF integration red flags:
|
||||
|
||||
* `credentials: 'include'` / `withCredentials: true` without any CSRF header/token handling ([Vue.js][1])
|
||||
* Third-party scripts:
|
||||
|
||||
* dynamic script injection (`createElement('script')`), CDN scripts without SRI ([MDN Web Docs][11])
|
||||
* External links security:
|
||||
|
||||
* `target="_blank"` without `rel="noopener"`/`noreferrer` (still recommended for legacy and explicitness) ([MDN Web Docs][12])
|
||||
|
||||
Always try to confirm:
|
||||
|
||||
* data origin (untrusted vs trusted)
|
||||
* sink type (HTML/DOM insertion, template compilation, URL navigation, style injection, script injection)
|
||||
* protective controls present (sanitization, allowlists, CSP/Trusted Types, backend validation)
|
||||
|
||||
---
|
||||
|
||||
## 6) Sources (accessed 2026-01-27)
|
||||
|
||||
Primary Vue documentation:
|
||||
|
||||
* Vue Docs: Security — `https://vuejs.org/guide/best-practices/security` ([Vue.js][1])
|
||||
* Vue Docs: Template Syntax (security warning about in-DOM templates) — `https://vuejs.org/guide/essentials/template-syntax` ([Vue.js][13])
|
||||
* Vue Docs: Production Deployment — `https://vuejs.org/guide/best-practices/production-deployment` ([Vue.js][3])
|
||||
* Vue Docs: Feature Flags — `https://link.vuejs.org/feature-flags` ([Vue.js][7])
|
||||
|
||||
Vite documentation (common Vue tooling):
|
||||
|
||||
* Vite Docs: Env Variables and Modes (VITE_* exposure + security notes) — `https://vite.dev/guide/env-and-mode` ([vitejs][2])
|
||||
* Vite Docs: CLI (`vite preview` not designed for production) — `https://vite.dev/guide/cli` ([vitejs][5])
|
||||
* Vite Docs: Server Options (`server.host` can listen on public addresses) — `https://vite.dev/config/server-options` ([vitejs][14])
|
||||
|
||||
OWASP and web platform hardening references:
|
||||
|
||||
* OWASP Cheat Sheet Series: XSS Prevention — `https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html` ([Vue.js][1])
|
||||
* OWASP Cheat Sheet Series: CSRF Prevention — `https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][9])
|
||||
* OWASP Cheat Sheet Series: Authorization — `https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][8])
|
||||
* OWASP Cheat Sheet Series: HTTP Headers — `https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html` ([OWASP Cheat Sheet Series][4])
|
||||
* HTML5 Security Cheat Sheet (referenced by Vue) — `https://html5sec.org/` ([Vue.js][1])
|
||||
|
||||
Browser/platform references:
|
||||
|
||||
* MDN: `rel="noopener"` — `https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/noopener` ([MDN Web Docs][12])
|
||||
* MDN: Subresource Integrity — `https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity` ([MDN Web Docs][11])
|
||||
* web.dev: Trusted Types — `https://web.dev/trusted-types/` ([web.dev][10])
|
||||
|
||||
[1]: https://vuejs.org/guide/best-practices/security "https://vuejs.org/guide/best-practices/security"
|
||||
[2]: https://vite.dev/guide/env-and-mode "https://vite.dev/guide/env-and-mode"
|
||||
[3]: https://vuejs.org/guide/best-practices/production-deployment "https://vuejs.org/guide/best-practices/production-deployment"
|
||||
[4]: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html "https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html"
|
||||
[5]: https://vite.dev/guide/cli "https://vite.dev/guide/cli"
|
||||
[6]: https://vite.dev/guide/build "https://vite.dev/guide/build"
|
||||
[7]: https://vuejs.org/guide/best-practices/production-deployment?utm_source=chatgpt.com "Production Deployment"
|
||||
[8]: https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html "https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html"
|
||||
[9]: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html "https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html"
|
||||
[10]: https://web.dev/articles/trusted-types "https://web.dev/articles/trusted-types"
|
||||
[11]: https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity?utm_source=chatgpt.com "Subresource Integrity - Security - MDN Web Docs"
|
||||
[12]: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/noopener "https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/noopener"
|
||||
[13]: https://vuejs.org/guide/essentials/template-syntax "Template Syntax | Vue.js"
|
||||
[14]: https://vite.dev/config/server-options "https://vite.dev/config/server-options"
|
||||
@@ -0,0 +1,882 @@
|
||||
# Django (Python) Web Security Spec (Django 6.0.x, Python 3.x)
|
||||
|
||||
This document is designed as a **security spec** that supports:
|
||||
|
||||
1. **Secure-by-default code generation** for new Django code.
|
||||
2. **Security review / vulnerability hunting** in existing Django code (passive “notice issues while working” and active “scan the repo and report findings”).
|
||||
|
||||
It is intentionally written as a set of **normative requirements** (“MUST/SHOULD/MAY”) plus **audit rules** (what bad patterns look like, how to detect them, and how to fix/mitigate them).
|
||||
|
||||
---
|
||||
|
||||
## 0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)
|
||||
|
||||
* MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session cookies, `SECRET_KEY`, `SECRET_KEY_FALLBACKS`, database passwords).
|
||||
* MUST NOT “fix” security by disabling protections (e.g., removing `CsrfViewMiddleware`, sprinkling `@csrf_exempt`, loosening `ALLOWED_HOSTS` to `['*']`, disabling `SecurityMiddleware`, disabling template auto-escaping, disabling permission checks).
|
||||
* MUST provide **evidence-based findings** during audits: cite file paths, code snippets, and concrete configuration values that justify the claim.
|
||||
* MUST treat uncertainty honestly: if a protection might exist in infrastructure (reverse proxy, WAF, CDN, ingress controller), report it as “not visible in app code; verify at runtime / edge config”.
|
||||
* MUST keep fixes compatible with Django’s intended security model: prefer Django’s built-ins (middleware, auth, forms, ORM) over custom security logic whenever possible. Django’s deployment checklist and system checks are part of the intended model. ([Django Project][1])
|
||||
|
||||
---
|
||||
|
||||
## 1) Operating modes
|
||||
|
||||
### 1.1 Generation mode (default)
|
||||
|
||||
When asked to write new Django code or modify existing code:
|
||||
|
||||
* MUST follow every **MUST** requirement in this spec.
|
||||
* SHOULD follow every **SHOULD** requirement unless the user explicitly says otherwise.
|
||||
* MUST prefer safe-by-default Django APIs and proven libraries over custom security code.
|
||||
* MUST avoid introducing new risky sinks (dynamic template rendering from untrusted strings, unsafe redirects, unsafe file serving, shell execution, raw SQL string formatting, SSRF-capable URL fetchers from untrusted input).
|
||||
|
||||
### 1.2 Passive review mode (always on while editing)
|
||||
|
||||
While working anywhere in a Django repo (even if the user did not ask for a security scan):
|
||||
|
||||
* MUST “notice” violations of this spec in touched/nearby code.
|
||||
* SHOULD mention issues as they come up, with a brief explanation + safe fix.
|
||||
|
||||
### 1.3 Active audit mode (explicit scan request)
|
||||
|
||||
When the user asks to “scan”, “audit”, or “hunt for vulns”:
|
||||
|
||||
* MUST systematically search the codebase for violations of this spec.
|
||||
* MUST output findings in a structured format (see §2.3).
|
||||
|
||||
Recommended audit order:
|
||||
|
||||
1. Deployment entrypoints (ASGI/WSGI), Dockerfiles, Procfiles, systemd units, platform manifests.
|
||||
2. `settings.py` and environment-specific settings modules.
|
||||
3. Middleware ordering and enabled protections.
|
||||
4. Authn/authz (login, session management, permissions, admin).
|
||||
5. CSRF protections and state-changing endpoints.
|
||||
6. Templates and XSS.
|
||||
7. File handling (uploads/downloads/static/media) and path traversal.
|
||||
8. Injection classes (SQL, command execution, unsafe deserialization).
|
||||
9. Outbound requests (SSRF).
|
||||
10. Redirect handling (open redirects) + CORS + security headers (CSP, HSTS, etc.).
|
||||
11. Dependency/pinning and patch posture.
|
||||
|
||||
---
|
||||
|
||||
## 2) Definitions and review guidance
|
||||
|
||||
### 2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)
|
||||
|
||||
Examples include:
|
||||
|
||||
* `request.GET`, `request.POST`, `request.FILES`
|
||||
* `request.body`, JSON bodies (e.g., `json.loads(request.body)`), DRF `request.data`
|
||||
* URL path parameters (e.g., `<int:id>`, `<slug:...>`)
|
||||
* `request.headers` / `request.META` (including `HTTP_HOST`, `HTTP_ORIGIN`, `HTTP_REFERER`, `HTTP_X_FORWARDED_*`)
|
||||
* `request.COOKIES`
|
||||
* Any data from external systems (webhooks, third-party APIs, message queues)
|
||||
* Any persisted content that originated from users (DB rows, cached content, file uploads)
|
||||
|
||||
Django explicitly emphasizes “never trust user-controlled data” and recommends using forms/validation. ([Django Project][2])
|
||||
|
||||
### 2.2 State-changing request
|
||||
|
||||
A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook send), or initiate privileged actions.
|
||||
|
||||
### 2.3 Required audit finding format
|
||||
|
||||
For each issue found, output:
|
||||
|
||||
* Rule ID:
|
||||
* Severity: Critical / High / Medium / Low
|
||||
* Location: file path + function/class/view name + line(s)
|
||||
* Evidence: the exact code/config snippet
|
||||
* Impact: what could go wrong, who can exploit it
|
||||
* Fix: safe change (prefer minimal diff)
|
||||
* Mitigation: defense-in-depth if immediate fix is hard
|
||||
* False positive notes: what to verify if uncertain
|
||||
|
||||
---
|
||||
|
||||
## 3) Secure baseline: minimum production configuration (MUST in production)
|
||||
|
||||
This is the smallest “production baseline” that prevents common Django misconfigurations. Django provides a “Deployment checklist” and recommends running `manage.py check --deploy` against production settings. ([Django Project][1])
|
||||
|
||||
### 3.1 Settings management pattern (SHOULD)
|
||||
|
||||
* SHOULD use environment-based configuration (or a secret manager) so production settings are not hard-coded.
|
||||
* MUST treat sensitive settings as confidential (e.g., `SECRET_KEY`, DB passwords) and keep them out of source control. Django’s checklist explicitly recommends loading `SECRET_KEY` from env or a file rather than hardcoding. ([Django Project][1])
|
||||
* SHOULD separate dev vs prod settings modules, with safe defaults for production (fail closed if critical settings are missing). ([Django Project][1])
|
||||
|
||||
### 3.2 Minimum baseline targets (production)
|
||||
|
||||
* MUST NOT use `manage.py runserver` as the production entrypoint; use a production-ready WSGI or ASGI server. ([Django Project][1])
|
||||
* MUST set `DEBUG = False` in production. ([Django Project][1])
|
||||
* MUST set a strong, secret `SECRET_KEY` and keep it secret; MAY use `SECRET_KEY_FALLBACKS` for safe rotation. ([Django Project][1])
|
||||
* MUST set `ALLOWED_HOSTS` to expected hosts (no wildcard unless you do your own host validation). ([Django Project][1])
|
||||
* MUST enforce HTTPS for authenticated areas (ideally site-wide for any login-capable app) and set `CSRF_COOKIE_SECURE=True` and `SESSION_COOKIE_SECURE=True` when HTTPS is used. ([Django Project][1])
|
||||
* SHOULD enable key `SecurityMiddleware` headers/settings: HSTS, Referrer-Policy, COOP, nosniff, SSL redirect (with correct proxy configuration). ([Django Project][3])
|
||||
* MUST treat user uploads as untrusted; ensure your web server never interprets them as executable content; keep `MEDIA_ROOT` separate from `STATIC_ROOT`. ([Django Project][1])
|
||||
|
||||
---
|
||||
|
||||
## 4) Rules (generation + audit)
|
||||
|
||||
Each rule contains: required practice, insecure patterns, detection hints, and remediation.
|
||||
|
||||
### DJANGO-DEPLOY-001: Do not use Django’s development server in production
|
||||
|
||||
Severity: High (if production)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT deploy `manage.py runserver` as the production server.
|
||||
* MUST run behind a production-grade WSGI or ASGI server. ([Django Project][1])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Production docs/scripts using `python manage.py runserver 0.0.0.0:8000`.
|
||||
* Docker `CMD`/entrypoint uses `runserver`.
|
||||
* Kubernetes/Procfile/systemd units invoking `runserver`.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `manage.py runserver`, `runserver 0.0.0.0`, `--insecure`.
|
||||
* Check Docker `CMD/ENTRYPOINT`, Procfile, systemd unit files, Helm charts.
|
||||
|
||||
Fix:
|
||||
|
||||
* Use a production server (WSGI/ASGI) as recommended in Django’s deployment checklist. ([Django Project][1])
|
||||
|
||||
Note:
|
||||
|
||||
* `runserver` is fine for local development. Only flag if it’s used as the production entrypoint.
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-DEPLOY-002: `DEBUG` MUST be disabled in production
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST set `DEBUG = False` in production.
|
||||
* MUST treat any mechanism that exposes debug pages/tracebacks to untrusted users as a critical information disclosure risk. Django’s checklist explicitly warns `DEBUG=True` leaks source excerpts, local variables, settings, and more. ([Django Project][1])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `DEBUG = True` in production settings.
|
||||
* Environment defaults to `DEBUG=True` unless explicitly overridden.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search `DEBUG = True`, `DEBUG=os.environ.get(..., True)`, `DJANGO_DEBUG`, `.env` files.
|
||||
* Look for “production” settings modules that import from dev defaults.
|
||||
|
||||
Fix:
|
||||
|
||||
* Set `DEBUG=False` in prod settings; use explicit environment config.
|
||||
* Ensure error reporting is via safe logging/monitoring, not debug pages. ([Django Project][1])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-CONFIG-001: `SECRET_KEY` must be strong, secret, and rotated safely
|
||||
|
||||
Severity: High (Critical if missing in production with signing/sessions)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST set a large random `SECRET_KEY` in production and keep it secret. ([Django Project][1])
|
||||
* MUST NOT commit it to source control or print/log it. ([Django Project][1])
|
||||
* SHOULD load it from env or a file/secret store (not hard-coded). ([Django Project][1])
|
||||
* MAY rotate keys using `SECRET_KEY_FALLBACKS` to avoid instantly invalidating all signed data; MUST remove old keys from fallbacks in a timely manner. ([Django Project][1])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Hard-coded `SECRET_KEY = "..."` in repo for production.
|
||||
* `SECRET_KEY` reused across environments.
|
||||
* `SECRET_KEY_FALLBACKS` contains long-expired keys indefinitely.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `SECRET_KEY =`, `SECRET_KEY_FALLBACKS`, `.env` committed files, `print(settings.SECRET_KEY)`.
|
||||
|
||||
Fix:
|
||||
|
||||
* Load from secret manager / environment variable.
|
||||
* If rotating:
|
||||
|
||||
* Set new `SECRET_KEY`
|
||||
* Keep old key(s) temporarily in `SECRET_KEY_FALLBACKS`
|
||||
* Remove old key(s) after the rotation window. ([Django Project][1])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-HOST-001: Host header must be validated (`ALLOWED_HOSTS` must be strict)
|
||||
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
|
||||
* MUST set `ALLOWED_HOSTS` in production to your expected domains/hosts. ([Django Project][1])
|
||||
* MUST NOT set `ALLOWED_HOSTS = ['*']` in production unless you also implement your own robust `Host` validation (Django warns that wildcards require your own validation to avoid CSRF-class attacks). ([Django Project][1])
|
||||
* SHOULD configure the fronting web server to reject unknown hosts early (defense-in-depth). ([Django Project][1])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `ALLOWED_HOSTS = ['*']` (or env expands to `*`) in production.
|
||||
* `ALLOWED_HOSTS = []` with `DEBUG=False` (site won’t run, or misconfigured deployments attempt workarounds).
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search `ALLOWED_HOSTS`.
|
||||
* Check platform environment settings that override `ALLOWED_HOSTS`.
|
||||
|
||||
Fix:
|
||||
|
||||
* Set `ALLOWED_HOSTS = ['example.com', 'www.example.com', ...]` for prod.
|
||||
* Keep dev hosts separate.
|
||||
|
||||
Notes:
|
||||
|
||||
* Django uses the Host header for URL construction; fake Host values can lead to CSRF, cache poisoning, and poisoned email links (Django security docs call this out). ([Django Project][2])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-HTTPS-001: If TLS is used cookie transport must be secured
|
||||
|
||||
Severity: High (Critical for auth-enabled apps)
|
||||
|
||||
NOTE: Only enforce this if TLS is enabled, as it will break non-TLS applications
|
||||
|
||||
If using TLS:
|
||||
* MUST set:
|
||||
|
||||
* `CSRF_COOKIE_SECURE = True` ([Django Project][1])
|
||||
* `SESSION_COOKIE_SECURE = True` ([Django Project][1])
|
||||
* SHOULD consider enabling:
|
||||
|
||||
* `SECURE_SSL_REDIRECT = True` (with correct proxy config) ([Django Project][3])
|
||||
* HSTS via `SECURE_HSTS_SECONDS` (+ includeSubDomains/preload as appropriate). ([Django Project][3])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Login pages over HTTP, or mixed HTTP/HTTPS with the same session cookie.
|
||||
* `CSRF_COOKIE_SECURE=False` or `SESSION_COOKIE_SECURE=False` in production HTTPS.
|
||||
* HSTS enabled incorrectly (can break site for the duration).
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Inspect `settings.py` for `CSRF_COOKIE_SECURE`, `SESSION_COOKIE_SECURE`, `SECURE_SSL_REDIRECT`, `SECURE_HSTS_SECONDS`.
|
||||
* Inspect proxy/ingress config for HTTP->HTTPS redirect behavior.
|
||||
|
||||
Fix:
|
||||
|
||||
* Enable HTTPS redirect and secure cookies.
|
||||
* Add HSTS carefully (start with low value, validate, then increase). Django warns misconfig can break your site for the HSTS duration. ([Django Project][3])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-PROXY-001: Reverse proxy trust must be configured correctly (`SECURE_PROXY_SSL_HEADER`)
|
||||
|
||||
Severity: Medium (when behind a TLS proxy)
|
||||
|
||||
Required:
|
||||
|
||||
* If behind a reverse proxy that terminates TLS, MUST configure Django so `request.is_secure()` reflects the *external* scheme, otherwise CSRF and other logic can break. Django documents using `SECURE_PROXY_SSL_HEADER` for this. ([Django Project][3])
|
||||
* MUST only set `SECURE_PROXY_SSL_HEADER` if you control the proxy (or have guarantees) and it strips inbound spoofed headers. Django explicitly warns misconfig can compromise security and lists required conditions. ([Django Project][3])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")` in an environment where the proxy does not strip user-supplied `X-Forwarded-Proto`.
|
||||
* Infinite redirect loops after setting `SECURE_SSL_REDIRECT=True` (often indicates proxy HTTPS detection is wrong). ([Django Project][3])
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search `SECURE_PROXY_SSL_HEADER`, `SECURE_SSL_REDIRECT`.
|
||||
* Inspect ingress/proxy behavior for stripping forwarded headers.
|
||||
|
||||
Fix:
|
||||
|
||||
* Set `SECURE_PROXY_SSL_HEADER` only if the proxy strips and sets the header correctly (per Django’s documented prerequisites). ([Django Project][3])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-SESS-001: Session cookies must use secure attributes in production
|
||||
|
||||
Severity: Medium (Only if TLS enabled)
|
||||
|
||||
Required (production, HTTPS):
|
||||
|
||||
* MUST set `SESSION_COOKIE_SECURE=True` (only transmit over HTTPS). ([Django Project][3])
|
||||
* MUST keep `SESSION_COOKIE_HTTPONLY=True` (Django default is `True`). ([Django Project][3])
|
||||
* SHOULD keep `SESSION_COOKIE_SAMESITE='Lax'` (Django default is `Lax`) unless a justified cross-site flow requires `None`. ([Django Project][3])
|
||||
* SHOULD avoid setting `SESSION_COOKIE_DOMAIN` unless you truly need cross-subdomain cookies (subdomain-wide cookies expand attack surface).
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `SESSION_COOKIE_SECURE=False` in production HTTPS.
|
||||
|
||||
IMPORTANT NOTE: Only set `Secure` in production environment when TLS is configured. When running in a local dev environment over HTTP, do not set `Secure` property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like `SESSION_COOKIE_SECURE` which can be used to disable `Secure` cookies when testing over HTTP.
|
||||
|
||||
* `SESSION_COOKIE_HTTPONLY=False`.
|
||||
* `SESSION_COOKIE_SAMESITE=None` combined with cookie-authenticated state-changing endpoints (higher CSRF risk).
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `SESSION_COOKIE_` settings, `response.set_cookie(..., httponly=..., secure=..., samesite=...)`.
|
||||
|
||||
Fix:
|
||||
|
||||
* Set the above explicitly in production settings.
|
||||
* Validate compatibility with your auth flows. ([Django Project][3])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-SESS-002: CSRF cookie settings must be deliberate (HttpOnly has tradeoffs)
|
||||
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD set `CSRF_COOKIE_SECURE=True` when using HTTPS/TLS. ([Django Project][3])
|
||||
* SHOULD keep `CSRF_COOKIE_SAMESITE='Lax'` unless you have a cross-site requirement. Django default is `Lax`. ([Django Project][3])
|
||||
* MAY set `CSRF_COOKIE_HTTPONLY=True` (default is `False`) if your frontend does not need to read the CSRF cookie. If you enable it, your JS must read the CSRF token from the DOM instead (Django documents this). ([Django Project][3])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `CSRF_COOKIE_SECURE=False` in production HTTPS/TLS.
|
||||
* Setting `CSRF_COOKIE_HTTPONLY=True` but still relying on “read csrftoken cookie in JS” patterns (breaks CSRF for AJAX).
|
||||
* `CSRF_COOKIE_SAMESITE=None` without a clear reason.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `CSRF_COOKIE_` settings.
|
||||
* Search JS for `document.cookie` usage to fetch `csrftoken`.
|
||||
|
||||
Fix:
|
||||
|
||||
* Align cookie settings with your CSRF token acquisition method (cookie vs DOM) as Django describes. ([Django Project][4])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-CSRF-001: Cookie-authenticated state-changing requests MUST be CSRF-protected
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST keep `django.middleware.csrf.CsrfViewMiddleware` enabled (it is activated by default). ([Django Project][4])
|
||||
* MUST include `{% csrf_token %}` in internal POST forms; MUST NOT include it in forms that POST to external URLs (Django warns this leaks the token). ([Django Project][4])
|
||||
* MUST protect all state-changing endpoints (POST/PUT/PATCH/DELETE) that rely on cookies for authentication.
|
||||
* For AJAX/SPA calls, MUST send the CSRF token via the `X-CSRFToken` header (or configured header name) as documented. ([Django Project][4])
|
||||
* MUST be very careful with `@csrf_exempt` and use it only when absolutely necessary; if used, MUST replace CSRF with an appropriate alternative control (e.g., request signing for webhooks). Django explicitly warns about `csrf_exempt`. ([Django Project][2])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Missing `CsrfViewMiddleware` in `MIDDLEWARE`.
|
||||
* `@csrf_exempt` on general-purpose authenticated views.
|
||||
* POST/PUT/PATCH/DELETE endpoints with session auth and no CSRF tokens.
|
||||
* Using GET for state-changing actions (amplifies CSRF risk).
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Inspect `settings.py` `MIDDLEWARE` for `CsrfViewMiddleware` and its order (Django notes it should come before middleware that assumes CSRF is handled). ([Django Project][4])
|
||||
* Search for `csrf_exempt`, `csrf_protect`, `ensure_csrf_cookie`.
|
||||
* Enumerate URL patterns for non-GET methods; confirm CSRF coverage.
|
||||
|
||||
Fix:
|
||||
|
||||
* Re-enable `CsrfViewMiddleware`, add CSRF tokens to forms, and add AJAX header handling.
|
||||
* For caching decorators: if you cache a view that needs CSRF tokens, apply `@csrf_protect` as Django documents to avoid caching a response without CSRF cookie/Vary headers. ([Django Project][4])
|
||||
|
||||
Notes:
|
||||
|
||||
* When deployed with HTTPS, Django’s CSRF middleware also checks the Referer header for same-origin (Django security docs mention this). ([Django Project][2])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-XSS-001: Prevent reflected/stored XSS in templates and HTML generation
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST rely on Django template auto-escaping (safe-by-default) for HTML templates. Django security docs highlight that Django templates escape dangerous characters but have limitations. ([Django Project][2])
|
||||
* MUST NOT disable auto-escaping broadly (`{% autoescape off %}`) unless the content is trusted or safely sanitized. ([Django Project][5])
|
||||
* MUST NOT mark untrusted content as safe:
|
||||
|
||||
* Avoid `mark_safe(...)` on user data.
|
||||
* Avoid `|safe` on user-controlled content.
|
||||
* MUST be careful about HTML context pitfalls (e.g., unquoted attributes); Django explicitly shows an example where escaping does not protect an unquoted attribute context. ([Django Project][2])
|
||||
* SHOULD prefer safe HTML construction helpers (e.g., `format_html`) rather than manual concatenation that risks missing escapes. ([Django Project][6])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `{% autoescape off %}{{ user_input }}{% endautoescape %}`
|
||||
* `{{ user_input|safe }}`
|
||||
* `mark_safe(request.GET["q"])`
|
||||
* Unquoted attribute injections: `<style class={{ var }}>...` (Django’s own example). ([Django Project][2])
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search templates for `|safe`, `autoescape off`, `safeseq`.
|
||||
* Search Python for `mark_safe`, `SafeString`, or direct HTML concatenation with request/DB values.
|
||||
* Review any code returning `HttpResponse(user_value)` where `user_value` contains HTML.
|
||||
|
||||
Fix:
|
||||
|
||||
* Remove unsafe marking; sanitize only when strictly necessary (use an allowlist-based HTML sanitizer).
|
||||
* Quote attributes and avoid placing untrusted values into dangerous contexts.
|
||||
* Add CSP as defense-in-depth (see DJANGO-CSP-001). ([Django Project][2])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-TEMPLATE-001: Never render untrusted template source strings
|
||||
|
||||
Severity: High to Critical (depends on context and exposure)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT render templates where the template source string is influenced by untrusted input (request, user content, DB rows editable by untrusted users).
|
||||
* MUST treat “template from string” patterns as dangerous, even if Django templates are more constrained than some other engines: they can still leak data from context, bypass escaping, and create XSS or content injection.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `Template(request.GET["tmpl"]).render(Context(...))`
|
||||
* Saving user templates in the DB and rendering them with normal privileges/context.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `django.template.Template(`, `Engine.from_string`, `.render(Context(` with non-constant strings.
|
||||
* Trace where the template string comes from (admin panels, DB, uploads, requests).
|
||||
|
||||
Fix:
|
||||
|
||||
* Replace with non-executing formatting (e.g., `string.Template`, explicit placeholders) or a strict allowlisted rendering model.
|
||||
* If you *must* support user-defined templates, isolate heavily (separate service/tenant context, strict allowlists, and assume bypasses are possible).
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-SQL-001: Prevent SQL injection (use ORM or parameterized raw SQL)
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST use Django ORM/querysets for normal DB access; Django notes querysets are parameterized and protected from SQL injection under typical use. ([Django Project][2])
|
||||
* MUST be very careful with raw SQL; if using `raw()`, `cursor.execute()`, `extra()`, or `RawSQL`, MUST pass parameters separately (e.g., `params=`) and MUST NOT string-interpolate untrusted input into SQL. Django’s raw SQL docs warn to escape user-controlled parameters using `params`. ([Django Project][7])
|
||||
* MUST NOT quote placeholders in SQL templates (Django docs explicitly warn that quoting `%s` placeholders makes it unsafe). ([Django Project][8])
|
||||
* SHOULD avoid `extra()` and `RawSQL` unless necessary; Django security docs call for caution. ([Django Project][2])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `cursor.execute(f"SELECT ... WHERE id={request.GET['id']}")`
|
||||
* `Model.objects.raw("... %s" % user_input)` (string formatting)
|
||||
* `extra(where=[f"headline='{q}'"])`
|
||||
* Quoted placeholders: `WHERE othercol = '%s'` (explicitly documented as unsafe). ([Django Project][8])
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Grep for `.raw(`, `.extra(`, `RawSQL(`, `connection.cursor()`, `.execute(`.
|
||||
* Grep for SQL keywords (`SELECT`, `UPDATE`, `DELETE`, `INSERT`) in Python strings.
|
||||
* Track untrusted inputs into these call sites.
|
||||
|
||||
Fix:
|
||||
|
||||
* Prefer ORM queries.
|
||||
* If raw SQL is unavoidable, use parameters (`params`, DB-API param binding) and do not quote placeholders. ([Django Project][7])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-CMD-001: Prevent OS command injection
|
||||
|
||||
Severity: Critical to High (depends on exposure)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST avoid executing system commands with attacker-influenced input.
|
||||
* If subprocess is necessary:
|
||||
|
||||
* MUST pass args as a list (not a shell string).
|
||||
* MUST NOT use `shell=True` with attacker-influenced content.
|
||||
* SHOULD use strict allowlists for variable components.
|
||||
* SHOULD prefer pure-Python libraries instead of shelling out.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `os.system(request.GET["cmd"])`
|
||||
* `subprocess.run(f"convert {path}", shell=True)` where `path` is user-controlled.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search `os.system`, `subprocess`, `Popen`, `shell=True`.
|
||||
* Trace request/DB inputs into those calls.
|
||||
|
||||
Fix:
|
||||
|
||||
* Replace with library APIs; if unavoidable, hard-code executable and allowlist validated parameters.
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-UPLOAD-001: File uploads must be validated, stored safely, and served safely
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST treat all user uploads as untrusted. Django explicitly warns “Media files are uploaded by your users. They’re untrusted!” ([Django Project][1])
|
||||
* MUST ensure the web server never interprets user uploads as executable code (e.g., don’t allow uploaded `.php` or HTML to execute/inline as active content). ([Django Project][1])
|
||||
* MUST enforce size limits (at least at the web server; Django security docs recommend limiting upload size at the server to prevent DoS). ([Django Project][2])
|
||||
* SHOULD validate file types using allowlists and content checks (not only extensions).
|
||||
* SHOULD store uploads outside the application code directory and outside any static root.
|
||||
* SHOULD consider serving uploads from a separate top-level/second-level domain to reduce same-origin impact; Django security docs recommend a distinct domain and note that a subdomain may be insufficient for some protections. ([Django Project][2])
|
||||
* MUST be aware of polyglot upload risks: Django documents a case where HTML can be uploaded “as an image” by using a valid PNG header (and may be served as HTML depending on the web server). ([Django Project][2])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Serving uploads inline with `text/html` or without forcing download for potentially active formats.
|
||||
* Upload allowlist based only on extension.
|
||||
* Upload storage inside static roots or code roots.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `request.FILES`, `FileField`, `ImageField`, upload forms/views.
|
||||
* Inspect upload serving paths and Nginx/Apache config (media handlers).
|
||||
* Check `MEDIA_URL`, `MEDIA_ROOT`, and static config.
|
||||
|
||||
Fix:
|
||||
|
||||
* Configure the web server to serve uploads as inert bytes (no execution), and consider forcing `Content-Disposition: attachment` for risky types.
|
||||
* Use a separate domain for user content when warranted. ([Django Project][2])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-PATH-001: Prevent path traversal and unsafe file serving (static/media separation)
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT treat user input as a filesystem path for reads/writes/serving.
|
||||
* MUST keep `MEDIA_ROOT` and `STATIC_ROOT` distinct; Django settings docs explicitly warn they must have different values to avoid security implications. ([Django Project][3])
|
||||
* SHOULD prefer using Django storage APIs keyed by server-side identifiers rather than accepting arbitrary relative paths from users.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `open(os.path.join(MEDIA_ROOT, request.GET["path"]))`
|
||||
* Download endpoints that take `?file=../../...` style parameters.
|
||||
* Misconfigured `MEDIA_ROOT == STATIC_ROOT`.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Grep for `open(`, `Path(`, `os.path.join(` used with request values.
|
||||
* Check `MEDIA_ROOT`, `STATIC_ROOT` in settings. ([Django Project][3])
|
||||
|
||||
Fix:
|
||||
|
||||
* Use server-side IDs mapped to known files.
|
||||
* Keep static and media separated and ensure the web server treats media as untrusted. ([Django Project][3])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-REDIRECT-001: Prevent open redirects (`next`, `return_to`, `redirect`)
|
||||
|
||||
Severity: Medium (High when combined with auth flows)
|
||||
|
||||
Required:
|
||||
|
||||
* MUST validate redirect targets derived from untrusted input (e.g., `next`, `return_to`).
|
||||
* SHOULD restrict to same-site relative paths or allowlisted hosts/schemes.
|
||||
* SHOULD use Django’s safe URL helpers (e.g., `django.utils.http.url_has_allowed_host_and_scheme`) rather than custom parsing.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* `return redirect(request.GET.get("next"))` with no validation.
|
||||
* Redirect allowlist implemented with naive string checks.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `redirect(` and track origin of the target.
|
||||
* Search for parameters named `next`, `return_to`, `redirect`, `url`.
|
||||
|
||||
Fix:
|
||||
|
||||
* Validate with allowlists and default to a safe internal path if validation fails.
|
||||
* Ensure host validation via `ALLOWED_HOSTS` remains strict (see DJANGO-HOST-001). ([Django Project][3])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-HEADERS-001: Enable essential security headers (SecurityMiddleware + clickjacking protection)
|
||||
|
||||
Severity: Medium to High
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD use `django.middleware.security.SecurityMiddleware` and configure it appropriately (production) for:
|
||||
|
||||
* `X-Content-Type-Options: nosniff` (Django setting `SECURE_CONTENT_TYPE_NOSNIFF`, default `True`). ([Django Project][3])
|
||||
* `Referrer-Policy` (Django setting `SECURE_REFERRER_POLICY`, default `'same-origin'`). ([Django Project][3])
|
||||
* COOP (Django setting `SECURE_CROSS_ORIGIN_OPENER_POLICY`, default `'same-origin'`). ([Django Project][3])
|
||||
* HTTPS redirects and HSTS as appropriate (see DJANGO-HTTPS-001). ([Django Project][3])
|
||||
* SHOULD enable clickjacking protection via X-Frame-Options middleware; Django security docs strongly recommend it for sites that don’t need third-party framing. ([Django Project][2])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Missing SecurityMiddleware.
|
||||
* Missing clickjacking protection (or disabling it globally) without a clear framing requirement.
|
||||
* Over-broad framing allowances for sensitive endpoints.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Inspect `MIDDLEWARE` for SecurityMiddleware and XFrameOptionsMiddleware.
|
||||
* Search for per-view disabling of framing/CSRF protections.
|
||||
|
||||
Fix:
|
||||
|
||||
* Add/enable middleware and configure the settings intentionally. ([Django Project][3])
|
||||
|
||||
NOTE:
|
||||
|
||||
* Some headers may be set at the edge (CDN/reverse proxy). If not visible in app code, flag as “verify at edge”.
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-CSP-001: Deploy a Content Security Policy (CSP) as defense-in-depth
|
||||
|
||||
Severity: Medium (High for apps rendering untrusted content)
|
||||
|
||||
NOTE: It is most important to set the CSP's script-src. All other directives are not as important and can generally be excluded for the ease of development.
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD deploy a CSP to mitigate XSS and content injection classes; Django’s security docs recommend CSP and note it is new in Django 6.0. ([Django Project][2])
|
||||
* MUST understand CSP limitations:
|
||||
|
||||
* Avoid excluding routes from CSP coverage; Django warns that an unprotected page can undermine protected pages due to same-origin policy. ([Django Project][2])
|
||||
* MAY start with `SECURE_CSP_REPORT_ONLY` to iterate safely (Django provides report-only support). ([Django Project][3])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* No CSP on apps that render user-controlled content.
|
||||
* CSP excludes “just a couple pages” (weakens overall protection), especially pages with any injection surface. ([Django Project][2])
|
||||
* CSP uses overly permissive directives (e.g., widespread `unsafe-inline`) without justification.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search `SECURE_CSP`, `SECURE_CSP_REPORT_ONLY`, and CSP middleware configuration.
|
||||
* Inspect reverse proxy/CDN config for CSP headers.
|
||||
|
||||
Fix:
|
||||
|
||||
* Implement a realistic CSP, ideally report-only first, then enforce. ([Django Project][3])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-AUTH-001: Password storage must use Django’s secure hashers; password policy must be configured
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST use Django’s built-in password hashing (never store plaintext or reversible encrypted passwords).
|
||||
* SHOULD prefer modern hashers and keep defaults updated; Django documents `PASSWORD_HASHERS` and includes modern options (Argon2, bcrypt, scrypt, PBKDF2 variants). ([Django Project][3])
|
||||
* SHOULD configure `AUTH_PASSWORD_VALIDATORS` (default is empty) for production password policy. ([Django Project][3])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Custom password storage or hashing.
|
||||
* Plaintext passwords stored in DB fields.
|
||||
* No password validation on consumer-facing apps.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search for `.set_password(` usage vs manual hashing.
|
||||
* Inspect settings for `PASSWORD_HASHERS` and `AUTH_PASSWORD_VALIDATORS`. ([Django Project][3])
|
||||
|
||||
Fix:
|
||||
|
||||
* Use Django auth user model APIs.
|
||||
* Enable password validators appropriate to the product’s risk profile. ([Django Project][3])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-AUTHZ-001: Authorization must be explicit and consistent
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST enforce authorization checks on every privileged action (view, modify, admin-like operations).
|
||||
* MUST NOT rely on UI-only restrictions (e.g., hiding buttons) without server-side permission checks.
|
||||
* SHOULD use Django’s permissions/groups and per-object authorization patterns where applicable.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Views that assume “user is logged in” implies “user may do action”.
|
||||
* Missing authorization checks on update/delete endpoints.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Enumerate views that modify state; ensure they validate ownership/permission.
|
||||
* Look for use of only `is_authenticated` or only `is_staff` without checking object-level access.
|
||||
|
||||
Fix:
|
||||
|
||||
* Add explicit permission checks and tests for unauthorized access.
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-ADMIN-001: Django admin must be treated as a high-value target
|
||||
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST ensure admin is protected by strong authentication and HTTPS-only transport (see DJANGO-HTTPS-001). ([Django Project][1])
|
||||
* SHOULD restrict admin exposure (network allowlists, VPN, SSO, or additional authentication controls) when possible.
|
||||
* SHOULD audit installed admin extensions and third-party apps for XSS/CSRF exposure.
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Admin exposed to the internet with weak authentication.
|
||||
* Admin served over HTTP.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Search `urlpatterns` for `admin.site.urls`.
|
||||
* Check deployment config for IP allowlisting or auth gateways.
|
||||
|
||||
Fix:
|
||||
|
||||
* Add network controls and enforce HTTPS.
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-LOG-001: Logging and error reporting must not leak secrets
|
||||
|
||||
Severity: Medium to High
|
||||
|
||||
Required:
|
||||
|
||||
* MUST NOT log secrets (including `SECRET_KEY`, session cookies, auth headers, password reset tokens).
|
||||
* MUST configure production logging deliberately; Django’s deployment checklist explicitly calls out reviewing logging before production. ([Django Project][1])
|
||||
* MUST ensure `DEBUG=False` in production so exceptions aren’t rendered with sensitive context. ([Django Project][1])
|
||||
|
||||
Insecure patterns:
|
||||
|
||||
* Logging full request headers or cookies in production.
|
||||
* Printing settings dictionaries.
|
||||
* Debug error pages.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Inspect `LOGGING` config; search for middleware that logs request headers/cookies.
|
||||
* Grep for `print(settings` / `logging.info(request.META)` patterns.
|
||||
|
||||
Fix:
|
||||
|
||||
* Redact sensitive values; log IDs not secrets.
|
||||
* Use structured logging and a safe error monitoring tool. ([Django Project][1])
|
||||
|
||||
---
|
||||
|
||||
### DJANGO-SUPPLY-001: Dependency and patch hygiene (Django + security-critical deps)
|
||||
|
||||
Severity: Medium (High if known vulnerable versions)
|
||||
|
||||
Required:
|
||||
|
||||
* SHOULD pin and regularly update Django and security-critical dependencies.
|
||||
* MUST respond to Django security releases promptly.
|
||||
|
||||
Detection hints:
|
||||
|
||||
* Check `requirements.txt`, lockfiles, build images.
|
||||
* Identify Django version; compare against latest supported release (Django’s download page publishes current stable and supported branches). ([Django Project][9])
|
||||
|
||||
Fix:
|
||||
|
||||
* Upgrade to patched versions; add regression tests for previously vulnerable classes.
|
||||
|
||||
---
|
||||
|
||||
## 5) Practical scanning heuristics (how to “hunt”)
|
||||
|
||||
When actively scanning, use these high-signal patterns:
|
||||
|
||||
* Deployment/dev server:
|
||||
|
||||
* `manage.py runserver`, `runserver 0.0.0.0`, `--insecure` ([Django Project][1])
|
||||
* Debug / settings:
|
||||
|
||||
* `DEBUG = True` ([Django Project][1])
|
||||
* `SECRET_KEY =`, `SECRET_KEY_FALLBACKS` ([Django Project][1])
|
||||
* Host validation:
|
||||
|
||||
* `ALLOWED_HOSTS = ['*']` ([Django Project][3])
|
||||
* HTTPS and proxy:
|
||||
|
||||
* `SECURE_SSL_REDIRECT`, `SECURE_HSTS_SECONDS`, `SECURE_PROXY_SSL_HEADER` ([Django Project][3])
|
||||
* Cookies / sessions:
|
||||
|
||||
* `SESSION_COOKIE_SECURE`, `SESSION_COOKIE_HTTPONLY`, `SESSION_COOKIE_SAMESITE` ([Django Project][3])
|
||||
* `CSRF_COOKIE_SECURE`, `CSRF_COOKIE_HTTPONLY`, `CSRF_COOKIE_SAMESITE` ([Django Project][3])
|
||||
* CSRF bypasses:
|
||||
|
||||
* `csrf_exempt`, missing `CsrfViewMiddleware`, POST forms without `{% csrf_token %}` ([Django Project][4])
|
||||
* XSS:
|
||||
|
||||
* `|safe`, `autoescape off`, `mark_safe(`, HTML string concatenation ([Django Project][5])
|
||||
* SQL injection:
|
||||
|
||||
* `.raw(`, `.extra(`, `RawSQL(`, `cursor.execute(` with formatted SQL strings ([Django Project][7])
|
||||
* User uploads / media:
|
||||
|
||||
* `request.FILES`, `MEDIA_ROOT`, `MEDIA_URL`, serving media inline; `MEDIA_ROOT == STATIC_ROOT` ([Django Project][1])
|
||||
* Redirects:
|
||||
|
||||
* `redirect(request.GET.get("next"))` patterns; missing allowlist validation
|
||||
* Security headers and CSP:
|
||||
|
||||
* Missing `SecurityMiddleware`, missing X-Frame-Options protection, missing `SECURE_CSP` adoption (where appropriate) ([Django Project][2])
|
||||
|
||||
Always try to confirm:
|
||||
|
||||
* data origin (untrusted vs trusted)
|
||||
* sink type (template/SQL/subprocess/files/redirect/http)
|
||||
* protective controls present (middleware, validation, allowlists, authz checks)
|
||||
* whether security headers/controls are set in-app vs at the edge
|
||||
|
||||
---
|
||||
|
||||
## 6) Sources (accessed 2026-01-27)
|
||||
|
||||
Primary Django documentation:
|
||||
|
||||
```text
|
||||
- Django Downloads (current stable & supported branches): https://www.djangoproject.com/download/
|
||||
- Django 6.0 Release Notes: https://docs.djangoproject.com/en/6.0/releases/6.0/
|
||||
- Django: Deployment checklist (incl. check --deploy, runserver warning, HTTPS/cookies guidance): https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
|
||||
- Django: Settings reference (SecurityMiddleware settings, cookies, SECRET_KEY_FALLBACKS, CSP settings): https://docs.djangoproject.com/en/6.0/ref/settings/
|
||||
- Django: Security in Django (XSS/CSRF/SQLi/clickjacking/HTTPS/host header validation/uploads/CSP): https://docs.djangoproject.com/en/6.0/topics/security/
|
||||
- Django: CSRF how-to (middleware, csrf_token usage, AJAX header patterns, csrf_exempt cautions): https://docs.djangoproject.com/en/6.0/howto/csrf/
|
||||
- Django: Performing raw SQL queries (parameterization guidance): https://docs.djangoproject.com/en/6.0/topics/db/sql/
|
||||
- Django: QuerySet API reference (extra() cautions; “do not quote placeholders” guidance): https://docs.djangoproject.com/en/6.0/ref/models/querysets/
|
||||
- Django: Template built-ins (autoescape tag): https://docs.djangoproject.com/en/6.0/ref/templates/builtins/
|
||||
- Django: Template language reference (turning off autoescape & risks): https://docs.djangoproject.com/en/6.0/ref/templates/language/
|
||||
- Django: Utilities reference (e.g., format_html): https://docs.djangoproject.com/en/6.0/ref/utils/
|
||||
```
|
||||
|
||||
OWASP:
|
||||
|
||||
```text
|
||||
- OWASP Cheat Sheet Series: Django Security Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Django_Security_Cheat_Sheet.html
|
||||
```
|
||||
|
||||
[1]: https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/ "https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/"
|
||||
[2]: https://docs.djangoproject.com/en/6.0/topics/security/ "Security in Django | Django documentation | Django"
|
||||
[3]: https://docs.djangoproject.com/en/6.0/ref/settings/ "Settings | Django documentation | Django"
|
||||
[4]: https://docs.djangoproject.com/en/6.0/howto/csrf/ "How to use Django’s CSRF protection | Django documentation | Django"
|
||||
[5]: https://docs.djangoproject.com/en/6.0/ref/templates/builtins/ "https://docs.djangoproject.com/en/6.0/ref/templates/builtins/"
|
||||
[6]: https://docs.djangoproject.com/en/6.0/ref/utils/ "https://docs.djangoproject.com/en/6.0/ref/utils/"
|
||||
[7]: https://docs.djangoproject.com/en/6.0/topics/db/sql/ "https://docs.djangoproject.com/en/6.0/topics/db/sql/"
|
||||
[8]: https://docs.djangoproject.com/en/6.0/ref/models/querysets/ "https://docs.djangoproject.com/en/6.0/ref/models/querysets/"
|
||||
[9]: https://www.djangoproject.com/download/ "Download Django | Django"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,705 @@
|
||||
# Flask (Python) Web Security Spec (Flask 3.1.x, Python 3.x)
|
||||
|
||||
This document is designed as a **security spec** that supports:
|
||||
1) **Secure-by-default code generation** for new Flask code.
|
||||
2) **Security review / vulnerability hunting** in existing Flask code (passive “notice issues while working” and active “scan the repo and report findings”).
|
||||
|
||||
It is intentionally written as a set of **normative requirements** (“MUST/SHOULD/MAY”) plus **audit rules** (what bad patterns look like, how to detect them, and how to fix/mitigate them).
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)
|
||||
|
||||
- MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session cookies, SECRET_KEY).
|
||||
- MUST NOT “fix” security by disabling protections (e.g., turning off CSRF, relaxing CORS, disabling escaping, disabling auth checks).
|
||||
- MUST provide **evidence-based findings** during audits: cite file paths, code snippets, and configuration values that justify the claim.
|
||||
- MUST treat uncertainty honestly: if a protection might exist in infrastructure (reverse proxy, WAF, CDN), report it as “not visible in app code; verify at runtime/config”.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 1) Operating modes
|
||||
|
||||
### 1.1 Generation mode (default)
|
||||
When asked to write new Flask code or modify existing code:
|
||||
- MUST follow every **MUST** requirement in this spec.
|
||||
- SHOULD follow every **SHOULD** requirement unless the user explicitly says otherwise.
|
||||
- MUST prefer safe-by-default APIs and proven libraries over custom security code.
|
||||
- MUST avoid introducing new risky sinks (template rendering from strings, shell execution, dynamic imports, unsafe redirects, serving user files as HTML, etc.).
|
||||
|
||||
### 1.2 Passive review mode (always on while editing)
|
||||
While working anywhere in a Flask repo (even if the user did not ask for a security scan):
|
||||
- MUST “notice” violations of this spec in touched/nearby code.
|
||||
- SHOULD mention issues as they come up, with a brief explanation + safe fix.
|
||||
|
||||
### 1.3 Active audit mode (explicit scan request)
|
||||
When the user asks to “scan”, “audit”, or “hunt for vulns”:
|
||||
- MUST systematically search the codebase for violations of this spec.
|
||||
- MUST output findings in a structured format (see §2.3).
|
||||
|
||||
Recommended audit order:
|
||||
1) App entrypoints / deployment scripts / Dockerfiles / Procfiles.
|
||||
2) Flask configuration and environment handling.
|
||||
3) Auth + sessions + cookies.
|
||||
4) CSRF protections and state-changing routes.
|
||||
5) Template rendering and XSS/SSTI.
|
||||
6) File handling (uploads + downloads) and path traversal.
|
||||
7) Injection classes (SQL, command execution, unsafe deserialization).
|
||||
8) Outbound requests (SSRF).
|
||||
9) Redirect handling (open redirects).
|
||||
10) CORS and security headers.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 2) Definitions and review guidance
|
||||
|
||||
### 2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)
|
||||
Examples include:
|
||||
- `request.args`, `request.form`, `request.values`
|
||||
- `request.get_json()`, `request.json`, `request.data`
|
||||
- `request.headers`, `request.cookies`
|
||||
- URL path parameters (e.g., `/user/<id>`)
|
||||
- Any data from external systems (webhooks, third-party APIs, message queues)
|
||||
- Any persisted user content (DB rows) that originated from users
|
||||
|
||||
### 2.2 State-changing request
|
||||
A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook send), or initiate privileged actions.
|
||||
|
||||
### 2.3 Required audit finding format
|
||||
For each issue found, output:
|
||||
|
||||
- Rule ID:
|
||||
- Severity: Critical / High / Medium / Low
|
||||
- Location: file path + function/route name + line(s)
|
||||
- Evidence: the exact code/config snippet
|
||||
- Impact: what could go wrong, who can exploit it
|
||||
- Fix: safe change (prefer minimal diff)
|
||||
- Mitigation: defense-in-depth if immediate fix is hard
|
||||
- False positive notes: what to verify if uncertain
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 3) Secure baseline: minimum production configuration (MUST in production)
|
||||
|
||||
This is the smallest “production baseline” that prevents common Flask misconfigurations.
|
||||
|
||||
### 3.1 App initialization pattern (SHOULD)
|
||||
SHOULD use an app factory and environment-based config so production config is not hard-coded.
|
||||
|
||||
Example skeleton (illustrative; adjust to your project):
|
||||
- Load config from environment / secret store.
|
||||
- Fail closed if critical settings are missing in production.
|
||||
|
||||
Key baseline config targets:
|
||||
- `SECRET_KEY` set and not committed
|
||||
- `SESSION_COOKIE_SECURE=True` (when HTTPS) IMPORTANT NOTE: Only set `Secure` in production environment when TLS is configured. When running in a local dev environment over HTTP, do not set `Secure` property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like `SESSION_COOKIE_SECURE` which can be used to disable `Secure` cookies when testing over HTTP.
|
||||
- `SESSION_COOKIE_HTTPONLY=True`
|
||||
- `SESSION_COOKIE_SAMESITE='Lax'` (or `'Strict'` if compatible)
|
||||
- `TRUSTED_HOSTS` set in production
|
||||
- Security headers set (CSP, etc.) either in app or at the edge
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 4) Rules (generation + audit)
|
||||
|
||||
Each rule contains: required practice, insecure patterns, detection hints, and remediation.
|
||||
|
||||
### FLASK-DEPLOY-001: Do not use Flask’s development server in production
|
||||
Severity: High (if production)
|
||||
|
||||
Required:
|
||||
- MUST NOT deploy the built-in development server as the production server.
|
||||
- MUST run behind a production-grade WSGI server or managed platform (such as gunicorn)
|
||||
|
||||
Insecure patterns:
|
||||
- `app.run(...)` in a production entrypoint.
|
||||
- Deployment docs/scripts that use `flask run` in production.
|
||||
|
||||
Detection hints:
|
||||
- Search for `app.run(`, `flask run`, `--debug`, `FLASK_DEBUG`, `FLASK_ENV=development`.
|
||||
- Check Docker CMD/ENTRYPOINT, Procfile, systemd units, shell scripts.
|
||||
|
||||
Fix:
|
||||
- Use a production WSGI server (and keep Flask as the app object).
|
||||
- Ensure the dev server is only used for local development.
|
||||
|
||||
Note:
|
||||
- These are often used in dev mode or local testing. This is allowed. Only flag if it is clear that it is being used as the production entrypoint
|
||||
|
||||
---
|
||||
|
||||
### FLASK-DEPLOY-002: Debug mode MUST be disabled in production
|
||||
Severity: Critical
|
||||
|
||||
Required:
|
||||
- MUST NOT enable debug mode in production.
|
||||
- MUST treat the interactive debugger as equivalent to remote code execution if exposed.
|
||||
|
||||
Insecure patterns:
|
||||
- `app.run(debug=True)`
|
||||
- `flask run --debug` in production.
|
||||
- `DEBUG=True` via env/config in production.
|
||||
|
||||
Detection hints:
|
||||
- Look for `debug=True`, `FLASK_DEBUG=1`, `DEBUG = True`, `app.debug = True`.
|
||||
- Look for `TRAP_HTTP_EXCEPTIONS`/debugger settings enabled in non-test contexts.
|
||||
|
||||
Fix:
|
||||
- Ensure debug is only enabled in local dev/test.
|
||||
- Prefer environment-based toggles and safe defaults.
|
||||
|
||||
Note:
|
||||
- These are often used in dev mode or local testing. This is allowed. Only flag if it is clear that it is being used as the production entrypoint
|
||||
|
||||
---
|
||||
|
||||
### FLASK-CONFIG-001: SECRET_KEY must be strong, secret, and rotated safely
|
||||
Severity: High (Critical if missing in production with sessions or signing)
|
||||
|
||||
Required:
|
||||
- MUST set a strong random `SECRET_KEY` in production.
|
||||
- MUST keep `SECRET_KEY` out of source control and out of logs.
|
||||
- MAY rotate keys periodically; MAY use `SECRET_KEY_FALLBACKS` to support rotation without instantly invalidating existing sessions, then remove old keys after the rotation window. This likely is not needed for smaller applications but is good practice for larger applications. As this may complicate deployment, suggest that it be implemented rather than implementing it by default.
|
||||
|
||||
Insecure patterns:
|
||||
- Missing `SECRET_KEY` in production.
|
||||
- Hard-coded `SECRET_KEY` in repo (including test keys accidentally used in prod).
|
||||
- Logging or printing `SECRET_KEY`.
|
||||
|
||||
Detection hints:
|
||||
- Search for `SECRET_KEY =`, `app.secret_key =`, `SECRET_KEY_FALLBACKS =`.
|
||||
- Check `.env` files committed to repo.
|
||||
- Check config modules for constants.
|
||||
|
||||
Fix:
|
||||
- Load from secret manager or environment variable.
|
||||
- Add a rotation process:
|
||||
- Set new `SECRET_KEY`
|
||||
- Keep old key(s) temporarily in `SECRET_KEY_FALLBACKS`
|
||||
- Remove old key(s) after the safe window.
|
||||
|
||||
Notes:
|
||||
- If the application uses Flask sessions (cookie-based by default), `SECRET_KEY` is directly security-critical.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-SESS-001: Session cookies must use secure attributes in production
|
||||
Severity: Medium
|
||||
|
||||
Required (production, HTTPS):
|
||||
- MUST set `SESSION_COOKIE_SECURE=True` (cookies only over HTTPS). NOTE: Only set `Secure` in production environment when TLS is configured. When running in a local dev environment over HTTP, do not set `Secure` property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like `SESSION_COOKIE_SECURE` which can be used to disable `Secure` cookies when testing over HTTP.
|
||||
- MUST ensure `SESSION_COOKIE_HTTPONLY=True` (protect from JS access).
|
||||
- SHOULD set `SESSION_COOKIE_SAMESITE='Lax'` (recommended) or `'Strict'` if compatible with UX.
|
||||
- SHOULD keep `SESSION_COOKIE_DOMAIN=None` unless you explicitly need subdomain-wide cookies.
|
||||
- If you need embedded/iframe third-party usage, MAY consider `SESSION_COOKIE_PARTITIONED=True` (requires HTTPS).
|
||||
|
||||
Insecure patterns:
|
||||
- `SESSION_COOKIE_SECURE=False` in production.
|
||||
- `SESSION_COOKIE_HTTPONLY=False`.
|
||||
- `SESSION_COOKIE_SAMESITE=None` with cookie-authenticated state-changing endpoints (higher CSRF risk).
|
||||
|
||||
Detection hints:
|
||||
- Inspect `app.config.update(...)` blocks and config classes.
|
||||
- Look for `set_cookie(..., secure=..., httponly=..., samesite=...)` usage on non-session cookies too.
|
||||
|
||||
Fix:
|
||||
- Set these config values explicitly in production config.
|
||||
|
||||
Notes:
|
||||
- SameSite is defense-in-depth; do not treat it as a full replacement for CSRF tokens.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-SESS-002: Sessions must be bounded and resistant to fixation/replay
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
- SHOULD set a bounded session lifetime appropriate to the app.
|
||||
- SHOULD set `session.permanent = True` only when you intend persistent sessions, and set `PERMANENT_SESSION_LIFETIME` to a justified value.
|
||||
- SHOULD clear the session on login and privilege changes to reduce session fixation risk.
|
||||
- MUST NOT store sensitive secrets in the default Flask session cookie. The default session is signed, not encrypted.
|
||||
|
||||
Insecure patterns:
|
||||
- Extremely long or unlimited lifetimes for privileged sessions.
|
||||
- No session clearing on login.
|
||||
- Storing secrets (passwords, access tokens, PII) directly in `session[...]` when using default cookie sessions.
|
||||
|
||||
Detection hints:
|
||||
- Search for `PERMANENT_SESSION_LIFETIME`, `session.permanent`, `session[...] =`.
|
||||
- Identify whether server-side session storage is used; if not, assume default cookie sessions.
|
||||
|
||||
Fix:
|
||||
- Set appropriate lifetimes.
|
||||
- Clear/rotate session on login.
|
||||
- Store sensitive data server-side; store only identifiers in the session cookie.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-CSRF-001: State-changing requests using cookie auth MUST be CSRF-protected
|
||||
Severity: High
|
||||
|
||||
- IMPORTANT NOTE: If cookies are not being used for auth (ie auth is via Authentication header or other passed token), then there is no CSRF risk.
|
||||
|
||||
Required:
|
||||
- MUST protect all state-changing endpoints (POST/PUT/PATCH/DELETE) that rely on cookies for authentication.
|
||||
- MAY use a well-tested CSRF library/integration (form framework or middleware) rather than rolling your own.
|
||||
- MAY use additional defenses (Origin/Referer checking, SameSite cookies, Fetch Metadata headers, custom headers for AJAX/API), but tokens remain the primary defense for cookie-authenticated apps.
|
||||
If tokens are impractical, or for small applications:
|
||||
* MUST at a minimum require a custom header to be set and set the session cookie SESSION_COOKIE_SAMESITE=lax, as this is the strongest method besides requiring a form token, and may be much easier to implement.
|
||||
|
||||
Insecure patterns:
|
||||
- Cookie-authenticated endpoints that change state with no CSRF protection.
|
||||
- Using GET for state-changing actions (amplifies CSRF risk).
|
||||
|
||||
Detection hints:
|
||||
- Enumerate routes with methods other than GET and identify auth mechanism.
|
||||
- Look for CSRF integrations (e.g., Flask-WTF, global CSRF middleware). If absent, treat as suspicious.
|
||||
- Check JSON API endpoints too, not only HTML forms.
|
||||
|
||||
Fix:
|
||||
- Add CSRF protection to all state-changing requests.
|
||||
- If the app is a pure API and uses Authorization headers (bearer tokens) rather than cookies, document that choice and ensure cookies aren’t used for auth. If cookies are not used for auth, there is no CSRF risk.
|
||||
|
||||
Notes:
|
||||
- XSS can defeat CSRF protections; CSRF defenses do not replace XSS prevention.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-XSS-001: Prevent reflected/stored XSS in templates and HTML generation
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
- MUST rely on Jinja auto-escaping for HTML templates.
|
||||
- MUST NOT mark untrusted content as safe:
|
||||
- Avoid `Markup(...)` on user data.
|
||||
- Avoid Jinja `|safe` on user-controlled content.
|
||||
- MUST quote HTML attributes containing Jinja expressions (`value="{{ x }}"` not `value={{ x }}`).
|
||||
- MUST NOT serve uploaded HTML as active HTML; serve as download (`Content-Disposition: attachment`) or transform to a safe format. Note: This is only relevant if it is possible to upload document content such as html, js, css, etc. If it purely is image files, there is no concern.
|
||||
- SHOULD deploy a Content Security Policy (CSP) to mitigate XSS classes (including `javascript:` in `href`).
|
||||
|
||||
Insecure patterns:
|
||||
- `Markup(request.args.get(...))`
|
||||
- Template filters: `{{ user_html|safe }}`
|
||||
- Unquoted attributes in templates
|
||||
- Serving user-uploaded content directly with `text/html` or inline rendering
|
||||
|
||||
Detection hints:
|
||||
- Search for `Markup(` and investigate origin of the data.
|
||||
- Search template files for `|safe`, `|tojson` misuse, and unquoted attributes.
|
||||
- Review file-serving routes that might return user uploads without `as_attachment=True`. Note: This is only relevant if it is possible to upload document content such as html, js, css, etc. If it purely is image files, there is no concern.
|
||||
|
||||
Fix:
|
||||
- Remove unsafe marking; sanitize only when strictly necessary using a trusted HTML sanitizer.
|
||||
- Always quote attributes.
|
||||
- Add CSP and reduce inline scripts.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-SSTI-001: Never render untrusted templates (Server-Side Template Injection)
|
||||
Severity: Critical
|
||||
|
||||
Required:
|
||||
- MUST NOT render templates that contain user-controlled template syntax.
|
||||
- MUST treat `render_template_string` and `Environment.from_string(...).render(...)` as dangerous if the template string is influenced by untrusted input.
|
||||
- MUST NOT use use `.format()` on user controlled strings
|
||||
- If untrusted templates are absolutely required, treat it as a special high-risk design:
|
||||
- MUST use a sandboxed templating approach and restrict capabilities.
|
||||
- MUST keep Jinja updated and assume sandbox escapes are possible; isolate further.
|
||||
|
||||
Insecure patterns:
|
||||
- `render_template_string(request.args["tmpl"], ...)`
|
||||
- Storing user templates in DB and rendering them with the normal Jinja environment.
|
||||
- `request.args["tmpl"].format(...)`
|
||||
|
||||
Detection hints:
|
||||
- Grep for `render_template_string`, `from_string`, `.render(` with dynamic strings.
|
||||
- Trace the origin of the template string (DB, request, uploads, admin panels).
|
||||
|
||||
Fix:
|
||||
- Replace with safe templating alternatives that do not evaluate code (e.g., string.Template, str.replace).
|
||||
- If templates must be user-defined, use a sandbox plus strict allowlists and heavy isolation.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-HEADERS-001: Set essential security headers (in app or at the edge)
|
||||
Severity: Medium
|
||||
|
||||
Required (typical web app):
|
||||
- SHOULD set:
|
||||
- CSP (`Content-Security-Policy`)
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
- Clickjacking protection (`X-Frame-Options: SAMEORIGIN` and/or CSP `frame-ancestors`) (there may be cases where the user wants to iframe their site elsewhere. If that is the case, work with them to safely allow it)
|
||||
- SHOULD consider additional hardening headers depending on app (Referrer-Policy, Permissions-Policy).
|
||||
- MUST ensure cookies are set with secure attributes (see FLASK-SESS-001).
|
||||
|
||||
NOTE: Security headers may be set via a proxy or other cloud provider. Check to see if there is evidence of that.
|
||||
|
||||
Insecure patterns:
|
||||
- No security headers anywhere (app or edge).
|
||||
- CSP missing on apps that display untrusted content.
|
||||
|
||||
Detection hints:
|
||||
- Search for `after_request` hooks, Flask-Talisman usage, reverse proxy config.
|
||||
- If not visible in app code, flag as “verify at edge”.
|
||||
|
||||
Fix:
|
||||
- Set headers centrally (middleware / after_request) or via reverse proxy/CDN.
|
||||
- Keep CSP realistic and compatible; avoid `unsafe-inline` where possible.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-LIMITS-001: Request size and form parsing limits MUST be set appropriately
|
||||
Severity: Low (Medium if file uploads / large bodies are possible)
|
||||
|
||||
Required:
|
||||
- SHOULD set and justify:
|
||||
- `MAX_CONTENT_LENGTH` (global maximum request bytes)
|
||||
- `MAX_FORM_MEMORY_SIZE` (max per non-file form field in multipart)
|
||||
- `MAX_FORM_PARTS` (max number of multipart fields)
|
||||
- MUST enforce additional limits at the reverse proxy / WSGI / platform level where possible.
|
||||
|
||||
Insecure patterns:
|
||||
- Unlimited request body sizes when handling uploads or user content.
|
||||
- Accepting arbitrarily large multipart forms or many fields.
|
||||
|
||||
Detection hints:
|
||||
- Inspect Flask config for these keys.
|
||||
- Inspect upload routes and APIs that accept large JSON.
|
||||
|
||||
Fix:
|
||||
- Set conservative defaults, override per-route only when needed.
|
||||
- Ensure large uploads use dedicated upload mechanisms.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-HOST-001: Host header must be validated in production
|
||||
Severity: Low (depends on app’s use of external URLs)
|
||||
|
||||
Required:
|
||||
- MUST set `TRUSTED_HOSTS` in production to restrict accepted Host values.
|
||||
- MUST NOT rely on `SERVER_NAME` as a host restriction mechanism.
|
||||
|
||||
Insecure patterns:
|
||||
- `TRUSTED_HOSTS` unset in production.
|
||||
- Code that generates external URLs for emails/password resets without host validation.
|
||||
|
||||
Detection hints:
|
||||
- Find `TRUSTED_HOSTS` config usage.
|
||||
- Find `url_for(..., _external=True)` and check how host is determined.
|
||||
|
||||
Fix:
|
||||
- Set `TRUSTED_HOSTS` to your expected domains (and required subdomains).
|
||||
- Ensure external URL generation uses trusted host/scheme.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-PROXY-001: Reverse proxy trust must be configured correctly
|
||||
Severity: Medium (High if relying on IPs for auth)
|
||||
|
||||
Required:
|
||||
- If behind a reverse proxy, MUST configure Flask/Werkzeug to trust forwarded headers only from the intended proxy.
|
||||
- MUST NOT blindly trust `X-Forwarded-*` headers from the open internet.
|
||||
|
||||
Insecure patterns:
|
||||
- `ProxyFix` applied with overly broad trust settings, or applied without understanding how many proxies are in front.
|
||||
- Relying on forwarded headers for scheme/host without validation.
|
||||
|
||||
Detection hints:
|
||||
- Search for `ProxyFix`.
|
||||
- Search for usage of `request.remote_addr`, `request.scheme`, `request.host` in security-sensitive logic.
|
||||
|
||||
Fix:
|
||||
- Configure `ProxyFix` (or platform-specific settings) with correct hop counts.
|
||||
- Keep `TRUSTED_HOSTS` in place even behind proxies.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-PATH-001: Prevent path traversal and unsafe file serving
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
- MUST NOT pass user-controlled file paths to `send_file` or to direct file I/O.
|
||||
- MUST use safe file serving patterns:
|
||||
- `send_from_directory` for user-specified paths under a trusted base directory
|
||||
- `safe_join` for joining a trusted base directory with untrusted path components
|
||||
- `secure_filename` for uploaded filenames (and still generate your own unique storage name)
|
||||
- MUST ensure user uploads are not served as executable/active content (especially HTML).
|
||||
- SHOULD in general use `safe_join` over `os.path.join` for almost any filesystem path computations.
|
||||
|
||||
Insecure patterns:
|
||||
- `send_file(request.args["path"])`
|
||||
- `open(os.path.join(base_dir, user_path))` where `user_path` is untrusted
|
||||
- Serving uploads from within a static web root without restrictions
|
||||
|
||||
Detection hints:
|
||||
- Search for `send_file(`, `open(`, `os.path.join(`, `pathlib.Path(...)/...` in file routes.
|
||||
- Identify where filenames come from (request args, DB, headers).
|
||||
|
||||
Fix:
|
||||
- Serve only from a non-user-controlled directory base.
|
||||
- Store uploads outside static roots; serve through controlled routes.
|
||||
- Always validate and normalize file identifiers.
|
||||
|
||||
Note: `safe_join` is imported from `werkzeug.security`
|
||||
|
||||
---
|
||||
|
||||
### FLASK-UPLOAD-001: File uploads must be validated, stored safely, and served safely
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
- MUST enforce upload size limits (app + edge).
|
||||
- MUST validate file type using allowlists and content checks (not only extension).
|
||||
- MUST store uploads outside executable/static roots when possible.
|
||||
- SHOULD generate server-side filenames (random IDs) and avoid trusting original names.
|
||||
- MUST serve potentially active formats safely (download attachment) unless explicitly intended.
|
||||
|
||||
Insecure patterns:
|
||||
- Accepting arbitrary file types and serving them back inline.
|
||||
- Using user-supplied filename as storage path.
|
||||
- Missing size/type validation.
|
||||
|
||||
Detection hints:
|
||||
- Look for `request.files[...]` handlers.
|
||||
- Check for `secure_filename` usage (and whether it’s combined with uniqueness).
|
||||
- Check where files are stored and how they are served.
|
||||
|
||||
Fix:
|
||||
- Implement allowlist validation + safe storage + safe serving.
|
||||
- Add scanning / quarantine if applicable.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-INJECT-001: Prevent SQL injection (use parameterized queries / ORM)
|
||||
Severity: High
|
||||
|
||||
Required:
|
||||
- MUST use parameterized queries or an ORM that parameterizes under the hood.
|
||||
- MUST NOT build SQL by string concatenation / f-strings with untrusted input.
|
||||
|
||||
Insecure patterns:
|
||||
- `f"SELECT ... WHERE id={request.args['id']}"`
|
||||
- `"... WHERE name = '%s'" % user_input`
|
||||
|
||||
Detection hints:
|
||||
- Grep for `SELECT`, `INSERT`, `UPDATE`, `DELETE` strings in Python code.
|
||||
- Track untrusted data into DB execute calls.
|
||||
|
||||
Fix:
|
||||
- Replace with parameterized queries or ORM query APIs.
|
||||
- Validate types (e.g., int IDs) before querying.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-INJECT-002: Prevent OS command injection
|
||||
Severity: Critical to High (depends on exposure)
|
||||
|
||||
Required:
|
||||
- MUST avoid executing shell commands with untrusted input.
|
||||
- If subprocess is necessary:
|
||||
- MUST pass args as a list (not a string)
|
||||
- MUST NOT use `shell=True` with attacker-influenced strings
|
||||
- SHOULD use strict allowlists for any variable component
|
||||
- If possible, use pure python or a python library rather than using a subprocess or system command
|
||||
- Do not assume that arguments to commands will be inherently safe even in `shell=False`. Commands may incorrectly process these arguments as command line flags or other trusted values.
|
||||
|
||||
Insecure patterns:
|
||||
- `os.system(user_input)`
|
||||
- `subprocess.run(f"cmd {user}", shell=True)`
|
||||
- Passing user strings into `bash -c`, `sh -c`, PowerShell, etc.
|
||||
|
||||
Detection hints:
|
||||
- Search for `os.system`, `subprocess`, `Popen`, `shell=True`.
|
||||
- Trace data from request/DB into these calls.
|
||||
|
||||
Fix:
|
||||
- Use library APIs instead of shell commands.
|
||||
- If unavoidable, hard-code the command and allowlist validated parameters. If supported by the subcommand, try to keep user values after `--` to prevent them being processed as command line flags.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-SSRF-001: Prevent server-side request forgery (SSRF) in outbound HTTP
|
||||
Severity: Medium
|
||||
|
||||
- Note: For small stand alone projects this is less important. It is most important when deploying into an LAN or with other services listening on the same server.
|
||||
|
||||
Required:
|
||||
- MUST treat outbound requests to user-provided URLs as high risk.
|
||||
- SHOULD validate and restrict destinations (allowlist hosts/domains) for any user-influenced URL fetch.
|
||||
- SHOULD block access to:
|
||||
- localhost / private IP ranges / link-local addresses
|
||||
- cloud metadata endpoints
|
||||
- MUST NOT allow non http/https protocols (ie file: etc)
|
||||
- SHOULD set timeouts and restrict redirects.
|
||||
|
||||
|
||||
|
||||
Insecure patterns:
|
||||
- `requests.get(request.args["url"])`
|
||||
- Webhooks/preview/fetch endpoints that accept arbitrary URLs.
|
||||
|
||||
Detection hints:
|
||||
- Search for `requests.get/post`, `httpx`, `urllib`, `aiohttp` usage with untrusted URL sources.
|
||||
- Identify URL fetch features (preview, import, webhook tester).
|
||||
|
||||
Fix:
|
||||
- Ensure URLs are http or https (disallow file: or other protocols)
|
||||
- Enforce allowlists and network egress controls.
|
||||
- Add strict parsing and IP resolution checks; set timeouts; disable redirects if not needed.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-REDIRECT-001: Prevent open redirects
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
- MUST validate redirect targets derived from untrusted input (e.g., `next`, `redirect`, `return_to`).
|
||||
- SHOULD use allowlists of internal paths or known domains.
|
||||
- SHOULD prefer redirecting only to same-site relative paths.
|
||||
|
||||
Insecure patterns:
|
||||
- `redirect(request.args.get("next"))` with no validation.
|
||||
|
||||
Detection hints:
|
||||
- Search for `redirect(` and examine where `location` comes from.
|
||||
|
||||
Fix:
|
||||
- Only allow relative paths or allowlisted domains.
|
||||
- Fall back to a safe default if validation fails.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-HTTP-001: Use HTTP methods safely; do not change state via GET; avoid secrets in URLs
|
||||
Severity: Medium
|
||||
|
||||
Required:
|
||||
- MUST NOT perform state-changing actions over GET.
|
||||
- MUST NOT put secrets in URLs (query strings are commonly logged and leaked via referrers).
|
||||
- SHOULD require POST/PUT/PATCH/DELETE for state change and apply CSRF protections when cookie-authenticated.
|
||||
|
||||
Insecure patterns:
|
||||
- `/delete?id=...` implemented as GET
|
||||
- Password reset tokens or API keys in query params
|
||||
|
||||
Detection hints:
|
||||
- Enumerate GET routes and inspect whether they mutate state.
|
||||
- Look for URL parameters named `token`, `key`, `secret`, `password`, etc.
|
||||
|
||||
Fix:
|
||||
- Move state changes to non-GET methods.
|
||||
- Move sensitive values to secure channels (POST bodies, headers) and protect them.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-CORS-001: CORS must be explicit and least-privilege
|
||||
Severity: Medium (High if misconfigured with credentials)
|
||||
|
||||
Required:
|
||||
- If CORS is not needed, MUST keep it disabled.
|
||||
- If CORS is needed:
|
||||
- MUST allowlist trusted origins (do not reflect arbitrary origins).
|
||||
- MUST be careful with credentialed requests; do not combine broad origins with cookies.
|
||||
- SHOULD restrict allowed methods and headers.
|
||||
|
||||
Insecure patterns:
|
||||
- `Access-Control-Allow-Origin: *` paired with credentialed cookies or overly broad access.
|
||||
- Reflecting `Origin` without validation.
|
||||
- `flask_cors.CORS(app)` with permissive defaults.
|
||||
|
||||
Detection hints:
|
||||
- Search for `flask_cors`, `CORS(`, `Access-Control-Allow-Origin`.
|
||||
- Check for `supports_credentials=True` and wildcard origins.
|
||||
|
||||
Fix:
|
||||
- Use a strict origin allowlist and minimal methods/headers.
|
||||
- Ensure cookie-authenticated endpoints are not exposed cross-origin unless necessary.
|
||||
|
||||
---
|
||||
|
||||
### FLASK-SUPPLY-001: Dependency and patch hygiene (focus on security-relevant deps)
|
||||
Severity: Low
|
||||
|
||||
Required:
|
||||
- SHOULD pin and regularly update security-critical dependencies (Flask, Werkzeug, Jinja2, itsdangerous).
|
||||
- MUST respond to known security advisories promptly.
|
||||
|
||||
Audit focus example:
|
||||
- If running on Windows and using file serving with untrusted paths, ensure Werkzeug’s `safe_join` behavior is not vulnerable to Windows device-name edge cases.
|
||||
|
||||
Detection hints:
|
||||
- Check `requirements.txt`, lockfiles, and runtime environments.
|
||||
- Identify where security helpers are used (safe_join, send_from_directory).
|
||||
|
||||
Fix:
|
||||
- Upgrade to patched versions and add regression tests for the impacted behavior.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 5) Practical scanning heuristics (how to “hunt”)
|
||||
|
||||
When actively scanning, use these high-signal patterns:
|
||||
|
||||
- Dev server / debug:
|
||||
- `app.run(`, `flask run`, `--debug`, `DEBUG=True`, `FLASK_DEBUG`
|
||||
- Secrets:
|
||||
- `SECRET_KEY`, `secret_key`, `.env` committed, `print(config)`
|
||||
- Cookies / sessions:
|
||||
- `SESSION_COOKIE_SECURE`, `SESSION_COOKIE_HTTPONLY`, `SESSION_COOKIE_SAMESITE`
|
||||
- `session[...] =` with sensitive values
|
||||
- CSRF:
|
||||
- POST/PUT/PATCH/DELETE handlers without CSRF checks in cookie-authenticated apps
|
||||
- XSS/SSTI:
|
||||
- `Markup(`, `|safe`, unquoted attributes, `render_template_string`
|
||||
- Files:
|
||||
- `send_file(` with user-controlled path; `open(` on user path; `os.path.join` with untrusted
|
||||
- upload handlers using user filename for path
|
||||
- Injection:
|
||||
- SQL strings + string formatting into `.execute(...)`
|
||||
- `subprocess.*`, `shell=True`, `os.system`
|
||||
- SSRF:
|
||||
- `requests.get/post` or `httpx` with URL from request/DB
|
||||
- Redirect:
|
||||
- `redirect(request.args.get("next"))`
|
||||
- CORS:
|
||||
- `flask_cors.CORS` permissive configs; wildcard origins with credentials
|
||||
|
||||
Always try to confirm:
|
||||
- data origin (untrusted vs trusted)
|
||||
- sink type (template/SQL/subprocess/files/redirect/http)
|
||||
- protective controls present (validation, allowlists, middleware)
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
## 6) Sources (accessed 2026-01-26)
|
||||
|
||||
Primary framework documentation:
|
||||
- Flask Docs: Deploying to Production — https://flask.palletsprojects.com/en/stable/deploying/
|
||||
- Flask Docs: Debugging Application Errors — https://flask.palletsprojects.com/en/stable/debugging/
|
||||
- Flask Docs: Configuration Handling — https://flask.palletsprojects.com/en/stable/config/
|
||||
- Flask Docs: Security Considerations — https://flask.palletsprojects.com/en/stable/web-security/
|
||||
- Flask Docs: Tell Flask it is Behind a Proxy — https://flask.palletsprojects.com/en/stable/deploying/proxy_fix/
|
||||
- Flask API Docs: Sessions — https://flask.palletsprojects.com/en/stable/api/#sessions
|
||||
|
||||
Werkzeug documentation & advisories:
|
||||
- Werkzeug Docs: Utilities (send_file / send_from_directory / safe_join / secure_filename / password hashing) — https://werkzeug.palletsprojects.com/en/stable/utils/
|
||||
- GitHub Advisory: CVE-2025-66221 (Werkzeug safe_join Windows device names) — https://github.com/advisories/GHSA-hgf8-39gv-g3f2
|
||||
|
||||
OWASP Cheat Sheet Series:
|
||||
- Session Management — https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
|
||||
- CSRF Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
|
||||
- XSS Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
|
||||
- Input Validation — https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html
|
||||
- SQL Injection Prevention — https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
|
||||
- Injection Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Injection_Prevention_Cheat_Sheet.html
|
||||
- OS Command Injection Defense — https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html
|
||||
- SSRF Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
|
||||
- File Upload — https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html
|
||||
- Unvalidated Redirects — https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html
|
||||
- HTTP Headers — https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html
|
||||
|
||||
Template safety references:
|
||||
- Jinja: Sandbox (rendering untrusted templates) — https://jinja.palletsprojects.com/en/stable/sandbox/
|
||||
- OWASP WSTG: Testing for Server-Side Template Injection — https://owasp.org/www-project-web-security-testing-guide/v41/4-Web_Application_Security_Testing/07-Input_Validation_Testing/18-Testing_for_Server_Side_Template_Injection
|
||||
- PortSwigger Web Security Academy: Server-side template injection — https://portswigger.net/web-security/server-side-template-injection
|
||||
|
||||
HTTP semantics:
|
||||
- RFC 9110: HTTP Semantics (safe methods) — https://www.rfc-editor.org/rfc/rfc9110
|
||||
Reference in New Issue
Block a user