Declaration of Throws for Generic Exception
Description
Declaration of Throws for Generic Exception is a vulnerability that occurs when methods declare they throw overly broad exceptions like Exception or Throwable, rather than specific exception types. This practice obscures important details about what can go wrong and produces inappropriate responses to specific error conditions. Java's exception mechanism is designed to allow developers to catch and respond to particular exception types, and declaring generic exceptions defeats this capability, making proper error handling by callers difficult or impossible.
Risk
When methods declare they throw generic exceptions, callers cannot anticipate or handle specific error conditions properly. The caller is forced to either catch the generic exception (leading to CWE-396) or declare that they also throw the generic exception, propagating the problem. Security-relevant exceptions get mixed with mundane errors, making it difficult to implement appropriate security responses. Generic throw declarations can hide details about unexpected adversary activities by making it difficult to troubleshoot specific error conditions. Code maintenance becomes harder as the actual exceptions that can occur are undocumented.
Solution
Declare specific exception types that methods can throw. Create custom exception hierarchies that meaningfully categorize different error conditions. Document which exceptions each method can throw using throws clauses for checked exceptions and Javadoc for unchecked exceptions. When wrapping lower-level exceptions, preserve the original exception as the cause. Consider whether each exception should be checked or unchecked based on whether callers can reasonably recover from it. Refactor existing code that uses generic exception declarations to use specific types.
Common Consequences
| Impact | Details |
|---|---|
| Non-Repudiation | Scope: Non-Repudiation, Other Hide Activities, Alter Execution Logic - Throwing a generic exception can hide details about unexpected adversary activities by making it difficult to properly troubleshoot error conditions during execution. |
Example Code
Vulnerable Code
// Vulnerable: Generic throws declaration
public class VulnerableService {
// Vulnerable: Throws generic Exception
public void doExchange() throws Exception {
// Caller has no idea what specific exceptions to expect
connectToServer();
sendData();
receiveResponse();
}
// Vulnerable: Throws Throwable (even worse)
public void processRequest() throws Throwable {
// Includes Error types that shouldn't be declared
handleRequest();
}
// Vulnerable: Generic exception hides what can go wrong
public User authenticate(String username, String password)
throws Exception {
// Could be: UserNotFoundException, InvalidPasswordException,
// DatabaseException, AccountLockedException, etc.
User user = userRepository.findByUsername(username);
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new Exception("Authentication failed");
}
return user;
}
// Vulnerable: Chain of generic exceptions
public void processOrder(Order order) throws Exception {
validateOrder(order); // throws Exception
chargePayment(order); // throws Exception
shipOrder(order); // throws Exception
}
}
// Vulnerable: C++ generic exception specification
class VulnerableProcessor {
public:
// Vulnerable: Overly broad exception specification
int process() throw(std::exception) {
// Caller can't distinguish between exception types
return doProcessing();
}
// Vulnerable: Throwing generic exception
void validate() {
if (!isValid()) {
throw std::runtime_error("Validation failed");
// Should throw specific validation exception
}
}
};
Fixed Code
// Fixed: Specific throws declarations
public class SecureService {
// Fixed: Declare specific exceptions
public void doExchange()
throws ConnectionException, DataTransferException {
connectToServer(); // throws ConnectionException
sendData(); // throws DataTransferException
receiveResponse(); // throws DataTransferException
}
// Fixed: Specific exception hierarchy for authentication
public User authenticate(String username, String password)
throws AuthenticationException {
try {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UserNotFoundException(username);
}
if (user.isLocked()) {
throw new AccountLockedException(username);
}
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new InvalidCredentialsException();
}
return user;
} catch (DatabaseException e) {
// Wrap infrastructure exception
throw new AuthenticationException("Database error", e);
}
}
// Fixed: Clear exception types for order processing
public void processOrder(Order order)
throws ValidationException, PaymentException, ShippingException {
validateOrder(order); // throws ValidationException
chargePayment(order); // throws PaymentException
shipOrder(order); // throws ShippingException
}
}
// Fixed: Custom exception hierarchy
public abstract class AuthenticationException extends Exception {
public AuthenticationException(String message) {
super(message);
}
public AuthenticationException(String message, Throwable cause) {
super(message, cause);
}
}
public class UserNotFoundException extends AuthenticationException {
private final String username;
public UserNotFoundException(String username) {
super("User not found: " + username);
this.username = username;
}
public String getUsername() { return username; }
}
public class InvalidCredentialsException extends AuthenticationException {
public InvalidCredentialsException() {
super("Invalid credentials");
}
}
public class AccountLockedException extends AuthenticationException {
private final String username;
public AccountLockedException(String username) {
super("Account locked: " + username);
this.username = username;
}
public String getUsername() { return username; }
}
// Fixed: Proper exception handling enabled by specific declarations
public class SecureController {
public Response handleLogin(String username, String password) {
try {
User user = authService.authenticate(username, password);
return Response.ok(user);
} catch (UserNotFoundException e) {
// Fixed: Can handle differently if needed
// (but might want same response for security)
return Response.unauthorized("Invalid credentials");
} catch (InvalidCredentialsException e) {
// Fixed: Can implement rate limiting
rateLimiter.recordFailedAttempt(username);
return Response.unauthorized("Invalid credentials");
} catch (AccountLockedException e) {
// Fixed: Specific handling for locked accounts
auditLog.logLockedAccountAttempt(e.getUsername());
return Response.forbidden("Account is locked");
}
}
}
// Fixed: Specific exception types in C++
#include <stdexcept>
class ValidationException : public std::runtime_error {
public:
explicit ValidationException(const std::string& msg)
: std::runtime_error(msg) {}
};
class ProcessingException : public std::runtime_error {
public:
explicit ProcessingException(const std::string& msg)
: std::runtime_error(msg) {}
};
class SecureProcessor {
public:
// Fixed: No exception specification (modern C++ style)
// Document exceptions in comments or use noexcept where appropriate
int process() {
// Throws specific exceptions
validate(); // throws ValidationException
return execute(); // throws ProcessingException
}
void validate() {
if (!isValid()) {
// Fixed: Throw specific exception
throw ValidationException("Input validation failed");
}
}
};
CVE Examples
No specific CVEs are listed for this CWE. The vulnerability pattern appears in:
- Java APIs with throws Exception declarations
- Libraries that propagate generic exceptions to callers
- Code that wraps specific exceptions in generic types
References
- MITRE Corporation. "CWE-397: Declaration of Throws for Generic Exception." https://cwe.mitre.org/data/definitions/397.html
- Joshua Bloch. "Effective Java." Item 73: Throw exceptions appropriate to the abstraction.