---
name: code-saurabh/security-engineer
source: https://app.decimal.ai/s/code-saurabh-security-engineer@1/SKILL.md
source_sha256: 016c55def3b9
---

# Security Engineering

Approach every security task as an engineer who has read real incident post-mortems and knows that most breaches are not exotic zero-days — they are OWASP Top 10 vulnerabilities that were known, understood, and skipped. Security is not a phase at the end of development. It is a design constraint applied from the first line of code.

---

## Step 0: Threat Model Before You Build

Before writing security controls, understand what you are protecting:

1. **What are the assets?** User data, credentials, financial records, proprietary algorithms — name them
2. **Who are the threat actors?** External attackers, malicious insiders, compromised dependencies, misconfigured services
3. **What are the attack surfaces?** API endpoints, authentication flows, file uploads, third-party integrations, admin interfaces
4. **What are the trust boundaries?** Where does data cross from untrusted to trusted? Every boundary needs validation
5. **What is the impact of a breach?** Data exposure, financial loss, regulatory penalty, reputational damage — prioritise controls by impact

Use STRIDE as a thinking tool: **S**poofing, **T**ampering, **R**epudiation, **I**nformation disclosure, **D**enial of service, **E**levation of privilege. For each component, ask which STRIDE categories apply.

---

## OWASP Top 10 — Mandatory Defenses

### A01 — Broken Access Control
- Deny by default — every resource is protected unless explicitly made public
- Verify ownership on every request — a user accessing `/api/documents/123` must own document 123
- Implement RBAC at the middleware layer — roles and permissions enforced before business logic runs
- Never trust client-supplied IDs for access decisions without server-side verification
- Log all access control failures — they are reconnaissance signals

### A02 — Cryptographic Failures
- Enforce HTTPS everywhere — redirect HTTP to HTTPS; HSTS header with long max-age
- Encrypt sensitive data at rest — AES-256 minimum
- Never store passwords in plaintext or with reversible encryption — bcrypt (cost ≥ 12) or argon2id
- Never store credit card numbers, SSNs, or equivalent PII unless absolutely required — tokenise instead
- Use strong, modern TLS — TLS 1.2 minimum, TLS 1.3 preferred; disable weak cipher suites

### A03 — Injection
- Parameterized queries for all database access — never interpolate user input into SQL
- Use an ORM as the default — raw queries only when performance requires it, with parameterization enforced
- Validate and sanitize all input at the API boundary — reject malformed input before it reaches business logic
- HTML-encode output in templates — use a templating engine that auto-escapes by default
- Avoid `eval()`, `exec()`, `os.system()`, shell interpolation with user data

### A04 — Insecure Design
- Security requirements belong in the design phase — add them to acceptance criteria, not as post-hoc patches
- Rate limit authentication endpoints — brute force is not sophisticated; stopping it is not optional
- Implement account lockout after repeated failed attempts with exponential backoff
- Multi-factor authentication for admin accounts and sensitive operations

### A05 — Security Misconfiguration
- Security headers on every HTTP response:
  ```
  Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  Content-Security-Policy: default-src 'self'
  X-Content-Type-Options: nosniff
  X-Frame-Options: DENY
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()
  ```
- CORS: whitelist specific origins — never `Access-Control-Allow-Origin: *` for credentialed requests
- Disable debug endpoints, stack traces, and verbose error messages in production
- Remove default credentials and unused accounts before deploying
- Review cloud storage permissions — S3 buckets and blob containers are public by accident more often than by design

### A06 — Vulnerable and Outdated Components
- Dependency scanning in CI — `npm audit`, `pip audit`, Snyk, Dependabot
- Pin dependency versions — floating versions pull in breaking or vulnerable updates silently
- Review dependency changelogs before updating — automated updates can introduce breaking changes
- Remove unused dependencies — every dependency is an attack surface

### A07 — Identification and Authentication Failures
- JWT best practices (see JWT section below)
- Session tokens: cryptographically random, minimum 128 bits of entropy
- Invalidate sessions on logout — do not rely on token expiry alone
- Invalidate all sessions on password change
- Implement account lockout: 5 failed attempts → temporary lockout → exponential backoff
- Never log authentication tokens, passwords, or session identifiers

### A08 — Software and Data Integrity Failures
- Verify integrity of downloaded artifacts in CI — checksums, signatures
- Do not deserialize untrusted data without schema validation — avoid `pickle`, `eval`, unsafe YAML loaders
- Use subresource integrity (SRI) for CDN-hosted scripts
- Sign build artifacts and container images; verify signatures at deploy time

### A09 — Security Logging and Monitoring Failures
- Log all authentication events: success, failure, lockout, password reset
- Log all access control failures
- Log all admin actions
- Ship logs to a system the attacker cannot reach — a compromised server should not be able to erase its own audit trail
- Alert on: multiple failed logins, privilege escalation attempts, access to sensitive endpoints outside business hours

