Files
security-patterns/secure-defaults.md
Rodin 647928a0a1 Initial commit: 9 security patterns for code review
Fundamentals: secure-defaults, input-validation, credential-handling, audit-logging
Identity: authentication, authorization
Attack Prevention: injection-prevention, dos-prevention, prompt-injection
2026-05-10 22:45:03 -07:00

143 lines
3.5 KiB
Markdown

# Secure Defaults
## Rule
Fail closed. Deny by default. Make the secure path the easy path.
**Source:** [OWASP Secure Design Principles](https://wiki.owasp.org/index.php/Security_by_Design_Principles)
## Fail Closed
### Correct Pattern
```python
def check_access(user_id: str, resource_id: str) -> bool:
"""Default deny — return False on any error."""
try:
permissions = get_permissions(user_id, resource_id)
return "read" in permissions
except Exception:
# Log the error for debugging
logging.exception("Permission check failed")
# But deny access — fail closed
return False
def process_request(request):
"""Handle errors by denying, not allowing."""
try:
validate_request(request)
return handle_request(request)
except ValidationError as e:
return {"error": str(e)}, 400
except Exception:
# Unknown error — don't leak info, don't allow access
logging.exception("Unexpected error")
return {"error": "Internal error"}, 500
```
### Incorrect Pattern
```python
# Wrong: fail open
def check_access(user_id, resource_id):
try:
return has_permission(user_id, resource_id)
except Exception:
return True # "Let them in if something breaks"
# Wrong: exception = success
try:
verify_signature(token)
except:
pass # Signature verification bypassed!
```
## Deny by Default
```python
# Correct: explicit allowlist
ALLOWED_ORIGINS = {"https://app.example.com", "https://admin.example.com"}
def check_cors(origin: str) -> bool:
return origin in ALLOWED_ORIGINS
# Wrong: blocklist approach
BLOCKED_ORIGINS = {"http://evil.com"}
def check_cors(origin: str) -> bool:
return origin not in BLOCKED_ORIGINS # New attacks bypass this
```
## Secure Configuration
```python
# Correct: secure defaults, explicit opt-out
class SecurityConfig:
https_only: bool = True
csrf_protection: bool = True
content_security_policy: str = "default-src 'self'"
cookie_secure: bool = True
cookie_httponly: bool = True
cookie_samesite: str = "Strict"
# Wrong: insecure defaults
class Config:
debug: bool = True # Should be False
verify_ssl: bool = False # Should be True
allow_all_origins: bool = True # Should be False
```
## Least Privilege
```python
# Correct: minimal permissions
def create_db_connection():
return connect(
user="app_readonly", # Not root
database="app_db",
# Only needed permissions
)
# Service accounts should have minimal scope
SERVICE_ACCOUNT_PERMISSIONS = [
"storage.objects.get",
"storage.objects.list",
# NOT: "storage.admin"
]
```
## Defense in Depth
```python
class SecureEndpoint:
"""Multiple layers of security."""
def handle(self, request):
# Layer 1: Rate limiting
if not self.rate_limiter.allow(request.ip):
raise TooManyRequests()
# Layer 2: Authentication
user = self.authenticate(request)
if not user:
raise Unauthorized()
# Layer 3: Authorization
if not self.authorize(user, request.resource):
raise Forbidden()
# Layer 4: Input validation
data = self.validate(request.data)
# Layer 5: Business logic with validated data
return self.process(user, data)
```
## Edge Cases
- Feature flags that disable security controls
- Debug endpoints left enabled in production
- Default passwords in documentation
- Verbose error messages in production
- Commented-out security checks