This guide provides comprehensive secure coding practices for web applications. As an AI assistant, your role is to approach code from a bug hunter's perspective and make applications as secure as possible without breaking functionality.
Key Principles:
Defense in depth: Never rely on a single security control
Fail securely: When something fails, fail closed (deny access)
Least privilege: Grant minimum permissions necessary
Input validation: Never trust user input, validate everything server-side
Output encoding: Encode data appropriately for the context it's rendered in
Access Control Issues
Access control vulnerabilities occur when users can access resources or perform actions beyond their intended permissions.
Core Requirements
Skills relacionados
For every data point and action that requires authentication:
User-Level Authorization
Each user must only access/modify their own data
No user should access data from other users or organizations
Always verify ownership at the data layer, not just the route level
Use UUIDs Instead of Sequential IDs
Use UUIDv4 or similar non-guessable identifiers
Exception: Only use sequential IDs if explicitly requested by user
Account Lifecycle Handling
When a user is removed from an organization: immediately revoke all access tokens and sessions
When an account is deleted/deactivated: invalidate all active sessions and API keys
Implement token revocation lists or short-lived tokens with refresh mechanisms
Authorization Checks Checklist
Verify user owns the resource on every request (don't trust client-side data)
Check organization membership for multi-tenant apps
Validate role permissions for role-based actions
Re-validate permissions after any privilege change
Check parent resource ownership (e.g., if accessing a comment, verify user owns the parent post)
Common Pitfalls to Avoid
IDOR (Insecure Direct Object Reference): Always verify the requesting user has permission to access the requested resource ID
Privilege Escalation: Validate role changes server-side; never trust role info from client
Horizontal Access: User A accessing User B's resources with the same privilege level
Vertical Access: Regular user accessing admin functionality
Mass Assignment: Filter which fields users can update; don't blindly accept all request body fields
Implementation Pattern
# 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
Client-Side Bugs
Cross-Site Scripting (XSS)
Every input controllable by the user—whether directly or indirectly—must be sanitized against XSS.
Input Sources to Protect
Direct Inputs:
Form fields (email, name, bio, comments, etc.)
Search queries
File names during upload
Rich text editors / WYSIWYG content
Indirect Inputs:
URL parameters and query strings
URL fragments (hash values)
HTTP headers used in the application (Referer, User-Agent if displayed)
Data from third-party APIs displayed to users
WebSocket messages
postMessage data from iframes
LocalStorage/SessionStorage values if rendered
Often Overlooked:
Error messages that reflect user input
PDF/document generators that accept HTML
Email templates with user data
Log viewers in admin panels
JSON responses rendered as HTML
SVG file uploads (can contain JavaScript)
Markdown rendering (if allowing HTML)
Protection Strategies
Output Encoding (Context-Specific)
HTML context: HTML entity encode (< → <)
JavaScript context: JavaScript escape
URL context: URL encode
CSS context: CSS escape
Use framework's built-in escaping (React's JSX, Vue's {{ }}, etc.)
# 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