01 Exception Mishandling Failure Modes
Exceptional condition flaws emerge when code paths do not account for unpredicted states or system faults:
- Fail-Open Authorization: An authorization check throws a null pointer or database connection timeout, and the catching logic defaults to allowing access rather than denying it.
- Unreleased Resources on Error: Unhandled exceptions before database transactions are committed or file locks/handles are released, leading to thread pool exhaustion and denial of service.
- Partial State Mutation: Exception occurring midway through a multi-step financial transaction without transactional rollback, leaving accounts in an inconsistent state.
- Silent Exception Swallowing: Empty
catch (Exception e) {}blocks hiding upstream system errors.
02 Vulnerability Demonstration: Fail-Open Logic
Vulnerable Implementation: Fail-Open on Policy Error
vulnerable_policy.phpPHP
// VULNERABLE: Fail-Open logic on network timeout or service failure
function isActionAllowed($userId, $action) {
try {
$client = new AuthPolicyServiceClient();
$response = $client->checkPermission($userId, $action);
return $response->isAllowed();
} catch (Exception $e) {
// CATASTROPHIC FLAW: Fails open to prevent "blocking users" on policy server errors
error_log("Policy server down: " . $e->getMessage());
return true; // Grants access to all restricted resources!
}
}
03 Audit & Static Code Analysis Rules
| Anti-Pattern | AST / Grep Pattern | Remediation |
|---|---|---|
| Fail-Open Return | catch\s*\(.*\)\s*\{\s*return\s+true; |
Enforce Fail-Closed (return false) |
| Empty Catch Block | catch\s*\([^\)]+\)\s*\{\s*\} |
Explicit handling, logging, and error state propagation |
04 Hardened Fail-Safe Exception Pattern
Fail-Closed Architecture with Transactional Rollbacks
secure_exception_engine.phpPHP 8+
class AccountTransferService {
private PDO $db;
public function transferFunds(int $fromId, int $toId, int $amountCents): bool {
if ($amountCents <= 0) {
throw new InvalidArgumentException("Transfer amount must be positive");
}
$this->db->beginTransaction();
try {
// 1. Deduct funds
$stmt = $this->db->prepare("UPDATE accounts SET balance = balance - :amt WHERE id = :id AND balance >= :amt");
$stmt->execute([':amt' => $amountCents, ':id' => $fromId]);
if ($stmt->rowCount() === 0) {
throw new InsufficientFundsException("Insufficient balance");
}
// 2. Credit destination
$stmt = $this->db->prepare("UPDATE accounts SET balance = balance + :amt WHERE id = :id");
$stmt->execute([':amt' => $amountCents, ':id' => $toId]);
$this->db->commit();
return true;
} catch (Throwable $e) {
// Roll back completely on ANY failure
if ($this->db->inTransaction()) {
$this->db->rollBack();
}
error_log("Transaction failed: " . $e->getMessage());
return false; // Fail-Closed
}
}
}
05 Verification & Compliance Checklist
| Resilience Gate | Standard | Status |
|---|---|---|
| Fail-Closed Architecture | All authorization checks fail closed (deny access) on exception. | Enforced |
| Database Transactions | Atomic multi-statement mutations wrapped in rollback blocks. | Enforced |
| No Empty Catches | Zero swallowed exceptions across all service layers. | Enforced |