Files
security-patterns/dos-prevention.md
T
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

181 lines
4.1 KiB
Markdown

# Denial of Service Prevention
## Rule
Bound all resource consumption. Assume attackers will send worst-case input.
**Source:** [CWE-400: Uncontrolled Resource Consumption](https://cwe.mitre.org/data/definitions/400.html)
## Request Limits
### Correct Pattern
```python
from functools import wraps
import time
# Rate limiting
class RateLimiter:
def __init__(self, max_requests: int, window_seconds: int):
self.max_requests = max_requests
self.window = window_seconds
self.requests = {} # ip -> [timestamps]
def is_allowed(self, ip: str) -> bool:
now = time.time()
cutoff = now - self.window
# Clean old entries
self.requests[ip] = [
t for t in self.requests.get(ip, [])
if t > cutoff
]
if len(self.requests[ip]) >= self.max_requests:
return False
self.requests[ip].append(now)
return True
# Request size limits
MAX_BODY_SIZE = 10 * 1024 * 1024 # 10MB
@app.before_request
def limit_request_size():
if request.content_length and request.content_length > MAX_BODY_SIZE:
abort(413) # Payload too large
```
### Incorrect Pattern
```python
# Wrong: no size limit
data = request.get_data() # Could be gigabytes
# Wrong: unbounded loop based on user input
for i in range(int(request.args["count"])):
process_item(i)
# Wrong: no timeout
response = requests.get(user_url) # Hangs forever
```
## Algorithmic Complexity
### Correct Pattern
```python
# Limit input size before expensive operations
MAX_ITEMS = 10000
def process_list(items: list) -> list:
if len(items) > MAX_ITEMS:
raise ValueError(f"Too many items: {len(items)} > {MAX_ITEMS}")
return sorted(items) # O(n log n) but bounded
# Use timeouts for expensive operations
import signal
def timeout_handler(signum, frame):
raise TimeoutError("Operation timed out")
def with_timeout(seconds: int):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(seconds)
try:
return func(*args, **kwargs)
finally:
signal.alarm(0)
return wrapper
return decorator
@with_timeout(5)
def expensive_operation(data):
...
```
### Incorrect Pattern
```python
# Wrong: O(n²) or worse on unbounded input
def find_duplicates(items):
for i in items:
for j in items: # O(n²)
if i == j:
yield i
# Wrong: regex with catastrophic backtracking
import re
pattern = re.compile(r'(a+)+$') # ReDoS vulnerable
pattern.match('a' * 30 + 'b') # Hangs
```
## Memory Limits
### Correct Pattern
```python
# Stream large files instead of loading into memory
def process_large_file(path: str):
with open(path, 'r') as f:
for line in f: # Streaming, constant memory
process_line(line)
# Limit collection sizes
class BoundedCache:
def __init__(self, max_size: int = 1000):
self.max_size = max_size
self.cache = {}
def set(self, key, value):
if len(self.cache) >= self.max_size:
# Evict oldest
oldest = next(iter(self.cache))
del self.cache[oldest]
self.cache[key] = value
```
### Incorrect Pattern
```python
# Wrong: loading entire file into memory
data = open(path).read() # Could be huge
# Wrong: unbounded cache
cache = {}
def get_or_compute(key):
if key not in cache:
cache[key] = expensive_compute(key) # Grows forever
return cache[key]
```
## Connection Limits
```python
# Limit concurrent connections per IP
MAX_CONNECTIONS_PER_IP = 10
# Timeouts on all network operations
import socket
socket.setdefaulttimeout(30)
# Connection pooling with limits
from urllib3 import PoolManager
http = PoolManager(
maxsize=100,
block=True,
timeout=30
)
```
## Edge Cases
- Zip bombs (small file, huge uncompressed)
- XML entity expansion (billion laughs attack)
- Hash collision attacks (hash flooding)
- Slowloris (slow, incomplete requests)
- Amplification attacks (small request, large response)