| name | security-engineering |
| description | Use when the user asks "is this secure", "how do I prevent SQL injection", "how should I store passwords", "do a threat model", "review this for security issues", "how do I handle authentication", or discusses OWASP, encryption, secrets management, or authorization. Provides security engineering guidance for software engineers. |
Security Engineering
Security is not a feature added at the end — it is a property of the design. A system designed without security considerations requires significant rework to harden. A system designed with security from the start is cheaper to build and cheaper to maintain.
When to Activate
- Starting a new service that handles sensitive data
- Reviewing code for security vulnerabilities
- Designing authentication or authorization
- Handling secrets, credentials, or PII
- Responding to a security finding or vulnerability report
- Preparing for a security audit or penetration test
Threat Modeling
Before writing code, identify what you are protecting and who might attack it. A lightweight threat model answers four questions:
- What are we building? — data flow diagram of the system
- What can go wrong? — enumerate threats using STRIDE
- What are we doing about it? — mitigations per threat
- Did we do a good job? — validation and review
STRIDE Threat Categories
| Category | Example |
|---|
| Spoofing | Attacker impersonates a legitimate user |
| Tampering | Attacker modifies data in transit or at rest |
| Repudiation | User denies having performed an action |
| Information Disclosure | Attacker reads data they should not |
| Denial of Service | Attacker makes the system unavailable |
| Elevation of Privilege | User gains access beyond their authorization |
For each threat, define a mitigation. For each mitigation, define a test.
OWASP Top 10
The minimum security floor. Know these and design against them.
A01: Broken Access Control
Every resource access must be authorized, not just authenticated. Common failures:
- URL manipulation to access other users' data (
/orders/123 → try /orders/124)
- Missing authorization on API endpoints (authentication middleware present, authorization check absent)
- Insecure direct object references (exposing database IDs that allow enumeration)
Fix: enforce authorization at the data layer, not just the route layer. Test with a second user account that should not have access.
A02: Cryptographic Failures
Transmitting or storing sensitive data without adequate encryption.
Fix:
- HTTPS everywhere, with HSTS
- Encrypt PII and payment data at rest (AES-256-GCM)
- Never store passwords — store bcrypt/argon2 hashes with appropriate cost factor
- Rotate secrets; never embed secrets in code
A03: Injection
User input interpreted as commands (SQL, shell, LDAP, XML).
SQL injection fix: parameterized queries always.
cursor.execute(f"SELECT * FROM users WHERE email = '{email}'")
cursor.execute("SELECT * FROM users WHERE email = %s", (email,))
Command injection fix: never construct shell commands from user input. Use library functions, not subprocess with shell=True.
A04: Insecure Design
Security requirements missing from the design phase. Fix: threat model before implementation.
A05: Security Misconfiguration
Default credentials, debug endpoints enabled in production, verbose error messages leaking internal paths.
Fix:
- Environment-specific configuration (no debug mode in production)
- Remove unused features, endpoints, default accounts
- HTTP security headers:
Content-Security-Policy, X-Content-Type-Options, X-Frame-Options
- Disable directory listing
A06: Vulnerable Components
Dependencies with known CVEs.
Fix:
npm audit, pip-audit, bundler-audit in CI
- Dependabot or Renovate for automated dependency updates
- Know your software bill of materials (SBOM)
A07: Identification and Authentication Failures
Weak passwords, missing MFA, broken session management.
Fix:
- Enforce strong passwords or better — use passkeys or SSO
- Implement MFA for privileged actions
- Short-lived sessions with secure cookie attributes (
HttpOnly, Secure, SameSite=Strict)
- Rate-limit authentication attempts
A08: Software and Data Integrity Failures
Untrusted data deserialized without validation; CI/CD pipeline compromised.
Fix:
- Validate and sanitize all deserialized input
- Sign artifacts in CI/CD
- Verify dependency integrity (lockfiles, checksums)
A09: Security Logging and Monitoring Failures
Attacks that succeed silently because there are no alerts.
Fix:
- Log authentication events (success, failure, lockout)
- Log authorization failures
- Alert on anomalous patterns (unusual volume, access from new geography)
- Never log PII, passwords, or tokens
A10: Server-Side Request Forgery (SSRF)
Attacker causes the server to make requests to unintended targets (internal services, cloud metadata endpoints).
Fix:
- Validate and allowlist URLs before fetching
- Block requests to private IP ranges (10.x, 172.16.x, 192.168.x, 169.254.x)
- Use network-level controls to prevent server from reaching internal services
Secrets Management
Never put secrets in code. No API keys, passwords, tokens, or certificates in source control — ever.
Use:
- Environment variables (minimum viable)
- A secrets manager: AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, Doppler
Rotation: secrets should be rotatable without downtime. Design for this from the start — it means no hardcoded secrets and support for a brief period where both the old and new secret are valid.
Scanning: add git-secrets, truffleHog, or gitleaks to CI to catch accidental commits before they reach origin.
Authentication Patterns
API Keys (Server-to-Server)
- Generate with cryptographic randomness (not sequential)
- Store only a hash (treat like a password)
- Scope keys to minimum permissions
- Log every use, alert on anomalous patterns
JWT (User-Delegated Access)
- Short expiry (15 minutes for access token, 7 days for refresh token)
- Sign with RS256 (asymmetric) or HS256 (symmetric, simpler but shared secret)
- Validate signature, expiry, issuer, and audience on every request
- Do not store sensitive data in JWT payload — it is base64 encoded, not encrypted
Session Tokens
- Cryptographically random, at least 128 bits
- Store server-side with a hash
- Regenerate on privilege escalation (login, sudo, role change)
- Invalidate on logout
Secure Defaults
Write code where the insecure path requires explicit choice:
- Default to deny in authorization (allowlist, not blocklist)
- Default to encrypted connections (reject unencrypted by default)
- Default to minimal permissions (request only the scopes needed)
- Default to input validation (reject anything unexpected)
Gotchas
-
Authorization at the route level only: middleware that checks "is this user logged in?" does not check "is this user allowed to access order #123?". Check authorization at the resource level.
-
Logging sensitive data: a log line that includes a user's full name, email, and action is a privacy violation waiting for a breach. Mask or omit PII from logs.
-
Trusting client-supplied data for authorization: never trust user_id from a request body or query parameter. Read identity from the verified session/token.
-
Relying on obscurity: hiding an API endpoint does not protect it. Authenticated, authorized access to every endpoint is the only protection.
-
Soft delete as access control: a deleted record that is still in the database can be accessed if the authorization layer does not filter deleted records. Ensure soft-delete filters are applied everywhere.
-
Missing rate limiting on sensitive endpoints: login, password reset, and SMS verification endpoints without rate limiting are trivially brute-forced.
Integration
- system-design — threat model at design time; security architecture decisions are the hardest to retrofit
- api-design — apply authentication, authorization, and input validation to every endpoint
- code-review — security review is part of every PR review
- incident-response — security incidents require immediate containment; have a playbook before you need it
References
Skill Metadata
Created: 2026-04-10
Version: 1.0.0