Dynamische Variablenevaluation
Beschreibung
Dynamische Variablenevaluation tritt auf, wenn Anwendungen Variablennamen verwenden, die von Benutzereingaben abgeleitet sind, um dynamisch auf Variablen zuzugreifen oder sie zu modifizieren. Dies umfasst PHPs variable Variablen ($$var), Pythons eval()/exec(), JavaScripts eval() und ähnliche Konstrukte. Angreifer können Variablennamen manipulieren, um auf sicherheitskritische Variablen zuzugreifen oder diese zu überschreiben, was zu Authentifizierungs-Bypass, Privilegien-Eskalation oder Code-Ausführung führt.
Risiko
Angreifer können beliebige Variablen im Anwendungsbereich lesen oder modifizieren. Sicherheitskritische Variablen wie Authentifizierungs-Flags können überschrieben werden. Konfigurationsvariablen können manipuliert werden. Im schlimmsten Fall führt dies zu beliebiger Code-Ausführung. Die Schwachstelle ermöglicht indirekte Kontrolle über den Anwendungszustand durch Variablennamen-Manipulation.
Lösung
Vermeiden Sie dynamische Variablenevaluation mit Benutzereingaben. Verwenden Sie Arrays oder Objekte mit validierten Schlüsseln stattdessen. Whitelisten Sie erlaubte Variablennamen. Verwenden Sie explizite Mappings zwischen Benutzereingabe und internen Namen. Verwenden Sie niemals eval() oder ähnliche Konstrukte auf Benutzereingaben. Implementieren Sie strikte Eingabevalidierung bei allen Werten, die im Variablenzugriff verwendet werden.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Bereich: Variablenmanipulation Beliebige Variablen können gelesen oder modifiziert werden. |
| Authentifizierung | Bereich: Bypass Auth-Variablen können überschrieben werden. |
| Vertraulichkeit | Bereich: Informationsoffenlegung Sensible Variablenwerte können extrahiert werden. |
Beispielcode + Lösungscode
Verwundbarer Code
<?php
// VERWUNDBAR: PHP variable Variablen
function processFormVulnerable($formData) {
// Erstellt Variablen aus Formulareingabe
foreach ($formData as $key => $value) {
$key = $value; // Variable Variable!
}
// Angreifer: formData['is_admin'] = '1'
// Jetzt $is_admin = '1'
if (isset($is_admin) && $is_admin) {
showAdminPanel();
}
}
// VERWUNDBAR: Dynamischer Property-Zugriff
class UserVulnerable {
public $name;
public $email;
public $role = 'user';
public function setProperty($prop, $value) {
// Angreifer kann jede Property setzen
$this->$prop = $value;
}
}
$user = new UserVulnerable();
$user->setProperty($_GET['prop'], $_GET['value']);
// Angriff: ?prop=role&value=admin
// VERWUNDBAR: Indirekte Variablenreferenz
function getConfigVulnerable($key) {
global $key; // Erlaubt Zugriff auf jedes Global
// Angriff: key=_SESSION
// Gibt $_SESSION-Inhalt zurück!
return $key;
}
// VERWUNDBAR: Dynamische Funktionsaufrufe
function callFunctionVulnerable($funcName, $args) {
// Ruft jede Funktion auf!
return $funcName(...$args);
}
// Angriff: funcName=system, args=['rm -rf /']
?>
# VERWUNDBAR: eval für dynamischen Variablenzugriff verwenden
user_data = {}
def set_variable_vulnerable(var_name, value):
# Verwendet eval um Variablen zu setzen
exec(f"{var_name} = {repr(value)}")
# Angriff: var_name="__import__('os').system('whoami')"
# VERWUNDBAR: getattr/setattr ohne Validierung
class ConfigVulnerable:
debug = False
secret_key = "supersecret"
def update(self, key, value):
# Setzt jedes Attribut
setattr(self, key, value)
config = ConfigVulnerable()
config.update(request.args['key'], request.args['value'])
# Angriff: ?key=debug&value=True
# VERWUNDBAR: globals()/locals() Manipulation
def update_config_vulnerable(updates):
for key, value in updates.items():
globals()[key] = value # Überschreibt jedes Global!
# Angriff: updates={'is_authenticated': True}
# VERWUNDBAR: Dynamischer Import
def load_module_vulnerable(module_name):
# Lädt jedes Modul
return __import__(module_name)
# Angriff: module_name='os' dann system() aufrufen
// VERWUNDBAR: eval() für Variablenzugriff
function getValueVulnerable(varName) {
// Evaluiert jeden Ausdruck
return eval(varName);
}
// Angriff: varName = "process.env.SECRET_KEY"
// Angriff: varName = "require('child_process').execSync('whoami')"
// VERWUNDBAR: Klammer-Notation mit Benutzereingabe
const config = {
theme: 'light',
language: 'en',
isAdmin: false
};
function updateConfig(key, value) {
// Benutzer kontrolliert key
config[key] = value;
}
// Angriff: updateConfig('isAdmin', true)
// VERWUNDBAR: Dynamischer Property-Zugriff auf global
function accessGlobal(name) {
return global[name]; // Oder window[name] im Browser
}
// Angriff: name = 'process' -> gibt Zugriff auf process-Objekt
// VERWUNDBAR: new Function() Konstruktor
function createCalculator(formula) {
// Erstellt ausführbaren Code aus Benutzereingabe
const calc = new Function('a', 'b', `return ${formula}`);
return calc;
}
// Angriff: formula = "a; require('child_process').execSync('whoami'); b"
Lösungscode
<?php
// SICHER: Array statt variable Variablen verwenden
function processFormSafe($formData) {
// Validiertes Array verwenden
$allowed = ['name', 'email', 'message'];
$safe = [];
foreach ($allowed as $key) {
if (isset($formData[$key])) {
$safe[$key] = $formData[$key];
}
}
// Sicherheitsvariablen von vertrauenswürdiger Quelle
$is_admin = checkAdminFromSession();
return $safe;
}
// SICHER: Property-Whitelist
class UserSafe {
private $name;
private $email;
private $role = 'user';
private $settableProperties = ['name', 'email'];
public function setProperty($prop, $value) {
// Nur spezifische Properties erlauben
if (!in_array($prop, $this->settableProperties)) {
throw new InvalidArgumentException("Kann nicht setzen: $prop");
}
$this->$prop = $value;
}
public function setName($name) { $this->name = $name; }
public function setEmail($email) { $this->email = $email; }
// Kein Setter für role!
}
// SICHER: Mapping statt dynamischer Zugriff
function getConfigSafe($key) {
$configMap = [
'app_name' => $GLOBALS['config']['app_name'],
'version' => $GLOBALS['config']['version'],
'theme' => $GLOBALS['config']['theme']
];
if (!array_key_exists($key, $configMap)) {
throw new InvalidArgumentException("Unbekannte Config: $key");
}
return $configMap[$key];
}
// SICHER: Funktions-Whitelist
function callFunctionSafe($funcName, $args) {
$allowed = ['strlen', 'strtoupper', 'strtolower', 'trim'];
if (!in_array($funcName, $allowed)) {
throw new InvalidArgumentException("Funktion nicht erlaubt: $funcName");
}
// Argumenttypen validieren
foreach ($args as $arg) {
if (!is_string($arg)) {
throw new InvalidArgumentException("Ungültiger Argumenttyp");
}
}
return $funcName(...$args);
}
?>
# SICHER: Dictionary-Zugriff mit Validierung
from typing import Any, Dict
class ConfigSafe:
def __init__(self):
self._data = {
'theme': 'light',
'language': 'en'
}
self._settable = {'theme', 'language'}
self._readonly = {'secret_key', 'debug'}
def get(self, key: str) -> Any:
if key in self._readonly:
raise PermissionError(f"Kann nicht zugreifen: {key}")
return self._data.get(key)
def set(self, key: str, value: Any) -> None:
if key not in self._settable:
raise PermissionError(f"Kann nicht setzen: {key}")
self._data[key] = value
# SICHER: Explizites Mapping
def update_config_safe(updates: Dict[str, Any]) -> None:
allowed_updates = {
'theme': lambda v: v in ('light', 'dark'),
'language': lambda v: v in ('en', 'de', 'fr'),
'page_size': lambda v: isinstance(v, int) and 1 <= v <= 100
}
for key, value in updates.items():
if key not in allowed_updates:
raise ValueError(f"Unbekannte Einstellung: {key}")
if not allowed_updates[key](value):
raise ValueError(f"Ungültiger Wert für {key}")
# Über explizite Methode aktualisieren
CONFIG[key] = value
# SICHER: Modul-Whitelist
ALLOWED_MODULES = {'json', 'datetime', 'math'}
def load_module_safe(module_name: str):
if module_name not in ALLOWED_MODULES:
raise ImportError(f"Modul nicht erlaubt: {module_name}")
return __import__(module_name)
# SICHER: getattr mit Whitelist verwenden
class SafeObject:
name = "test"
value = 42
_secret = "hidden"
PUBLIC_ATTRS = {'name', 'value'}
def get_attr(self, attr_name: str):
if attr_name not in self.PUBLIC_ATTRS:
raise AttributeError(f"Kann nicht zugreifen: {attr_name}")
return getattr(self, attr_name)
// SICHER: Map-basierter Zugriff
const config = new Map([
['theme', 'light'],
['language', 'en']
]);
const settableKeys = new Set(['theme', 'language']);
const readonlyKeys = new Set(['isAdmin', 'secretKey']);
function getConfig(key) {
if (readonlyKeys.has(key)) {
throw new Error(`Kann nicht zugreifen: ${key}`);
}
return config.get(key);
}
function setConfig(key, value) {
if (!settableKeys.has(key)) {
throw new Error(`Kann nicht setzen: ${key}`);
}
config.set(key, value);
}
// SICHER: Property-Zugriff mit Validierung
const safeObject = {
name: 'app',
version: '1.0',
allowedProps: ['name', 'version'],
get(prop) {
if (!this.allowedProps.includes(prop)) {
throw new Error(`Ungültige Property: ${prop}`);
}
return this[prop];
}
};
// SICHER: Kein eval - explizite Operationen verwenden
const operations = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => b !== 0 ? a / b : NaN
};
function calculate(operation, a, b) {
if (!operations.hasOwnProperty(operation)) {
throw new Error(`Unbekannte Operation: ${operation}`);
}
return operations[operation](a, b);
}
// SICHER: Schema-Validierung für Objekt-Updates
const Joi = require('joi');
const configSchema = Joi.object({
theme: Joi.string().valid('light', 'dark'),
language: Joi.string().valid('en', 'de', 'fr'),
pageSize: Joi.number().integer().min(1).max(100)
});
function updateConfigSafe(updates) {
const { error, value } = configSchema.validate(updates);
if (error) {
throw new Error(`Ungültige Config: ${error.message}`);
}
Object.assign(config, value);
}
Ausgenutzt in der Praxis
PHP-Anwendungen
Variable Variablen ausgenutzt um Authentifizierungs-Flags zu überschreiben.
Python Flask/Django
setattr()-Missbrauch ermöglichte Privilegien-Eskalation.
Node.js
eval() in Konfigurations-Handlern führte zu RCE.
Tools zum Testen/Ausnutzen
- Statische Analyse-Tools für eval()/exec()-Erkennung.
- Manuelles Testen mit Variablennamen-Manipulation.
- Fuzzing von Parameternamen und -werten.
CVE-Beispiele
- CVEs von dynamischen Evaluations-Schwachstellen in Webanwendungen.
- Python/PHP-CMS-Systeme mit Variablenmanipulations-Problemen.
Referenzen
- MITRE. "CWE-627: Dynamic Variable Evaluation." https://cwe.mitre.org/data/definitions/627.html
- PHP-Handbuch. Variable Variablen-Dokumentation.