Improper Following of Specification by Caller

Description

Improper Following of Specification by Caller is a vulnerability where a product fails to adhere to or incorrectly follows the specifications mandated by the implementation language, environment, framework, protocol, or platform. When leveraging external functionality such as APIs, libraries, or protocols, it is critical that the caller does so in accordance with the documented requirements and contracts. Failing to follow these specifications can result in undefined behavior, security vulnerabilities, data corruption, or system failures that may be difficult to predict and diagnose.

Risk

Violating specifications creates unpredictable and often dangerous behavior. Security protocols may fail silently when implementation requirements are not followed, leaving systems vulnerable despite appearing secure. Cryptographic implementations that skip required steps like proper padding verification can enable signature forgery attacks. API misuse can lead to memory corruption, resource leaks, or privilege escalation. The risk is compounded because such violations often work in testing but fail catastrophically in production or under adversarial conditions. Additionally, specification violations may not trigger obvious errors, making vulnerabilities difficult to detect.

Solution

Thoroughly read and understand the complete specification for any external functionality before use. Follow all documented requirements, including error handling, initialization sequences, and cleanup procedures. Pay special attention to security-relevant specifications in cryptographic libraries and authentication protocols. Use static analysis tools that can detect specification violations. Implement comprehensive testing that covers edge cases and error conditions specified in the documentation. Regularly review code for compliance with updated specifications. Consider using wrapper functions that enforce correct usage patterns.

Common Consequences

ImpactDetails
OtherScope: Other

Quality Degradation - Applications may behave unpredictably when specifications are not followed, leading to reliability issues.
IntegrityScope: Integrity

Varies by Context - Security protocols may fail to provide their guarantees when implementation requirements are violated.
ConfidentialityScope: Confidentiality

Varies by Context - Improper use of cryptographic or authentication APIs may expose sensitive data or credentials.

Example Code

Vulnerable Code

// Vulnerable: Not following JDBC specification for connection cleanup
import java.sql.*;

public class VulnerableJdbcUsage {

    // Vulnerable: Not following specification for resource cleanup
    public List<User> getUsers() throws SQLException {
        Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT * FROM users");

        List<User> users = new ArrayList<>();
        while (rs.next()) {
            users.add(new User(rs.getString("name"), rs.getInt("id")));
        }

        // Vulnerable: Resources not closed in proper order
        // Specification requires closing in reverse order of creation
        conn.close();  // Wrong! Should close ResultSet, Statement, then Connection

        return users;
        // ResultSet and Statement may leak
    }

    // Vulnerable: Not following PreparedStatement specification
    public void updateUser(String userId, String name) throws SQLException {
        Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);

        // Vulnerable: Reusing PreparedStatement incorrectly
        PreparedStatement pstmt = conn.prepareStatement(
            "UPDATE users SET name = ? WHERE id = ?");

        // Specification says parameters must be set before each execution
        pstmt.setString(1, name);
        pstmt.setString(2, userId);
        pstmt.executeUpdate();

        // Vulnerable: Reusing without clearing parameters
        pstmt.setString(1, "new name");
        // Missing: pstmt.setString(2, newId);
        pstmt.executeUpdate();  // Second parameter retained from before
    }
}
// Vulnerable: Not following cryptographic specification
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;

public class VulnerableCryptoUsage {

    // Vulnerable: RSA without proper padding verification
    public byte[] vulnerableDecrypt(byte[] ciphertext, PrivateKey key)
            throws Exception {

        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, key);

        // Vulnerable: Not checking for padding errors correctly
        // Specification requires validating padding to prevent oracle attacks
        try {
            return cipher.doFinal(ciphertext);
        } catch (BadPaddingException e) {
            // Vulnerable: Returning null reveals padding failure
            // This enables Bleichenbacher-style attacks (CVE-2006-4339)
            return null;
        }
    }

    // Vulnerable: Not following IV specification for CBC mode
    public byte[] vulnerableEncrypt(byte[] plaintext, SecretKey key)
            throws Exception {

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

        // Vulnerable: Using fixed IV instead of random
        // Specification requires unique IV for each encryption
        byte[] fixedIv = new byte[16];  // All zeros!
        IvParameterSpec ivSpec = new IvParameterSpec(fixedIv);

        cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
        return cipher.doFinal(plaintext);
    }

    // Vulnerable: Not following key derivation specification
    public SecretKey vulnerableKeyDerivation(String password) throws Exception {
        // Vulnerable: Using MD5 instead of proper PBKDF
        // Specification recommends PBKDF2 with iteration count
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] keyBytes = md.digest(password.getBytes());

        return new SecretKeySpec(keyBytes, "AES");
    }
}
// Vulnerable: Not following POSIX signal specification
#include <signal.h>
#include <stdio.h>

