From nickcrew-claude-ctx-plugin
Provides OWASP checklists and patterns to prevent web vulnerabilities like XSS, CSRF, SSRF, SQL injection, access control flaws. Use for writing or reviewing web app endpoints and user input handling.
npx claudepluginhub nickcrew/claude-cortexThis skill uses the workspace's default tool permissions.
Comprehensive secure coding practices for web applications. Approach code from a **bug hunter's perspective** and make applications **as secure as possible** without breaking functionality.
Provides OWASP Top 10 guidelines, secure Python/Flask coding patterns, prevention strategies, and remediation for access control and cryptographic vulnerabilities.
Audits web applications and REST APIs for OWASP Top 10 vulnerabilities including broken access control, authentication failures, and data protection. Use when reviewing code, auth/authz, APIs, or before deployment.
Implements secure backend coding practices for input validation, authentication, API security, injection prevention, and HTTP headers. Use for security implementations and code reviews.
Share bugs, ideas, or general feedback.
Comprehensive secure coding practices for web applications. Approach code from a bug hunter's perspective and make applications as secure as possible without breaking functionality.
defense-in-depth insteadDetermine which security domains apply to the code under review:
| Domain | Trigger |
|---|---|
| Access Control | Any authenticated endpoint, multi-tenant data |
| XSS | User input rendered in HTML, JavaScript, or CSS |
| CSRF | State-changing endpoints (POST, PUT, DELETE) |
| SSRF | Server makes requests to user-provided URLs |
| SQL Injection | Dynamic database queries |
| File Upload | Any file upload functionality |
| Path Traversal | User input in file paths |
Use the relevant sections below as checklists for each identified domain.
Ensure all responses include the required headers (see Security Headers Checklist).
Key Principles:
Access control vulnerabilities occur when users can access resources or perform actions beyond their intended permissions.
For every data point and action that requires authentication:
User-Level Authorization
Use UUIDs Instead of Sequential IDs
Account Lifecycle Handling
# Pseudocode for secure resource access
function getResource(resourceId, currentUser):
resource = database.find(resourceId)
if resource is null:
return 404 # Don't reveal if resource exists
if resource.ownerId != currentUser.id:
if not currentUser.hasOrgAccess(resource.orgId):
return 404 # Return 404, not 403, to prevent enumeration
return resource
Every input controllable by the user—whether directly or indirectly—must be sanitized against XSS.
Direct Inputs:
Indirect Inputs:
Often Overlooked:
Output Encoding (Context-Specific)
< → <)Content Security Policy (CSP)
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.yourdomain.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
'unsafe-inline' and 'unsafe-eval' for scriptsreport-uri /csp-reportInput Sanitization
Additional Headers
X-Content-Type-Options: nosniffX-Frame-Options: DENY (or use CSP frame-ancestors)Every state-changing endpoint must be protected against CSRF attacks.
Authenticated Actions:
Pre-Authentication Actions:
CSRF Tokens
SameSite Cookies
Set-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly
Strict: Cookie never sent cross-site (best security)Lax: Cookie sent on top-level navigations (good balance)Double Submit Cookie Pattern
No secrets or sensitive information should be accessible to client-side code.
API Keys and Secrets:
Sensitive User Data:
Infrastructure Details:
.env filesAny endpoint accepting a URL for redirection must be protected against open redirect attacks.
Allowlist Validation
allowed_domains = ['yourdomain.com', 'app.yourdomain.com']
function isValidRedirect(url):
parsed = parseUrl(url)
return parsed.hostname in allowed_domains
Relative URLs Only
/dashboard) not full URLs/ and doesn't contain //Indirect References
?redirect=dashboard → lookup to /dashboard| Technique | Example | Why It Works |
|---|---|---|
| @ symbol | https://legit.com@evil.com | Browser navigates to evil.com with legit.com as username |
| Subdomain abuse | https://legit.com.evil.com | evil.com owns the subdomain |
| Protocol tricks | javascript:alert(1) | XSS via redirect |
| Double URL encoding | %252f%252fevil.com | Decodes to //evil.com after double decode |
| Backslash | https://legit.com\@evil.com | Some parsers normalize \ to / |
| Null byte | https://legit.com%00.evil.com | Some parsers truncate at null |
| Tab/newline | https://legit.com%09.evil.com | Whitespace confusion |
| Unicode normalization | https://legіt.com (Cyrillic і) | IDN homograph attack |
| Data URLs | data:text/html,<script>... | Direct payload execution |
| Protocol-relative | //evil.com | Uses current page's protocol |
| Fragment abuse | https://legit.com#@evil.com | Parsed differently by different libraries |
Any functionality where the server makes requests to URLs provided or influenced by users must be protected.
Allowlist Approach (Preferred)
Network Segmentation
| Technique | Example | Description |
|---|---|---|
| Decimal IP | http://2130706433 | 127.0.0.1 as decimal |
| Octal IP | http://0177.0.0.1 | Octal representation |
| Hex IP | http://0x7f.0x0.0x0.0x1 | Hexadecimal |
| IPv6 localhost | http://[::1] | IPv6 loopback |
| IPv6 mapped IPv4 | http://[::ffff:127.0.0.1] | IPv4-mapped IPv6 |
| Short IPv6 | http://[::] | All zeros |
| DNS rebinding | Attacker's DNS returns internal IP | First request resolves to external IP, second to internal |
| CNAME to internal | Attacker domain CNAMEs to internal | DNS points to internal hostname |
| URL parser confusion | http://attacker.com#@internal | Different parsing behaviors |
| Redirect chains | External URL redirects to internal | Follow redirects carefully |
| IPv6 scope ID | http://[fe80::1%25eth0] | Interface-scoped IPv6 |
| Rare IP formats | http://127.1 | Shortened IP notation |
Block access to cloud metadata endpoints:
169.254.169.254metadata.google.internal, 169.254.169.254, http://metadata169.254.169.254169.254.169.254File uploads must validate type, content, and size to prevent various attacks.
1. File Type Validation
2. File Content Validation
3. File Size Limits
| Attack | Description | Prevention |
|---|---|---|
| Extension bypass | shell.php.jpg | Check full extension, use allowlist |
| Null byte | shell.php%00.jpg | Sanitize filename, check for null bytes |
| Double extension | shell.jpg.php | Only allow single extension |
| MIME type spoofing | Set Content-Type to image/jpeg | Validate magic bytes |
| Magic byte injection | Prepend valid magic bytes to malicious file | Check entire file structure, not just header |
| Polyglot files | File valid as both JPEG and JavaScript | Parse file as expected type, reject if invalid |
| SVG with JavaScript | <svg onload="alert(1)"> | Sanitize SVG or disallow entirely |
| XXE via file upload | Malicious DOCX, XLSX (which are XML) | Disable external entities in parser |
| ZIP slip | ../../../etc/passwd in archive | Validate extracted paths |
| ImageMagick exploits | Specially crafted images | Keep ImageMagick updated, use policy.xml |
| Filename injection | ; rm -rf / in filename | Sanitize filenames, use random names |
| Content-type confusion | Browser MIME sniffing | Set X-Content-Type-Options: nosniff |
| Type | Magic Bytes (hex) |
|---|---|
| JPEG | FF D8 FF |
| PNG | 89 50 4E 47 0D 0A 1A 0A |
| GIF | 47 49 46 38 |
25 50 44 46 | |
| ZIP | 50 4B 03 04 |
| DOCX/XLSX | 50 4B 03 04 (ZIP-based) |
Content-Disposition: attachment (forces download)X-Content-Type-Options: nosniffContent-Type matching actual file typeSQL injection occurs when user input is incorporated into SQL queries without proper handling.
1. Parameterized Queries (Prepared Statements) — PRIMARY DEFENSE
-- VULNERABLE
query = "SELECT * FROM users WHERE id = " + userId
-- SECURE
query = "SELECT * FROM users WHERE id = ?"
execute(query, [userId])
2. ORM Usage
3. Input Validation
xp_cmdshell in SQL ServerXXE vulnerabilities occur when XML parsers process external entity references in user-supplied XML.
Direct XML Input:
Indirect XML:
Java:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setExpandEntityReferences(false);
Python (lxml):
from lxml import etree
parser = etree.XMLParser(resolve_entities=False, no_network=True)
# Or use defusedxml library
PHP:
libxml_disable_entity_loader(true);
// Or use XMLReader with proper settings
Node.js:
// Use libraries that disable DTD processing by default
// If using libxmljs, set { noent: false, dtdload: false }
.NET:
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit;
settings.XmlResolver = null;
Path traversal vulnerabilities occur when user input controls file paths, allowing access to files outside intended directories.
# VULNERABLE
file_path = "/uploads/" + user_input
file_path = base_dir + request.params['file']
template = "templates/" + user_provided_template
1. Avoid User Input in Paths
# Instead of using user input directly
# Use indirect references
files = {'report': '/reports/q1.pdf', 'invoice': '/invoices/2024.pdf'}
file_path = files.get(user_input) # Returns None if invalid
2. Canonicalization and Validation
import os
def safe_join(base_directory, user_path):
# Ensure base is absolute and normalized
base = os.path.abspath(os.path.realpath(base_directory))
# Join and then resolve the result
target = os.path.abspath(os.path.realpath(os.path.join(base, user_path)))
# Ensure the commonpath is the base directory
if os.path.commonpath([base, target]) != base:
raise ValueError("Error!")
return target
3. Input Sanitization
.. sequences/, C:)Include these headers in all responses:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Content-Security-Policy: [see XSS section]
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Cache-Control: no-store (for sensitive pages)
JWT misconfigurations can lead to full authentication bypass and token forgery.
| Vulnerability | Prevention |
|---|---|
alg: none attack | Always verify algorithm server-side, reject none |
| Algorithm confusion | Explicitly specify expected algorithm, never derive from token |
| Weak HMAC secrets | Use 256+ bit cryptographically random secrets |
| Missing expiration | Always set exp claim |
| Token in localStorage | Store in httpOnly, Secure, SameSite=Strict cookies, never localStorage |
// 1. SIGNING
// Always use environment variables for secrets
const secret = process.env.JWT_SECRET;
const token = jwt.sign({
sub: userId,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + (15 * 60), // 15 mins (Short-lived)
jti: crypto.randomUUID() // Unique ID for revocation/blacklisting
}, secret, {
algorithm: 'HS256'
});
// 2. SENDING (Cookie Best Practices)
// Protect against XSS and CSRF
res.cookie('token', token, {
httpOnly: true,
secure: true,
sameSite: 'strict'
});
// 3. VERIFYING
// CRITICAL: Whitelist the allowed algorithm
jwt.verify(token, secret, { algorithms: ['HS256'] }, (err, decoded) => {
if (err) {
// Handle invalid token
}
// Trust the payload
});
alg: none rejectedexp claim always set and validatedAccepting unfiltered request bodies can lead to privilege escalation.
// VULNERABLE — user can set { role: "admin" } in request body
User.update(req.body)
// SECURE — whitelist allowed fields
const allowed = ['name', 'email', 'avatar']
const updates = pick(req.body, allowed)
User.update(updates)
This applies to any ORM/framework — always explicitly define which fields a request can modify.
| Vulnerability | Prevention |
|---|---|
| Introspection in production | Disable introspection in production environments. |
| Query depth attack | Implement query depth limiting (e.g., maximum of 10 levels). |
| Query complexity attack | Calculate and enforce strict query cost limits. |
| Batching attack | Limit the number of operations allowed per single request. |
const server = new ApolloServer({
introspection: process.env.NODE_ENV !== 'production',
validationRules: [
depthLimit(10),
costAnalysis({ maximumCost: 1000 })
]
})
When generating code, always:
When unsure, choose the more restrictive/secure option and document the security consideration in comments.