Unzureichende Verwendung symbolischer Konstanten
Beschreibung
Unzureichende Verwendung symbolischer Konstanten tritt auf, wenn Quellcode literale Konstanten (Magic Numbers oder Strings) verwendet, die sich möglicherweise ändern oder weiterentwickeln müssen, anstatt symbolische Konstanten mit aussagekräftigen Namen zu definieren und zu verwenden. Dies macht Code schwerer zu verstehen, zu warten und konsistent zu ändern. Wenn derselbe literale Wert an mehreren Stellen erscheint, erfordert eine Änderung das Finden und Aktualisieren aller Vorkommen, was fehleranfällig ist und zu Inkonsistenzen führen kann.
Risiko
Die Verwendung literaler Konstanten anstelle symbolischer hat indirekte Sicherheitsimplikationen. Puffergrößen-Literale, die im Code verstreut sind, machen Buffer-Overflow-Schwachstellen wahrscheinlicher, wenn sich Größen ändern. Sicherheitsrelevante Konstanten (maximale Versuche, Timeouts, Schlüsselgrößen) können inkonsistent aktualisiert werden. Code-Review wird schwieriger, wenn Magic Numbers die Absicht verschleiern. Wartende können ein Vorkommen ändern, aber andere übersehen, was Sicherheitslücken erzeugt. Tests werden durch unklare Konstantenwerte erschwert. Konfigurationsfehler sind mit hartcodierten Werten wahrscheinlicher.
Lösung
Definieren Sie symbolische Konstanten für alle Werte, die semantische Bedeutung haben oder sich ändern müssen. Verwenden Sie sprachgeeignete Konstantenmechanismen (const, #define, enum, final, static readonly). Gruppieren Sie verwandte Konstanten in dedizierten Dateien oder Klassen. Benennen Sie Konstanten, um ihren Zweck klar zu vermitteln. Verwenden Sie Konstanten konsistent in der gesamten Codebasis. Wenden Sie statische Analyse an, um Magic Numbers zu erkennen. Fügen Sie Einheiten in Konstantennamen ein, wo anwendbar (z.B. TIMEOUT_SECONDS). Dokumentieren Sie die Begründung für Konstantenwerte.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Andere | Bereich: Ändere Reduzierte Wartbarkeit - Literale Konstanten machen Code schwerer zu verstehen und zu aktualisieren. |
| Andere | Bereich: Ändere Erhöhte analytische Komplexität - Magic Numbers verschleiern Code-Absicht. |
| Integrität | Bereich: Integrität Inkonsistenter Zustand - Aktualisieren eines Literals aber Übersehen anderer verursacht Inkonsistenzen. |
Beispielcode
Anfälliger Code
// Anfällig: Literale Konstanten (Magic Numbers)
#include <stdio.h>
#include <string.h>
void vulnerable_process_input() {
// Anfällig: Magic Number 1024 erscheint mehrfach
char buffer[1024];
char temp[1024];
// Wenn wir Puffergröße ändern müssen, müssen wir ALLE Vorkommen finden
fgets(buffer, 1024, stdin); // Magic Number dupliziert
// Anfällig: Was bedeutet 5?
if (login_attempts > 5) {
lock_account();
}
// Anfällig: Was ist 30?
sleep(30); // Timeout? Sekunden? Minuten?
// Anfällig: Sicherheitsrelevante Magic Numbers
if (password_length < 8) { // Warum 8? Wo wird dies sonst geprüft?
reject_password();
}
// Anfällig: Kryptographische Konstanten ohne Erklärung
unsigned char key[32]; // AES-256 Schlüsselgröße, aber unklar
unsigned char iv[16]; // Blockgröße, aber unklar
// Anfällig: Netzwerkkonstanten
connect_to_server("192.168.1.100", 8080); // Hartcodierte IP und Port
// Anfällig: Fehlercodes als Magic Numbers
if (result == -1) { // Welcher Fehler?
handle_error();
} else if (result == -2) { // Anderer Fehler?
handle_other_error();
}
}
// Anfällig: Gleiche Werte über Funktionen dupliziert
void vulnerable_validate_user(char* username) {
if (strlen(username) < 3 || strlen(username) > 50) {
reject_username();
}
}
void vulnerable_validate_email(char* email) {
// Gleiche Limits dupliziert - leicht aus dem Sync zu geraten
if (strlen(email) < 3 || strlen(email) > 50) {
reject_email();
}
}
// Anfällig: Java mit Magic Numbers
public class VulnerableUserService {
public void validatePassword(String password) {
// Anfällig: Magic Numbers für Passwortregeln
if (password.length() < 8) { // Warum 8?
throw new ValidationException("Passwort zu kurz");
}
if (password.length() > 128) { // Warum 128?
throw new ValidationException("Passwort zu lang");
}
}
public void processLogin(String username, String password) {
// Anfällig: Magic Number für Wiederholungslimit
for (int i = 0; i < 3; i++) {
if (attemptLogin(username, password)) {
return;
}
// Anfällig: Magic Number für Verzögerung (Millisekunden? Sekunden?)
Thread.sleep(1000);
}
lockAccount(username);
}
public byte[] encryptData(byte[] data) {
// Anfällig: Kryptographische Magic Numbers
byte[] key = new byte[32]; // AES Schlüsselgröße
byte[] iv = new byte[16]; // IV Größe
// Anfällig: Algorithmusname als String-Literal
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
return cipher.doFinal(data);
}
public void cacheData(String key, Object value) {
// Anfällig: Was bedeutet 3600?
cache.put(key, value, 3600); // TTL in Sekunden? Millisekunden?
}
public void sendRequest() {
// Anfällig: Timeout Magic Number
connection.setConnectTimeout(5000); // 5 Sekunden, aber nicht klar
connection.setReadTimeout(30000); // 30 Sekunden, aber nicht klar
}
}
# Anfällig: Python mit Magic Numbers
class VulnerableDataProcessor:
def process_file(self, filepath):
# Anfällig: Magic Number für Chunk-Größe
with open(filepath, 'rb') as f:
while True:
chunk = f.read(4096) # Warum 4096?
if not chunk:
break
self.process_chunk(chunk)
def validate_input(self, data):
# Anfällig: Magic Numbers für Validierung
if len(data) > 10000: # Warum 10000?
raise ValueError("Eingabe zu groß")
if data.count('\n') > 1000: # Warum 1000?
raise ValueError("Zu viele Zeilen")
def calculate_rate(self, requests, period):
# Anfällig: Magic Number für Rate-Limit
if requests / period > 100: # 100 Anfragen pro was?
raise RateLimitException()
def connect_database(self):
# Anfällig: Hartcodierte Verbindungsparameter
return connect(
host='localhost',
port=5432,
database='myapp',
pool_size=10, # Warum 10?
timeout=30 # Sekunden? Millisekunden?
)
Korrigierter Code
// Korrigiert: Ordnungsgemäße Verwendung symbolischer Konstanten
#include <stdio.h>
#include <string.h>
// Sicherheitsrelevante Konstanten
enum SecurityConstants {
MAX_LOGIN_ATTEMPTS = 5,
ACCOUNT_LOCKOUT_SECONDS = 30,
MIN_PASSWORD_LENGTH = 8,
MAX_PASSWORD_LENGTH = 128
};
// Puffergrößen-Konstanten
enum BufferSizes {
INPUT_BUFFER_SIZE = 1024,
MAX_USERNAME_LENGTH = 50,
MIN_USERNAME_LENGTH = 3,
MAX_EMAIL_LENGTH = 254 // RFC 5321
};
// Kryptographische Konstanten
enum CryptoConstants {
AES_256_KEY_SIZE = 32,
AES_BLOCK_SIZE = 16,
PBKDF2_ITERATIONS = 100000
};
// Netzwerkkonstanten
static const char* const DEFAULT_SERVER_HOST = "192.168.1.100";
static const int DEFAULT_SERVER_PORT = 8080;
// Fehlercodes mit aussagekräftigen Namen
typedef enum {
ERROR_SUCCESS = 0,
ERROR_CONNECTION_FAILED = -1,
ERROR_AUTHENTICATION_FAILED = -2,
ERROR_TIMEOUT = -3
} ErrorCode;
void fixed_process_input() {
// Korrigiert: Symbolische Konstanten verwenden
char buffer[INPUT_BUFFER_SIZE];
char temp[INPUT_BUFFER_SIZE];
fgets(buffer, INPUT_BUFFER_SIZE, stdin);
// Korrigiert: Klare Absicht
if (login_attempts > MAX_LOGIN_ATTEMPTS) {
lock_account();
}
// Korrigiert: Timeout klar benannt
sleep(ACCOUNT_LOCKOUT_SECONDS);
// Korrigiert: Passwortanforderungen klar
if (password_length < MIN_PASSWORD_LENGTH) {
reject_password();
}
// Korrigiert: Krypto-Größen benannt
unsigned char key[AES_256_KEY_SIZE];
unsigned char iv[AES_BLOCK_SIZE];
// Korrigiert: Benannte Fehlercodes
ErrorCode result = connect_to_server(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);
if (result == ERROR_CONNECTION_FAILED) {
handle_connection_error();
} else if (result == ERROR_AUTHENTICATION_FAILED) {
handle_auth_error();
}
}
// Korrigiert: Geteilte Konstanten konsistent verwendet
void fixed_validate_user(char* username) {
if (strlen(username) < MIN_USERNAME_LENGTH ||
strlen(username) > MAX_USERNAME_LENGTH) {
reject_username();
}
}
void fixed_validate_email(char* email) {
// Gleiche Konstante stellt Konsistenz sicher
if (strlen(email) < MIN_USERNAME_LENGTH ||
strlen(email) > MAX_EMAIL_LENGTH) {
reject_email();
}
}
// Korrigiert: Java mit ordnungsgemäßen Konstanten
public class FixedUserService {
// Sicherheitskonstanten
private static final int MIN_PASSWORD_LENGTH = 8;
private static final int MAX_PASSWORD_LENGTH = 128;
private static final int MAX_LOGIN_ATTEMPTS = 3;
private static final int RETRY_DELAY_MILLIS = 1000;
// Kryptographische Konstanten
private static final int AES_KEY_SIZE_BYTES = 32; // 256 Bits
private static final int AES_IV_SIZE_BYTES = 16; // 128 Bits
private static final String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";
// Cache-Konstanten
private static final int DEFAULT_CACHE_TTL_SECONDS = 3600; // 1 Stunde
// Verbindungskonstanten
private static final int CONNECT_TIMEOUT_MILLIS = 5000; // 5 Sekunden
private static final int READ_TIMEOUT_MILLIS = 30000; // 30 Sekunden
public void validatePassword(String password) {
// Korrigiert: Konstanten machen Absicht klar
if (password.length() < MIN_PASSWORD_LENGTH) {
throw new ValidationException(
"Passwort muss mindestens " + MIN_PASSWORD_LENGTH + " Zeichen haben");
}
if (password.length() > MAX_PASSWORD_LENGTH) {
throw new ValidationException(
"Passwort darf " + MAX_PASSWORD_LENGTH + " Zeichen nicht überschreiten");
}
}
public void processLogin(String username, String password) {
// Korrigiert: Benannte Konstanten
for (int attempt = 0; attempt < MAX_LOGIN_ATTEMPTS; attempt++) {
if (attemptLogin(username, password)) {
return;
}
Thread.sleep(RETRY_DELAY_MILLIS);
}
lockAccount(username);
}
public byte[] encryptData(byte[] data) {
// Korrigiert: Benannte Krypto-Konstanten
byte[] key = new byte[AES_KEY_SIZE_BYTES];
byte[] iv = new byte[AES_IV_SIZE_BYTES];
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
return cipher.doFinal(data);
}
public void cacheData(String key, Object value) {
// Korrigiert: Benannte TTL-Konstante
cache.put(key, value, DEFAULT_CACHE_TTL_SECONDS);
}
public void sendRequest() {
// Korrigiert: Benannte Timeout-Konstanten
connection.setConnectTimeout(CONNECT_TIMEOUT_MILLIS);
connection.setReadTimeout(READ_TIMEOUT_MILLIS);
}
}
// Korrigiert: Konstanten in dedizierter Klasse zum Teilen
public final class SecurityConstants {
public static final int MIN_PASSWORD_LENGTH = 8;
public static final int MAX_PASSWORD_LENGTH = 128;
public static final int MAX_LOGIN_ATTEMPTS = 3;
public static final int PASSWORD_EXPIRY_DAYS = 90;
// Instanziierung verhindern
private SecurityConstants() {}
}
# Korrigiert: Python mit ordnungsgemäßen Konstanten
from typing import Final
from enum import IntEnum
from dataclasses import dataclass
# Sicherheitskonstanten
MIN_PASSWORD_LENGTH: Final[int] = 8
MAX_PASSWORD_LENGTH: Final[int] = 128
MAX_LOGIN_ATTEMPTS: Final[int] = 3
# Dateiverarbeitungskonstanten
class FileConstants:
CHUNK_SIZE: Final[int] = 4096 # 4 KB Chunks für optimale I/O
MAX_INPUT_SIZE: Final[int] = 10_000 # Maximale Eingabe-Bytes
MAX_LINE_COUNT: Final[int] = 1000 # Maximale Zeilen pro Datei
# Rate-Limiting-Konstanten
class RateLimitConstants:
MAX_REQUESTS_PER_SECOND: Final[int] = 100
RATE_LIMIT_WINDOW_SECONDS: Final[int] = 1
# Datenbankkonstanten
@dataclass(frozen=True)
class DatabaseConfig:
HOST: str = 'localhost'
PORT: int = 5432
DATABASE: str = 'myapp'
POOL_SIZE: int = 10
TIMEOUT_SECONDS: int = 30
# Fehlercodes als Enum
class ErrorCode(IntEnum):
SUCCESS = 0
CONNECTION_FAILED = -1
AUTHENTICATION_FAILED = -2
TIMEOUT = -3
class FixedDataProcessor:
def process_file(self, filepath: str) -> None:
"""Datei in Chunks verarbeiten."""
with open(filepath, 'rb') as f:
while True:
# Korrigiert: Benannte Konstante mit Dokumentation
chunk = f.read(FileConstants.CHUNK_SIZE)
if not chunk:
break
self.process_chunk(chunk)
def validate_input(self, data: str) -> None:
"""Eingabedaten validieren."""
# Korrigiert: Benannte Konstanten machen Limits klar
if len(data) > FileConstants.MAX_INPUT_SIZE:
raise ValueError(
f"Eingabe überschreitet maximale Größe von {FileConstants.MAX_INPUT_SIZE}")
if data.count('\n') > FileConstants.MAX_LINE_COUNT:
raise ValueError(
f"Eingabe überschreitet Maximum von {FileConstants.MAX_LINE_COUNT} Zeilen")
def calculate_rate(self, requests: int, period_seconds: float) -> None:
"""Rate-Limit prüfen."""
# Korrigiert: Benannte Konstanten mit Einheiten im Namen
rate = requests / period_seconds
if rate > RateLimitConstants.MAX_REQUESTS_PER_SECOND:
raise RateLimitException(
f"Rate {rate}/s überschreitet Limit von "
f"{RateLimitConstants.MAX_REQUESTS_PER_SECOND}/s")
def connect_database(self):
"""Mit Datenbank mit konfigurierten Einstellungen verbinden."""
# Korrigiert: Konfigurationsobjekt mit benannten Werten
config = DatabaseConfig()
return connect(
host=config.HOST,
port=config.PORT,
database=config.DATABASE,
pool_size=config.POOL_SIZE,
timeout=config.TIMEOUT_SECONDS
)
CVE-Beispiele
Diese CWE ist für direkte CVE-Zuordnung als VERBOTEN markiert, da sie ein Codequalitätsproblem und keine direkte Sicherheitsschwachstelle darstellt.
Verwandte CWEs
- CWE-1078: Inappropriate Source Code Style or Formatting (Eltern)
- CWE-1006: Bad Coding Practices (Kategoriemitglied)
- CWE-1107: Insufficient Isolation of Symbolic Constant Definitions (verwandt)
Referenzen
- MITRE Corporation. "CWE-1106: Insufficient Use of Symbolic Constants." https://cwe.mitre.org/data/definitions/1106.html
- Martin, Robert C. "Clean Code" - Meaningful Names.
- CERT C Coding Standard. "DCL06-C: Use meaningful symbolic constants to represent literal values."