# Security

> Web security rules for modern frontend development - OWASP Top 10 (2025), HTTPS and HSTS, security headers, Content Security Policy (CSP), XSS prevention, CSRF prevention, clickjacking, CORS configuration, input validation and sanitization, SQL/NoSQL injection prevention with parameterized queries, authentication security and secrets, password hashing (bcrypt/argon2), rate limiting and anti-abuse, supply chain and dependency auditing, subresource integrity, prototype pollution, security testing, framework-specific security (React/Next/Astro/Vite)

- Skill: `14bryanespinoza/security` (Agent Skill)
- Install (CLI): `npx skillmds@latest add 14bryanespinoza/security`
- Raw SKILL.md: https://api.skillmd.com/api/skills/14bryanespinoza/security/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: 14BryanEspinoza (https://skillmd.com/u/14bryanespinoza)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/14bryanespinoza/security

---


# Security — Web Security for Modern Frontends

---

## 1. Philosophy

1. **Defense in depth** — Multiple layers: headers, CSP, validation, framework protections.
2. **Secure by default** — Frameworks protect you; don't disable protections.
3. **Minimal attack surface** — Least privilege, minimal dependencies, no secrets in code.
4. **Detect and respond** — Logging, monitoring, incident response plan.
5. **Supply chain integrity** — Pin dependencies, verify provenance, audit regularly.

---

## 2. Threat Model (OWASP 2025 Essential)

| Risk                               | Mitigation                                     |
| ---------------------------------- | ---------------------------------------------- |
| **A01: Broken Access Control**     | Server-side auth checks, RBAC, deny by default |
| **A02: Cryptographic Failures**    | TLS 1.3, Argon2id, RS256, key rotation         |
| **A03: Injection**                 | Parameterized queries, input validation, ORM   |
| **A04: Insecure Design**           | Threat modeling, secure patterns, code review  |
| **A05: Security Misconfiguration** | Hardened headers, CSP, no defaults             |
| **A06: Vulnerable Components**     | Dependency scanning, pinning, updates          |
| **A07: Auth Failures**             | MFA, rotation, reuse detection, rate limits    |
| **A08: Software Integrity**        | SRI, signed commits, CI verification           |
| **A09: Logging Failures**          | Structured logs, alerting, no secrets          |
| **A10: SSRF**                      | Allowlist URLs, no user-controlled fetch       |

---

## 3. HTTPS & HSTS

```nginx
# TLS 1.3 only
ssl_protocols TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;

# HSTS (1 year, include subdomains, preload)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
```

### Rules

