CWE-693: Protection Mechanism Failure
Overview
Protection mechanism failure occurs when security controls are improperly implemented, misconfigured, or bypassable, relying on single layers of defense, client-side checks, or weak validation, enabling attackers to circumvent intended security protections.
OWASP Classification
A06:2025 - Insecure Design
Risk
High: Failed protection mechanisms enable complete security bypass (client-side validation), authentication defeat (weak implementations), authorization bypass, input validation circumvention, and exploitation despite presence of security controls.
Remediation Strategy
Implement Defense in Depth (Primary Defense)
# VULNERABLE - single layer
@app.route('/admin')
def admin_panel():
# Only checks cookie
if request.cookies.get('admin') == 'true':
return render_admin() # Client can set cookie!
# SECURE - multiple layers
@app.route('/admin')
@require_authentication # Layer 1: Must be logged in
@require_mfa # Layer 2: Multi-factor auth
@require_role('ADMIN') # Layer 3: Role check
@rate_limit(10, per_minute=1) # Layer 4: Rate limiting
def admin_panel():
# Log access for audit
log_admin_access(current_user)
return render_admin()
Never Trust Client-Side Validation
// VULNERABLE - client-side only
function submitForm() {
const price = document.getElementById('price').value;
if (price > 0 && price < 1000) { // Client-side check
// Attacker bypasses with browser dev tools
document.getElementById('form').submit();
}
}
// SECURE - server-side validation
@app.route('/submit', methods=['POST'])
def submit():
price = float(request.form['price'])
# Server-side validation (required)
if price <= 0 or price >= 1000:
abort(400, "Invalid price")
# Additional checks
if price != get_expected_price(request.form['item_id']):
abort(400, "Price mismatch")
process_order(price)
Proper Error Handling
// VULNERABLE - exception bypasses check
public void accessResource(String userId) {
try {
checkPermission(userId); // May throw exception
} catch (Exception e) {
// Swallowing exception
}
// Continues even if permission check failed!
grantAccess();
}
// SECURE - fail securely
public void accessResource(String userId)
throws AccessDeniedException {
try {
checkPermission(userId);
} catch (PermissionException e) {
log.error("Permission check failed", e);
throw new AccessDeniedException();
}
grantAccess();
}
Validate All Inputs
# VULNERABLE - incomplete validation
def process_upload(filename):
# Only checks extension
if filename.endswith('.jpg'):
save_file(filename) # Bypass: shell.php.jpg
# SECURE - comprehensive validation
import re
from pathlib import Path
ALLOWED_EXTENSIONS = {'jpg', 'png', 'gif'}
MAX_SIZE = 5 * 1024 * 1024 # 5MB
def process_upload(file):
# Check file size
if file.size > MAX_SIZE:
raise ValueError("File too large")
# Check extension (multiple checks)
filename = secure_filename(file.filename)
ext = Path(filename).suffix[1:].lower()
if ext not in ALLOWED_EXTENSIONS:
raise ValueError("Invalid file type")
# Check MIME type
if file.content_type not in ['image/jpeg', 'image/png', 'image/gif']:
raise ValueError("Invalid content type")
# Check magic bytes
header = file.read(16)
file.seek(0)
if not is_valid_image_header(header):
raise ValueError("Invalid image file")
# Sanitize filename
safe_name = re.sub(r'[^a-zA-Z0-9._-]', '', filename)
save_file(safe_name, file)
Remediation Steps
Core principle: All security-sensitive functionality must be protected by explicit, enforced security controls that cannot be bypassed by request manipulation or execution path changes; security mechanisms must exist and be correctly enforced.
- Identify the failed protection mechanism
- Trace bypass paths
- Locate missing layers
- Apply defense-in-depth: Add multiple independent security layers (authentication + authorization + input validation + rate limiting)
- Implement server-side enforcement: Move all security checks to server-side code where they cannot be bypassed
- Test bypass scenarios: Attempt to circumvent each protection mechanism (disable JavaScript, modify requests, force exceptions)
- Document the defense layers and verify all critical paths have multiple protections
Common Protection Failures
Client-Side Only:
<!-- Bypassable -->
<form onsubmit="return validateForm()">
<input name="price" id="price">
</form>
<script>
function validateForm() {
return document.getElementById('price').value < 100;
}
</script>
<!-- Attacker: Disable JavaScript or modify DOM -->
Weak Authentication:
Insufficient Authorization:
// Checks if admin, but not if admin for THIS resource
if (user.isAdmin()) {
deleteUser(targetUserId); // Can delete any user!
}
Incomplete Input Validation:
# Only checks for single quote
if "'" not in user_input:
query = f"SELECT * FROM users WHERE id = {user_input}"
# Bypass: 1 OR 1=1--
Defense in Depth Example
class SecureAPI:
def __init__(self):
self.rate_limiter = RateLimiter()
self.validator = InputValidator()
self.auth = AuthenticationService()
self.authz = AuthorizationService()
@require_https # Layer 1: Encrypted transport
def handle_request(self, request):
# Layer 2: Rate limiting
if not self.rate_limiter.allow(request.ip):
abort(429)
# Layer 3: Authentication
user = self.auth.authenticate(request.token)
if not user:
abort(401)
# Layer 4: Authorization
if not self.authz.can_access(user, request.resource):
abort(403)
# Layer 5: Input validation
data = self.validator.validate(request.data)
# Layer 6: Business logic with additional checks
result = self.process(user, data)
# Layer 7: Output encoding
return self.encode_output(result)
Dynamic Scan Guidance
For guidance on remediating this CWE when detected by dynamic (DAST) scanners:
- Dynamic Scan Guidance - Analyzing DAST findings and mapping to source code