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
| Impact | Details |
|---|---|
| Other | Scope: Other Quality Degradation - Applications may behave unpredictably when specifications are not followed, leading to reliability issues. |
| Integrity | Scope: Integrity Varies by Context - Security protocols may fail to provide their guarantees when implementation requirements are violated. |
| Confidentiality | Scope: 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
- MITRE Corporation. "CWE-573: Improper Following of Specification by Caller." https://cwe.mitre.org/data/definitions/573.html
- POSIX.1-2017 Specification for signal handling and async-signal-safety.
- Oracle. "JDBC API Specification."