- **TLS 1.3 only** — disable 1.2 and below
- **HSTS with preload** — submit to hstspreload.org
- **Certificates** — 90-day rotation (Let's Encrypt), monitor expiry
- **No mixed content** — all resources HTTPS

---

## 4. Security Headers

| Header                         | Value                                      | Purpose                   |
| ------------------------------ | ------------------------------------------ | ------------------------- |
| `Content-Security-Policy`      | See §5                                     | XSS, injection prevention |
| `X-Frame-Options`              | `DENY`                                     | Clickjacking              |
| `X-Content-Type-Options`       | `nosniff`                                  | MIME sniffing             |
| `Referrer-Policy`              | `strict-origin-when-cross-origin`          | Referrer leakage          |
| `Permissions-Policy`           | `camera=(), microphone=(), geolocation=()` | Feature control           |
| `Cross-Origin-Opener-Policy`   | `same-origin`                              | COOP                      |
| `Cross-Origin-Resource-Policy` | `same-origin`                              | CORP                      |
| `Cross-Origin-Embedder-Policy` | `require-corp`                             | COEP                      |

```nginx
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
```

---

## 5. Content Security Policy (CSP)

### Essential policy

```http
Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'wasm-unsafe-eval';  # wasm for some libs
  style-src 'self' 'unsafe-inline';       # Tailwind/inline styles
  img-src 'self' data: https:;            # images + data URIs
  font-src 'self' data:;                  # fonts
  connect-src 'self' https://api.example.com;  # API + websockets
  frame-src 'none';                       # no iframes
  object-src 'none';                      # no plugins
  base-uri 'self';                        # base tag
  form-action 'self';                     # form targets
  frame-ancestors 'none';                 # embedding
  block-all-mixed-content;                # HTTPS only
  upgrade-insecure-requests;              # upgrade HTTP
```

### Rules CSP

- **Report-only first** — `Content-Security-Policy-Report-Only` to test
- **Nonce for inline scripts** — `script-src 'nonce-<random>'`
- **No `unsafe-eval`** — except WASM if needed
- **Report URI** — `report-uri /csp-report` for monitoring

---

## 6. XSS Prevention

### Server-side

```ts
// Escape for HTML context
import { escapeHtml } from "escape-html";
const safe = escapeHtml(userInput);

// Sanitize HTML (if user HTML allowed)
import DOMPurify from "isomorphic-dompurify";
const clean = DOMPurify.sanitize(dirtyHtml, { ALLOWED_TAGS: ["b", "i", "em"] });
```

### Client-side

```tsx
// React: auto-escapes by default
<div>{userContent}</div>  // ✅ Safe

// Dangerous - avoid
<div dangerouslySetInnerHTML={{ __html: userContent }} />  // ❌ Unless sanitized
```

### Rules XSS Prevention

- **Framework auto-escaping** — React, Vue, Svelte escape by default
- **Never `dangerouslySetInnerHTML`** without DOMPurify
- **CSP as backup** — blocks injected scripts
- **`Content-Type: application/json`** — prevents sniffing

---

## 7. CSRF Prevention

> **Auth integration**: see `auth` skill.

### SameSite cookies (primary)

```http
Set-Cookie: session=...; SameSite=Lax; Secure; HttpOnly
Set-Cookie: csrf_token=...; SameSite=Strict; Secure; HttpOnly
```

### Double-submit (fallback)

```ts
// Generate
const csrfToken = crypto.randomBytes(32).toString("hex");
res.cookie("csrf_token", csrfToken, {
  sameSite: "strict",
  secure: true,
  httpOnly: true,
});

// Validate (non-GET)
if (req.method !== "GET") {
  const headerToken = req.headers["x-csrf-token"];
  const cookieToken = req.cookies.csrf_token;
  if (!headerToken || headerToken !== cookieToken) {
    throw new Error("CSRF token mismatch");
  }
}
```

### Rules CSRF

- **`SameSite=Lax`** for session — allows navigation
- **`SameSite=Strict`** for CSRF token — no cross-site send
- **Custom header** — `X-CSRF-Token` for SPA mutations
- **Exempt GET/HEAD** — safe methods

---

## 8. Clickjacking

```http
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none';
```

### Rules Clickjacking

- **`X-Frame-Options: DENY`** — no embedding
- **CSP `frame-ancestors 'none'`** — modern replacement
- **Allow specific** only if business requires:
  `frame-ancestors https://partner.example.com`

---

## 9. CORS

```http
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, Idempotency-Key, X-CSRF-Token
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
```

### Rules CORS

- **Exact origin** — no `*` with credentials
- **Preflight cache** — `Max-Age: 86400`
- **Allow only needed headers** — `Authorization`, `Content-Type`, `Idempotency-Key`, `X-CSRF-Token`

---

## 10. Input Validation

```ts
import { z } from "zod";

const schema = z.object({
  email: z.string().email(),
  age: z.number().int().min(13).max(120),
  tags: z.array(z.string().max(50)).max(10),
  metadata: z.record(z.string()).optional(),
});

// Validate at boundary
const result = schema.safeParse(input);
if (!result.success) {
  throw new ValidationError(result.error.flatten());
}
```

### Rules Input Validation

- **Validate at boundaries** — API entry, form submit
- **Schema-based** — Zod, Valibot, ArkType
- **Reject unknown** — `strict()` mode
- **Sanitize, don't just validate** — HTML, SQL, paths

---

## 11. Injection Prevention

### SQL (parameterized)

```ts
// ✅ Safe
const users = await db.query("SELECT * FROM users WHERE email = $1", [email]);

// ❌ Unsafe
const users = await db.query(`SELECT * FROM users WHERE email = '${email}'`);
```

### NoSQL (whitelist operators)

```ts
// ✅ Safe
const users = await db.users.find({ email: { $eq: email } });

// ❌ Unsafe
const users = await db.users.find({ $where: `this.email == '${email}'` });
```

### Path traversal

```ts
// ✅ Safe
const safePath = path.resolve(baseDir, userInput);
if (!safePath.startsWith(baseDir)) throw new Error("Invalid path");

// ❌ Unsafe
fs.readFile(`/var/www/${userInput}`);
```

### Rules Injections Prevention

- **Always parameterized** — never string concat
- **ORM preferred** — Prisma, Drizzle, TypeORM
- **Whitelist operators** — reject `$where`, `$expr`
- **Path resolution** — `path.resolve` + prefix check

---

## 12. Auth & Secrets Security

> **Auth patterns**: see `auth` skill.

### Secrets management

```bash
# Never in code
# .env.local (gitignored)
JWT_SECRET=...
DATABASE_URL=...

# Production: secret manager
# AWS Secrets Manager, GCP Secret Manager, Vault
```

### Rules Auth & Secrets Security

- **No secrets in repo** — `.env*` in `.gitignore`
- **Secret manager in prod** — rotation, audit, access control
- **Different secrets per env** — dev/staging/prod
- **Rotate on leak** — immediate, automated

---

## 13. Rate Limiting

> **Rate limiting patterns**: see `api-design` skill.

### Layers

| Layer              | Limit        | Scope               |
| ------------------ | ------------ | ------------------- |
| **Edge (CDN)**     | 1000/min/IP  | DDoS                |
| **API Gateway**    | 100/min/user | Abuse               |
| **Auth endpoints** | 5/min/IP     | Brute force         |
| **Auth mutations** | 10/min/user  | Credential stuffing |

---

## 14. Supply Chain Security

```json
// package.json
{
  "dependencies": {
    "lodash": "4.17.21" // pinned, not ^
  }
}
```

```bash
# Audit
pnpm audit --prod
pnpm audit --audit-level high

# Provenance
pnpm install --frozen-lockfile
```

### Rules Supply Chain Security

- **Pin exact versions** — no `^`/`~` in production
- **`pnpm audit` in CI** — fail on high/critical
- **Dependency review** — `pnpm why <pkg>`, check maintenance
- **Signed commits** — `git commit -S`
- **Provenance verification** — `npm pkg get integrity`

---

## 15. Subresource Integrity (SRI)

```html
<script
  src="https://cdn.example.com/lib.js"
  integrity="sha384-abc123..."
  crossorigin="anonymous"
></script>
```

```bash
# Generate
openssl dgst -sha384 -binary lib.js | openssl base64 -A
```

### Rules SRI

- **All third-party CDN scripts** — SRI required
- **Generate at build** — Vite/Rollup plugin
- **Fail on mismatch** — browser blocks

---

## 16. Prototype Pollution

```ts
// Prevention
Object.freeze(Object.prototype);
Object.freeze(Array.prototype);

// Safe merge
import { merge } from "lodash-es";
const result = merge({}, userInput, { safe: true }); // lodash 4.17.21+

// Or use structuredClone
const safe = structuredClone(userInput);
```

### Rules Prototype Pollution

- **Freeze prototypes** — early in app bootstrap
- **Use safe libraries** — lodash 4.17.21+, no `_.merge` without checks
- **Validate object keys** — reject `__proto__`, `constructor`, `prototype`

---

## 17. Security Testing

### CI Gates

```yaml
# .github/workflows/security.yml
- name: Dependency audit
  run: pnpm audit --prod --audit-level high

- name: SAST (Semgrep)
  uses: returntocorp/semgrep-action@v1
  with:
    config: p/security-audit

- name: Container scan
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: fs
    severity: HIGH,CRITICAL
```

### Rules Security Testing

- **`pnpm audit` in CI** — fail on high/critical
- **SAST** — Semgrep, CodeQL for code patterns
- **Container scan** — Trivy for Docker images
- **Penetration test** — annual, after major changes

---

## 18. Methodology

Before using ANY security pattern not documented in
this skill:

1. **MCP Context7** (priority): `context7_resolve-library-id` +
   `context7_query-docs` for OWASP, CSP, crypto libs.
2. **Official docs**: OWASP Cheatsheets, MDN Security, RFC specs.
3. **Project config**: `package.json`, CSP headers, middleware
   — verify against actual setup.
4. **HARD RULE**: If not in this skill AND cannot be verified against
   2 authoritative sources → DO NOT USE IT. Document as assumption or risk in
   report to orchestrator.

---

## 19. Prohibitions

- ❌ Do not disable CSP — even in dev
- ❌ Do not use `eval`/`Function` constructor
- ❌ Do not skip input validation — ever
- ❌ Do not use string concat for SQL/NoSQL
- ❌ Do not store secrets in repo — ever
- ❌ Do not use `dangerouslySetInnerHTML` without DOMPurify
- ❌ Do not skip rate limiting on auth endpoints
- ❌ Do not use `SameSite=None` without `Secure`
- ❌ Do not skip SRI on CDN scripts
- ❌ Do not ignore `pnpm audit` failures in CI

---

## 20. References

> **Note:** For JavaScript conventions (crypto, fetch),
> see [JavaScript](../javascript/SKILL.md)
> **Note:** For Auth patterns, see [Auth](../auth/SKILL.md)
> **Note:** For API Design (rate limiting, CORS),
> see [API Design](../api-design/SKILL.md)
> **Note:** For Next.js patterns, see [Next.js](../nextjs/SKILL.md)
> **Note:** For React patterns, see [React](../reactjs/SKILL.md)
> **Note:** For Astro patterns, see [Astro](../astro/SKILL.md)
> **Note:** For Vite config, see [Vite](../vite/SKILL.md)

---

Last updated: 2026-08

