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

AuswirkungDetails
AndereBereich: Ändere

Reduzierte Wartbarkeit - Literale Konstanten machen Code schwerer zu verstehen und zu aktualisieren.
AndereBereich: Ändere

Erhöhte analytische Komplexität - Magic Numbers verschleiern Code-Absicht.
IntegritätBereich: 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

  1. MITRE Corporation. "CWE-1106: Insufficient Use of Symbolic Constants." https://cwe.mitre.org/data/definitions/1106.html
  2. Martin, Robert C. "Clean Code" - Meaningful Names.
  3. CERT C Coding Standard. "DCL06-C: Use meaningful symbolic constants to represent literal values."