// Vulnerable: Async-signal-unsafe functions in handler
void vulnerable_signal_handler(int sig) {
    // Vulnerable: printf is not async-signal-safe per POSIX
    printf("Caught signal %d\n", sig);  // Violates specification

    // Vulnerable: malloc is not async-signal-safe
    char* buffer = malloc(100);  // May cause deadlock

    // Vulnerable: exit is not async-signal-safe
    exit(1);  // Should use _exit() in signal handler
}

void setup_vulnerable_handler() {
    // Using non-async-signal-safe functions in handlers
    signal(SIGINT, vulnerable_signal_handler);
}

// Vulnerable: Not following fork() specification
void vulnerable_fork_usage() {
    FILE* file = fopen("data.txt", "w");

    pid_t pid = fork();

    // Vulnerable: Both parent and child have same FILE*
    // Specification says buffered I/O state is duplicated
    if (pid == 0) {
        fprintf(file, "Child writing\n");
        fclose(file);
    } else {
        fprintf(file, "Parent writing\n");
        fclose(file);
    }
    // Output may be corrupted or interleaved
}
# Vulnerable: Not following iterator specification
class VulnerableIterator:
    def __init__(self, items):
        self.items = items
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index < len(self.items):
            result = self.items[self.index]
            self.index += 1
            return result
        # Vulnerable: Specification requires raising StopIteration
        return None  # Wrong! Should raise StopIteration

# Vulnerable: Not following context manager specification
class VulnerableContextManager:
    def __init__(self):
        self.resource = None

    def __enter__(self):
        self.resource = acquire_resource()
        # Vulnerable: Must return something (usually self)
        # Missing return statement

    def __exit__(self, exc_type, exc_val, exc_tb):
        # Vulnerable: Not handling cleanup on exception
        if exc_type is None:
            release_resource(self.resource)
        # Specification says cleanup should happen regardless of exception
        # Also not returning True/False as specified

Fixed Code

// Fixed: Proper JDBC resource management following specification
import java.sql.*;

public class SecureJdbcUsage {

    // Fixed: Using try-with-resources per specification
    public List<User> getUsers() throws SQLException {
        List<User> users = new ArrayList<>();

        // Fixed: Resources automatically closed in reverse order
        try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {

            while (rs.next()) {
                users.add(new User(rs.getString("name"), rs.getInt("id")));
            }
        }
        // Resources closed properly: rs, stmt, conn (in reverse order)

        return users;
    }

    // Fixed: Proper PreparedStatement usage per specification
    public void updateUsers(List<UserUpdate> updates) throws SQLException {
        try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
             PreparedStatement pstmt = conn.prepareStatement(
                 "UPDATE users SET name = ? WHERE id = ?")) {

            for (UserUpdate update : updates) {
                // Fixed: Clear and set all parameters before each execution
                pstmt.clearParameters();
                pstmt.setString(1, update.getName());
                pstmt.setString(2, update.getId());
                pstmt.executeUpdate();
            }
        }
    }
}
// Fixed: Following cryptographic specifications
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;

public class SecureCryptoUsage {

    // Fixed: Constant-time padding error handling
    public byte[] secureDecrypt(byte[] ciphertext, PrivateKey key)
            throws GeneralSecurityException {

        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        cipher.init(Cipher.DECRYPT_MODE, key);

        // Fixed: Don't reveal timing information about padding
        // Use OAEP padding which is more secure than PKCS#1 v1.5
        return cipher.doFinal(ciphertext);
        // Exception handling should be uniform regardless of error type
    }

