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

ImpactDetails
Non-RepudiationScope: 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

  1. MITRE Corporation. "CWE-397: Declaration of Throws for Generic Exception." https://cwe.mitre.org/data/definitions/397.html
  2. Joshua Bloch. "Effective Java." Item 73: Throw exceptions appropriate to the abstraction.