### A10 — Server-Side Request Forgery (SSRF)
- Validate and whitelist URLs before making server-side HTTP requests
- Reject requests to private IP ranges (10.x.x.x, 172.16.x.x, 192.168.x.x, 169.254.x.x, localhost)
- Use an allow-list of permitted external domains — not a block-list
- Never pass user-controlled data directly to HTTP clients, DNS resolvers, or file loaders

---

## JWT Review Checklist

When reviewing or implementing JWTs:

- [ ] Algorithm explicitly specified and validated — reject `alg: none`; use RS256 or ES256 for multi-service; HS256 only when the secret is shared safely
- [ ] Signature verified on every request — never decode without verifying
- [ ] `exp` claim present and validated — short-lived access tokens (15 minutes); longer refresh tokens (7-30 days)
- [ ] `iss` (issuer) and `aud` (audience) claims validated — prevent token reuse across services
- [ ] Refresh tokens rotated on use — a refresh token used twice indicates theft; invalidate both
- [ ] Refresh tokens stored securely — httpOnly cookie, not localStorage
- [ ] Token revocation strategy defined — short expiry is the simplest; token blacklist for immediate revocation needs
- [ ] Sensitive data not in the payload — JWTs are base64-encoded, not encrypted; do not store PII in claims

---

## OAuth 2.0 / OIDC

- Use the **Authorization Code flow with PKCE** for all user-facing applications — never the Implicit flow
- Use the **Client Credentials flow** for service-to-service authentication
- Validate the `state` parameter to prevent CSRF on the callback
- Validate `id_token` signature, `iss`, `aud`, `exp`, and `nonce`
- Store tokens in httpOnly cookies — not in JavaScript-accessible storage
- Request minimal scopes — principle of least privilege applies to OAuth scopes
- Validate redirect URIs strictly — registered URIs only, no open redirects

---

## RBAC Design

Role-Based Access Control done correctly:

1. **Define roles at the domain level** — not "admin/user" but "billing-admin", "content-editor", "read-only-viewer"
2. **Permissions are the unit of access control** — roles are collections of permissions; check permissions, not roles in business logic
3. **Deny by default** — a missing permission entry means no access
4. **Enforce at the middleware layer** — permission checks happen before handlers run, not inside them
5. **Resource ownership is a permission** — `order:read:own` (can read own orders) vs `order:read:any` (admin can read all orders)
6. **Audit permission assignments** — who granted this role, when, and why

```
User → has Roles → Roles contain Permissions → Permissions gate Resources
```

Never check roles in business logic. Check permissions. Roles are a UI concern.

---

## XSS Prevention

- React, Vue, Angular auto-escape by default — do not use `dangerouslySetInnerHTML` / `v-html` with untrusted content
- Sanitize HTML with DOMPurify before rendering any user-generated rich text
- Content Security Policy blocks inline scripts and untrusted sources — implement it
- `HttpOnly` flag on session cookies — JavaScript cannot read them
- `SameSite=Strict` or `SameSite=Lax` on all cookies

---

## CSRF Prevention

- `SameSite=Strict` on session cookies prevents CSRF for modern browsers
- For APIs: require a custom request header (e.g., `X-Requested-With: XMLHttpRequest`) — cross-origin requests cannot set custom headers
- For traditional form submissions: double-submit cookie or synchronizer token pattern
- Verify `Origin` and `Referer` headers on state-changing requests as a secondary check

---

## Secret Management

**Never:**
- Hardcode secrets in source code
- Commit `.env` files to version control
- Log secrets, tokens, or API keys
- Pass secrets as command-line arguments (visible in process list)
- Store secrets in environment variables on shared CI runners without scoping

**Always:**
- Use a secrets manager in production: AWS Secrets Manager, Azure Key Vault, HashiCorp Vault, Doppler
- Rotate secrets on a schedule and immediately after any suspected exposure
- Scope secrets to the minimum surface — a service that reads from one bucket does not need write access
- Audit secret access — who accessed what secret, when

**In CI/CD:**
- Use platform-native secret storage (GitHub Secrets, GitLab CI Variables)
- Mask secret values in logs — configure the CI platform to redact known secret patterns
- Use short-lived credentials where possible — OIDC-based cloud authentication instead of long-lived keys

---

## Bundled Resources

Read [threat-model.md](./references/threat-model.md) when scoping a security review. Run `python scripts/scan_secrets.py <path>` only as a local, read-only first pass; confirm every finding manually and use dedicated secret-scanning tooling in CI.

## Definition of Done — Security Work

- [ ] Threat model documented for the feature or service
- [ ] OWASP Top 10 defenses applied relevant to the surface area
- [ ] All inputs validated and sanitized at the boundary
- [ ] Authentication and authorization enforced on every protected endpoint
- [ ] Secrets in a secrets manager — not in source code or environment files committed to git
- [ ] Security headers configured on all HTTP responses
- [ ] Dependency scan passing in CI
- [ ] Security logging in place for auth events and access control failures
- [ ] JWT/OAuth implementation reviewed against the checklist