Fail-Open Vulnerabilities
Fail-open vulnerabilities occur when applications default to allowing access or trusting input when security checks fail or logic errors occur.
The opposite of proper security is "fail-open" — when something goes wrong, the system defaults to allowing everything instead of denying everything. This is the most dangerous category of logic errors because it actively weakens security.
Real-World Attack Scenarios
Scenario 1: Failing Open on Authorization Check (CWE-636)
An application checks if user is admin:
def delete_user(user_id):
# Check if current user is admin
try:
is_admin = check_admin_status()
except:
# FAIL OPEN - Assume admin on error!
pass
# No check if is_admin is True!
# If exception, is_admin is undefined!
# Proceed with deletion
db.delete_user(user_id)
return {"status": "User deleted"}The vulnerability:
If check_admin_status() throws exception:
Exception caught but not handled properly
is_adminundefinedCode proceeds anyway
User deleted even though authorization failed
The attack:
Attacker sends request while admin check service is down:
Admin check service fails with connection error:
Exception thrown
Caught and ignored (pass statement)
Code proceeds anyway
User 42 deleted by non-admin attacker
Result:
Complete authorization bypass
Unauthorized admin actions
Data deletion
Service unavailable = security disabled
The fix:
Fail securely (default deny):
Finding it: Look for try/except blocks that catch security checks. Check if exception handling leads to access grant rather than denial.
Scenario 2: Missing Break Statement in Switch (CWE-484)
User role authorization check missing break:
The vulnerability:
Missing break causes fall-through:
role = "user"setsaccessLevel = 1Falls through to
admincaseOverrides with
accessLevel = 10User becomes admin!
The attack:
Attacker registers as regular user, then somehow triggers the "user" case followed by admin case:
Result:
Privilege escalation
Regular user becomes admin
Unauthorized access
The fix:
Always include break statements:
Finding it: Search for switch statements without break. Use compiler warnings. Test for fall-through behavior.
Scenario 3: Missing Default Case (CWE-478)
Payment status check missing default case:
The vulnerability:
No default case for unexpected values:
status = "approved"→ Delivers (correct)status = "declined"→ Declines (correct)status = "pending"→ Falls through, does nothingstatus = "refund"→ Falls through, does nothingstatus = "invalid"→ Falls through, does nothing
Attacker sends status = "pending":
No delivery
No payment charge
Free product
The attack:
Attacker modifies API call:
Result:
Free products
Revenue loss
Fraud
The fix:
Always have default case:
Or use explicit state machine:
Finding it: Look for if/elif chains without else. Look for switch statements without default. Check for unhandled enum values.
Scenario 4: NULL Pointer Dereference (CWE-476)
User authorization check returns NULL:
The vulnerability:
If getCurrentUserId() returns invalid ID:
findUserById()returns nulluser.isAdmin()throws NullPointerExceptionException caught and ignored
Authorization check fails silently
Code continues anyway
User deletion proceeds without authorization check
The attack:
Attacker sends request with invalid user ID:
findUserById("invalid")returns nullisAdmin()throws NullPointerExceptionException silently caught
deleteUser()proceedsUser deleted without proper authorization
Result:
Authorization bypass
Unauthorized deletion
Data destruction
The fix:
Always check for null:
Finding it: Search for method calls without null checks. Use static analysis tools (FindBugs, Checkstyle). Enable compiler warnings for null dereferences.
Scenario 5: Divide By Zero (CWE-369)
Price calculation with user-provided denominator:
Normal case:
The attack:
Attacker provides 0 discount:
Actually, divide by zero needs different scenario. Better example:
The attack:
Attacker provides 0 items:
Result:
Divide by zero exception
Application crash
Denial of service
The fix:
Validate input:
Finding it: Look for division operations. Check if denominator is validated. Test with zero values.
Scenario 6: Inverted Authorization Logic
Authorization check logic accidentally inverted:
The vulnerability:
Logic is backwards:
If
user_id == current_user.id→ Deny (your own profile)If
user_id != current_user.id→ Allow (someone else's profile)
The attack:
Attacker gets other user's ID (123) and modifies their profile:
Authorization check:
user_id = 123(victim)current_user.id = 456(attacker)123 != 456→ Allow!Attacker modifies victim's profile
Result:
Account takeover
Profile defacement
Email change
Password reset
The fix:
Correct the logic:
Finding it: Review authorization logic carefully. Test with unauthorized users. Look for inverted conditions (not equals instead of equals).
Scenario 7: Default Allow in Access Control
Access control matrix with missing entries defaults to allow:
The vulnerability:
Actually, this one defaults to deny. Better example:
The attack:
Attacker sends empty or null role:
Result:
Complete authorization bypass
Unauthorized access
Admin actions as non-admin
The fix:
Always validate and fail securely:
Finding it: Review access control logic. Check for default allow. Look for missing validation. Test with null/empty values.
Mitigation Strategies
Fail secure (default deny)
Validate all input
Check for null/empty
Use explicit state machines
Comprehensive exception handling
Code review
Review all authorization logic
Check switch statements for break
Verify all cases handled
Test error paths
Static analysis
Use FindBugs, Checkstyle, SonarQube
Check for null dereferences
Check for missing cases
Check for fall-through
Last updated