    // Fixed: Random IV per specification for CBC mode
    public EncryptedData secureEncrypt(byte[] plaintext, SecretKey key)
            throws GeneralSecurityException {

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

        // Fixed: Generate random IV per specification
        SecureRandom random = SecureRandom.getInstanceStrong();
        byte[] iv = new byte[12];  // GCM recommended IV size
        random.nextBytes(iv);

        GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec);

        byte[] ciphertext = cipher.doFinal(plaintext);

        // Return IV with ciphertext as specification requires
        return new EncryptedData(iv, ciphertext);
    }

    // Fixed: Proper key derivation following PKCS#5 specification
    public SecretKey secureKeyDerivation(String password, byte[] salt)
            throws GeneralSecurityException {

        // Fixed: Use PBKDF2 with proper iteration count per specification
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec spec = new PBEKeySpec(
            password.toCharArray(),
            salt,
            310000,  // OWASP recommended minimum iterations
            256      // Key length in bits
        );

        byte[] keyBytes = factory.generateSecret(spec).getEncoded();
        return new SecretKeySpec(keyBytes, "AES");
    }
}
// Fixed: Following POSIX signal specification
#include <signal.h>
#include <unistd.h>
#include <string.h>

// Flag for signal handling (sig_atomic_t is async-signal-safe)
static volatile sig_atomic_t signal_received = 0;

// Fixed: Only use async-signal-safe functions in handler
void secure_signal_handler(int sig) {
    // Fixed: Only set flag, don't call unsafe functions
    signal_received = sig;

    // Fixed: If must write, use async-signal-safe write()
    const char msg[] = "Signal received\n";
    write(STDERR_FILENO, msg, sizeof(msg) - 1);
}

void setup_secure_handler() {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = secure_signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;  // Restart interrupted syscalls

    sigaction(SIGINT, &sa, NULL);
}

void main_loop() {
    while (!signal_received) {
        // Do work
        // Check flag periodically and handle signal safely here
    }
    // Handle signal in main context where all functions are safe
    printf("Handled signal %d\n", signal_received);
}

// Fixed: Following fork() specification for file I/O
void secure_fork_usage() {
    pid_t pid = fork();

    if (pid == 0) {
        // Child: Open own file handle
        FILE* file = fopen("child_data.txt", "w");
        if (file) {
            fprintf(file, "Child writing\n");
            fclose(file);
        }
        _exit(0);  // Use _exit() in child after fork
    } else if (pid > 0) {
        // Parent: Use separate file
        FILE* file = fopen("parent_data.txt", "w");
        if (file) {
            fprintf(file, "Parent writing\n");
            fclose(file);
        }
        wait(NULL);
    }
}
# Fixed: Following iterator specification
class SecureIterator:
    def __init__(self, items):
        self.items = items
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index < len(self.items):
            result = self.items[self.index]
            self.index += 1
            return result
        # Fixed: Raise StopIteration as per specification
        raise StopIteration

# Fixed: Following context manager specification
class SecureContextManager:
    def __init__(self):
        self.resource = None

    def __enter__(self):
        self.resource = acquire_resource()
        return self  # Fixed: Return self as per specification

    def __exit__(self, exc_type, exc_val, exc_tb):
        # Fixed: Always cleanup regardless of exception
        if self.resource is not None:
            try:
                release_resource(self.resource)
            except Exception:
                pass  # Ensure cleanup doesn't raise during exception handling
            finally:
                self.resource = None

        # Fixed: Return False to propagate exceptions (or True to suppress)
        return False

# Usage with proper specification following
with SecureContextManager() as ctx:
    ctx.do_work()
# Resource guaranteed to be released

CVE Examples

  • CVE-2006-4339: OpenSSL RSA implementation did not properly verify PKCS#1 padding, enabling signature forgery attacks.
  • CVE-2006-7140: Crypto++ library incorrectly removed padding during signature verification, allowing forged signatures.

References

  1. MITRE Corporation. "CWE-573: Improper Following of Specification by Caller." https://cwe.mitre.org/data/definitions/573.html
  2. POSIX.1-2017 Specification for signal handling and async-signal-safety.
  3. Oracle. "JDBC API Specification."