Erste Version vom Implementierungshandbuch angelegt

blaerf 2025-11-15 17:25:21 +01:00
parent 8d31b172dc
commit 78165248f7

@ -0,0 +1,737 @@
# Technisches Implementierungshandbuch: PHP-basierte Active Directory-Verwaltung mit IIS auf Windows Server 2025
---
## Zusammenfassung
Dieses Dokument beschreibt die detaillierte, produktionsreife Implementierung einer PHP-Webanwendung auf einem Windows Server 2025 Datacenter, die über die Internet Information Services (IIS) bereitgestellt wird.
Die Anwendung erfüllt folgende hoch privilegierte Aufgaben:
- Authentifizierung von Administratoren über LDAP
- Abfrage von Servermetriken über SNMP
- Erstellung von Active-Directory-(AD)-Benutzern (einzeln und als CSV-Massenimport)
Die AD-Benutzererstellung wird durch die sichere Ausführung von PowerShell-Skripten aus PHP heraus realisiert. Die Skripte verarbeiten sowohl Einzelbenutzer als auch CSV-Massenimporte.
Der Schwerpunkt dieses Handbuchs liegt auf der Konfiguration einer robusten und sicheren Architektur nach dem **Least-Privilege-Prinzip**, um Risiken durch Web-basierten Zugriff auf AD und Serverdienste zu minimieren.
---
## Teil 1: Einrichtung der Basisinfrastruktur (IIS, PHP & FastCGI)
Dieser Abschnitt legt das Fundament für die Webanwendung durch die Installation und Konfiguration des Webservers und der PHP-Laufzeitumgebung auf einem sauberen Windows Server 2025.
### 1.1 Installation der IIS-Serverrolle
1. Öffnen des Server-Managers.
2. Auswahl von **Verwalten → Rollen und Features hinzufügen**.
3. Im Assistenten zur Seite **Serverrollen** navigieren.
4. **Webserver (IIS)** auswählen.
5. Im erscheinenden Pop-up auf **Features hinzufügen** klicken.
6. Zur Seite **Rollendienste** fortfahren.
7. Unter **Webserver → Anwendungsentwicklung** den Knoten erweitern.
8. Das Kontrollkästchen **CGI** aktivieren.
**Technische Erläuterung:**
Die Auswahl von **CGI** ist entscheidend. Diese Option installiert nicht nur klassisches CGI, sondern auch das moderne **FastCGI-Modul**, das für die performante und stabile Anbindung von PHP an IIS erforderlich ist. Ohne FastCGI kann IIS nicht mit `php-cgi.exe` kommunizieren.
9. Den Assistenten abschließen und die Installation abwarten.
---
### 1.2 Installation der PHP-Laufzeitumgebung
Die Installation von PHP erfolgt manuell, da der Web Platform Installer (WPI) nicht mehr unterstützt wird.
#### Voraussetzung: Visual C++ Redistributable
PHP unter Windows benötigt das **Microsoft Visual C++ 20152022 Redistributable (x64)**.
1. Neueste x64-Version von Microsoft herunterladen (`vc_redist.x64.exe`).
2. Installation ausführen und abschließen.
#### PHP-Download
1. Zur offiziellen Download-Seite für PHP unter Windows wechseln:
`https://windows.php.net/download/`
2. Die **x64 Non-Thread Safe (NTS)** Version der gewünschten PHP-Version (z.B. PHP 8.3) im **Zip-Format** auswählen.
**Technische Erläuterung:**
Die NTS-Version ist für den Betrieb mit IIS + FastCGI optimiert. FastCGI übernimmt das Prozess-Pooling, daher ist Thread-Safety in PHP selbst nicht erforderlich. Die Verwendung einer Thread-Safe-Version (TS) kann hier zu Performance- und Stabilitätsproblemen führen.
#### PHP-Installation
1. Ein Verzeichnis für PHP erstellen, z.B.:
`C:\PHP`
2. Das heruntergeladene ZIP-Archiv nach `C:\PHP` entpacken.
---
### 1.3 Konfiguration von IIS für PHP (Handler Mapping)
Nun muss IIS angewiesen werden, wie `.php`-Dateien behandelt werden. Dies erfolgt über eine **Handler-Zuordnung**.
1. IIS-Manager starten (`inetmgr`).
2. Im linken Bereich **Verbindungen** den Servernamen (oberste Ebene) auswählen.
3. Im mittleren Bereich auf **Handlerzuordnungen** doppelklicken.
4. Im rechten Bereich unter **Aktionen** auf **Modulzuordnung hinzufügen…** klicken.
5. Felder wie folgt ausfüllen:
- **Anforderungspfad:** `*.php`
- **Modul:** `FastCgiModule`
- **Ausführbare Datei:** `C:\PHP\php-cgi.exe` (Pfad ggf. anpassen)
- **Name:** z.B. `PHP via FastCGI`
6. Mit **OK** bestätigen.
7. Die Nachfrage, ob eine FastCGI-Anwendung angelegt werden soll, mit **Ja** bestätigen.
---
### 1.4 Konfiguration der „Default Web Site“
1. Im IIS-Manager im linken Bereich **Websites** erweitern.
2. Rechtsklick auf **Default Web Site** → **Verwalten der Website → Erweiterte Einstellungen…**
3. Die Eigenschaft **Physischer Pfad** suchen.
4. Den Standardpfad (z.B. `%SystemDrive%\inetpub\wwwroot`) ändern auf den `public`-Ordner des Projekts, z.B.:
`C:\Web\AdAdminPanel\public`
5. Mit **OK** bestätigen.
Ab jetzt leitet IIS Anfragen an `http://localhost/` in das Verzeichnis `C:\Web\AdAdminPanel\public`.
---
### 1.5 Validierung der Basisinstallation
1. Eine neue Textdatei `phpinfo.php` in
`C:\Web\AdAdminPanel\public` erstellen.
2. Folgenden Inhalt einfügen:
```php
<?php
phpinfo();
```
3. Im Browser `http://localhost/phpinfo.php` aufrufen.
Wenn alles korrekt ist, erscheint die PHP-Info-Seite mit allen Konfigurationsdetails.
**Wichtig:**
Den Wert bei **Loaded Configuration File (php.ini) Path** notieren. Dieser Pfad ist wichtig für die Konfiguration in **Teil 3**.
**Sicherheit:**
Die Datei `phpinfo.php` nach der Validierung wieder löschen.
---
## Teil 2: Konfiguration der Host-Dienste (SNMP & Active Directory)
Bevor die PHP-Anwendung konfiguriert wird, müssen die Zieldienste auf dem Server (SNMP) und in der Domäne (Active Directory) vorbereitet werden.
### 2.1 Aktivierung und Konfiguration des SNMP-Dienstes
#### Installation des SNMP-Dienstes
Unter Windows Server 2025 erfolgt die Installation bevorzugt über PowerShell:
```powershell
Install-WindowsFeature SNMP-Service,SNMP-WMI-Provider -IncludeManagementTools
```
#### Konfiguration des SNMP-Dienstes
1. `services.msc` öffnen.
2. Den Dienst **SNMP Service** suchen und **Eigenschaften** öffnen.
**Registerkarte „Agent“:**
- Optional **Kontakt** und **Standort** eintragen.
- Unter **Dienst** passende Kategorien aktivieren (z.B. *Physisch*, *Anwendungen*, *Internet*).
**Registerkarte „Sicherheit“ (kritischer Schritt):**
- Unter **Akzeptierte Communitynamen** → **Hinzufügen…**
- Rechte: **SCHREIBGESCHÜTZT (READ ONLY)**
- Community-Name, z.B. `public_ro` (nicht `public` verwenden).
- Unter **SNMP-Pakete von diesen Hosts annehmen** → **Hinzufügen…**
- `127.0.0.1` eintragen.
**Technische Erläuterung:**
Die Beschränkung auf `127.0.0.1` stellt sicher, dass nur lokale Prozesse (z.B. PHP) den SNMP-Dienst abfragen können. Anfragen anderer Hosts werden verworfen.
Den SNMP-Dienst nach der Konfiguration neu starten.
---
### 2.2 Vorbereitung von Active Directory (OU und Dienstkonto)
#### Organisationseinheit (OU) erstellen
1. **Active Directory-Benutzer und -Computer** (`dsa.msc`) öffnen.
2. Rechtsklick auf die Domäne → **Neu → Organisationseinheit**.
3. Namen vergeben, z.B. `WebAppUsers`.
Diese OU dient als isolierter Container für alle Benutzer, die über die PHP-Anwendung erstellt werden. Dort wird später die Rechte-Delegierung vorgenommen.
#### Dienstkonto (Service Account) erstellen
1. Einen neuen Standard-Domänenbenutzer erstellen, z.B.:
- Benutzername: `svc_iis_php_ad`
2. Keiner administrativen Gruppe (z.B. *Domänen-Admins*) hinzufügen.
3. Ein langes, komplexes Kennwort vergeben (25+ Zeichen).
4. Kontooptionen:
- **Kennwort läuft nie ab** aktivieren.
- **Benutzer kann Kennwort nicht ändern** aktivieren.
- **Benutzer muss Kennwort bei der nächsten Anmeldung ändern** deaktivieren.
Dieses Konto wird später als Identität des IIS-Anwendungspools verwendet.
---
## Teil 3: Erweiterte PHP-Konfiguration (LDAP und SNMP aktivieren)
Nun werden die PHP-Erweiterungen aktiviert, die für LDAP-Authentifizierung und SNMP-Abfragen benötigt werden.
### 3.1 Bearbeitung der `php.ini`
1. Zum PHP-Installationsverzeichnis wechseln, z.B. `C:\PHP`.
2. Falls keine `php.ini` existiert:
- `php.ini-production` nach `php.ini` kopieren.
3. `php.ini` mit einem Editor (z.B. Notepad++ als Administrator) öffnen.
### 3.2 Aktivierung der PHP-Erweiterungen
1. In der `php.ini` nach `extension_dir` suchen und sicherstellen, dass es auf das `ext`-Verzeichnis zeigt:
```ini
extension_dir = "ext"
```
2. Folgende Zeilen aktivieren (Semikolon am Zeilenanfang entfernen):
```ini
extension=ldap
extension=snmp
extension=openssl
```
- **ldap**: für Administrator-Authentifizierung via LDAP
- **snmp**: für SNMP-Abfragen (Servermetriken)
- **openssl**: u.a. für LDAPS (LDAP über SSL/TLS)
### 3.3 Behebung von Windows-spezifischen DLL-Abhängigkeiten
Erweiterungen wie `php_ldap.dll` benötigen zusätzliche DLLs (z.B. `libsasl.dll`, `libcrypto-*.dll`, `libssl-*.dll`), die sich im PHP-Stammverzeichnis befinden.
**Typische Symptome:**
- In `phpinfo()` fehlen die Sektionen `ldap` oder `snmp`.
- In der Ereignisanzeige oder in IIS-Logs erscheinen PHP-Startwarnungen.
**Lösung (Best Practice):**
1. Systemsteuerung → **System → Erweiterte Systemeinstellungen**.
2. **Umgebungsvariablen…** öffnen.
3. Unter **Systemvariablen** die Variable `Path` bearbeiten.
4. **Neu**: `C:\PHP` hinzufügen.
5. Alle Fenster mit **OK** schließen.
**Erforderlicher Schritt:**
- Vollständigen Neustart des Servers durchführen
**oder** zumindest die Dienste **W3SVC** und **WAS** neu starten, damit der IIS-Prozess `w3wp.exe` den aktualisierten `PATH` übernimmt.
### 3.4 Überprüfung der geladenen Module
- `phpinfo.php` erneut aufrufen.
- Mit `Strg+F` nach `ldap` und `snmp` suchen.
Sind separate Konfigurationsblöcke für `ldap` und `snmp` sichtbar, wurden die Module korrekt geladen.
#### Tabelle 1: Erforderliche PHP-Erweiterungen und Abhängigkeiten
| Erweiterung | `php.ini`-Eintrag | Wichtige DLLs (im PHP-Stammverzeichnis) | Zweck |
|------------|---------------------|--------------------------------------------------------|--------------------------------------------------------|
| LDAP | `extension=ldap` | `libsasl.dll`, `libcrypto-*.dll`, `libssl-*.dll` | Administrator-Authentifizierung (Login) |
| SNMP | `extension=snmp` | (i.d.R. keine zusätzlichen DLLs) | Abfrage von Serverinformationen (CPU, RAM, Uptime) |
| OpenSSL | `extension=openssl` | `libcrypto-*.dll`, `libssl-*.dll` | LDAPS/HTTPS-Funktionalität |
---
## Teil 4: Die PowerShell-Automatisierungsebene
Die AD-Operationen werden über PowerShell-Skripte ausgeführt, die als sichere, parametrisierte Schnittstelle zwischen PHP und Active Directory dienen.
**Pfad für Skripte:**
`C:\Web\AdAdminPanel\scripts\powershell\`
### 4.1 Skript für einzelne Benutzererstellung (`Create-ADUser.ps1`)
```powershell
# C:\Web\AdAdminPanel\scripts\powershell\Create-ADUser.ps1
param(
[Parameter(Mandatory=$true)][string]$Username,
[Parameter(Mandatory=$true)][string]$Password,
[Parameter(Mandatory=$true)][string]$GivenName,
[Parameter(Mandatory=$true)][string]$Surname,
[Parameter(Mandatory=$true)][string]$OUPath
)
try {
Import-Module ActiveDirectory -ErrorAction Stop
# Kritisch: Konvertierung des Klartext-Passworts in einen SecureString
$SecurePassword = ConvertTo-SecureString -String $Password -AsPlainText -Force
$userParams = @{
Name = "$GivenName $Surname"
GivenName = $GivenName
Surname = $Surname
SamAccountName = $Username
UserPrincipalName = "$Username@yourdomain.com" # "yourdomain.com" anpassen
AccountPassword = $SecurePassword
Path = $OUPath
Enabled = $true
ChangePasswordAtLogon = $true
}
New-ADUser @userParams
Write-Output "Erfolg: Benutzer $Username wurde in der OU $OUPath erstellt."
}
catch {
Write-Error "Fehler bei der Benutzererstellung für $Username: $($_.Exception.Message)"
exit 1
}
```
---
### 4.2 Skript für CSV-Massenimport (`Bulk-Create-ADUsers.ps1`)
```powershell
# C:\Web\AdAdminPanel\scripts\powershell\Bulk-Create-ADUsers.ps1
param(
[Parameter(Mandatory=$true)][string]$CsvPath,
[Parameter(Mandatory=$true)][string]$OUPath
)
$domainSuffix = "@yourdomain.com" # Anpassen
try {
Import-Module ActiveDirectory -ErrorAction Stop
# Erwartete Spalten: SamAccountName, GivenName, Surname, Password
$users = Import-Csv -Path $CsvPath
if ($null -eq $users) {
Write-Warning "Die CSV-Datei unter $CsvPath ist leer oder konnte nicht gelesen werden."
exit 0
}
foreach ($user in $users) {
try {
$SecurePassword = ConvertTo-SecureString -String $user.Password -AsPlainText -Force
$userParams = @{
Name = "$($user.GivenName) $($user.Surname)"
GivenName = $user.GivenName
Surname = $user.Surname
SamAccountName = $user.SamAccountName
UserPrincipalName = "$($user.SamAccountName)$domainSuffix"
AccountPassword = $SecurePassword
Path = $OUPath
Enabled = $true
ChangePasswordAtLogon = $true
}
New-ADUser @userParams
Write-Output "Erfolg (Bulk): Benutzer $($user.SamAccountName) wurde erstellt."
}
catch {
Write-Warning "Fehler bei Benutzer $($user.SamAccountName): $($_.Exception.Message)"
}
}
Write-Output "Massenimport abgeschlossen."
}
catch {
Write-Error "Schwerwiegender Fehler beim Massenimport: $($_.Exception.Message)"
exit 1
}
```
---
## Teil 5: Sicherheitsarchitektur (IIS, PHP und PowerShell)
Die größte Herausforderung ist die sichere Verbindung zwischen der IIS-Webanwendung (niedrige Vertrauensstellung) und Active Directory (hohe Vertrauensstellung).
### 5.1 Analyse des Sicherheitsproblems
`shell_exec()` in PHP wird im Sicherheitskontext des IIS-Anwendungspool-Benutzers ausgeführt. Standardmäßig ist dies ein virtuelles Konto wie `IIS APPPOOL\DefaultAppPool` mit sehr wenigen Rechten. Dieses Konto darf weder:
- das AD-PowerShell-Modul nutzen,
- noch AD-Objekte erstellen,
- noch sich an Domänencontrollern authentifizieren.
**Falsche Lösung:** Dem AppPool Domänen-Admin-Rechte geben → massive Sicherheitslücke.
**Korrekte Lösung:** Verwendung des dedizierten Dienstkontos `svc_iis_php_ad` (Least Privilege) und gezielte Rechte-Delegierung.
---
### 5.2 Konfiguration der IIS-Anwendungspool-Identität
1. IIS-Manager öffnen.
2. **Anwendungspools** auswählen.
3. Rechtsklick → **Anwendungspool hinzufügen…**
- Name: `PHP_AD_AppPool`
- .NET CLR-Version: **Kein verwalteter Code**
4. Auf die Website (z.B. **Default Web Site**) wechseln → **Basis-Einstellungen…** bzw. **Standardeinstellungen…** und den Anwendungspool auf `PHP_AD_AppPool` umstellen.
5. In der Liste der Anwendungspools:
- `PHP_AD_AppPool` markieren → **Erweiterte Einstellungen…**
- Unter **Prozessmodell → Identität****Benutzerdefiniertes Konto** auswählen.
- **Festlegen…** → Anmeldeinformationen für `IHREDOMÄNE\svc_iis_php_ad` eintragen.
Ab jetzt läuft der AppPool im Kontext des Dienstkontos.
---
### 5.3 Delegierung von AD-Berechtigungen
1. Auf einem Domänencontroller `dsa.msc` öffnen.
2. Rechtsklick auf OU **WebAppUsers** → **Objektverwaltung zuweisen…**
3. Im Assistenten:
- **Weiter****Hinzufügen…** → Konto `svc_iis_php_ad` auswählen.
- **Weiter****Eine benutzerdefinierte Aufgabe zum Zuweisen erstellen**.
- Objekttyp: **Nur die folgenden Objekttypen im Ordner****Benutzerobjekte** aktivieren.
- Berechtigungen:
- **Diese Objekte im Ordner erstellen** (ggf. auch löschen).
- **Alle Eigenschaften schreiben**
- **Kennwort zurücksetzen**
- **Kennwort ändern**
**Ergebnis:**
`svc_iis_php_ad` kann nur Benutzerobjekte innerhalb der OU `WebAppUsers` erstellen/ändern und sonst nichts.
---
### 5.4 PowerShell-Ausführungsrichtlinie
Standardmäßig gilt `Restricted`. Statt die Richtlinie global zu ändern, wird sie pro Aufruf übergangen mit:
```powershell
-ExecutionPolicy ByPass -NoProfile
```
Dies wird in den PHP-Skripten beim Aufruf von `powershell.exe` verwendet.
---
### 5.5 Dateisystemberechtigungen (NTFS)
Dem Dienstkonto `svc_iis_php_ad` werden nur die minimal erforderlichen NTFS-Rechte gewährt:
- `C:\PHP` → Lesen & Ausführen
- `C:\Web\AdAdminPanel\public` → Lesen
- `C:\Web\AdAdminPanel\scripts\powershell` → Lesen & Ausführen
- `C:\Web\AdAdminPanel\storage\uploads` → Ändern/Schreiben (für CSV-Uploads)
---
## Teil 6: Implementierung der PHP-Anwendungslogik
Die folgenden PHP-Dateien liegen im Web-Stammverzeichnis, z.B.:
`C:\Web\AdAdminPanel\public`
### 6.1 Modul 1: Administrator-Authentifizierung (LDAP-Login)
**Datei:** `login.php`
```php
<?php
// login.php
session_start();
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Domänencontroller und Domäne anpassen
$ldap_server = "ldap://dc01.yourdomain.com";
$domain = "@yourdomain.com";
// Anmeldung mit UPN
$ldap_user_dn = $_POST['username'] . $domain;
$ldap_pass = $_POST['password'];
$ldap_conn = ldap_connect($ldap_server);
if (!$ldap_conn) {
$error = "Fehler: Verbindung zum LDAP-Server fehlgeschlagen.";
} else {
ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap_conn, LDAP_OPT_REFERRALS, 0);
// @ unterdrückt Warnungen bei fehlerhaftem Login
if (@ldap_bind($ldap_conn, $ldap_user_dn, $ldap_pass)) {
$_SESSION['admin_user'] = $ldap_user_dn;
header("Location: dashboard.php");
exit;
} else {
$error = "LDAP-Bind fehlgeschlagen: Ungültiger Benutzername oder Passwort.";
}
ldap_close($ldap_conn);
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Admin Login</title>
</head>
<body>
<h2>Admin Login</h2>
<?php if ($error): ?>
<p style="color:red;"><?php echo htmlspecialchars($error); ?></p>
<?php endif; ?>
<form method="post" action="login.php">
Benutzername: <input type="text" name="username"><br>
Passwort: <input type="password" name="password"><br>
<input type="submit" value="Anmelden">
</form>
</body>
</html>
```
---
### 6.2 Modul 2: Server-Monitoring (SNMP)
**Datei:** `dashboard.php`
```php
<?php
// dashboard.php
session_start();
if (!isset($_SESSION['admin_user'])) {
header("Location: login.php");
exit;
}
// SNMP-Konfiguration
$community = 'public_ro';
$host = '127.0.0.1';
snmp_set_quick_print(true);
snmp_set_oid_output_format(SNMP_OID_OUTPUT_NUMERIC);
// CPU-Last (erster Kern, Beispiel)
$cpu_oid = '.1.3.6.1.2.1.25.3.3.1.2.1';
$cpu_load = @snmpget($host, $community, $cpu_oid, 1000000, 2);
// System Uptime
$uptime_oid = '.1.3.6.1.2.1.25.1.1.0';
$uptime_raw = @snmpget($host, $community, $uptime_oid);
$uptime = $uptime_raw ? ($uptime_raw / (100 * 60 * 60 * 24)) . " Tage" : "Fehler";
// RAM (statisches Beispiel mit Index 5)
$ram_total_oid = '.1.3.6.1.2.1.25.2.3.1.5.5';
$ram_used_oid = '.1.3.6.1.2.1.25.2.3.1.6.5';
$ram_alloc_units_oid = '.1.3.6.1.2.1.25.2.3.1.4.5';
$ram_total_raw = @snmpget($host, $community, $ram_total_oid);
$ram_used_raw = @snmpget($host, $community, $ram_used_oid);
$ram_units = @snmpget($host, $community, $ram_alloc_units_oid);
if ($ram_total_raw && $ram_used_raw && $ram_units) {
$ram_total_gb = ($ram_total_raw * $ram_units) / (1024 ** 3);
$ram_used_gb = ($ram_used_raw * $ram_units) / (1024 ** 3);
$ram_percent = ($ram_used_gb / $ram_total_gb) * 100;
}
?>
<h2>Admin Dashboard</h2>
<p>Angemeldet als: <?php echo htmlspecialchars($_SESSION['admin_user']); ?></p>
<h3>Server-Status (SNMP)</h3>
<p>CPU-Last (Kern 1): <?php echo $cpu_load ?: "Fehler bei Abfrage"; ?>%</p>
<p>System Uptime: <?php echo $uptime; ?></p>
<p>RAM-Auslastung:
<?php echo isset($ram_percent) ? number_format($ram_percent, 2) . "%" : "Fehler bei Abfrage"; ?>
</p>
<hr>
<p><a href="create_single_user.php">Einzelnen Benutzer erstellen</a></p>
<p><a href="bulk_create_users.php">Benutzer per CSV importieren</a></p>
```
---
### 6.3 Modul 3: Einzelne Benutzererstellung
**Datei:** `create_single_user.php`
```php
<?php
// create_single_user.php
session_start();
if (!isset($_SESSION['admin_user'])) {
header("Location: login.php");
exit;
}
$output = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$ps_script = 'C:\\Web\\AdAdminPanel\\scripts\\powershell\\Create-ADUser.ps1';
if (empty($_POST['username']) || empty($_POST['password'])) {
$output = "Fehler: Benutzername und Passwort sind erforderlich.";
} else {
// Kritisch: Maskierung aller Shell-Argumente (Schutz vor Command Injection)
$username = escapeshellarg($_POST['username']);
$password = escapeshellarg($_POST['password']);
$givenname = escapeshellarg($_POST['givenname']);
$surname = escapeshellarg($_POST['surname']);
$oupath = escapeshellarg("OU=WebAppUsers,DC=yourdomain,DC=com");
$command = "powershell.exe -ExecutionPolicy ByPass -NoProfile -File $ps_script " .
"-Username $username -Password $password -GivenName $givenname " .
"-Surname $surname -OUPath $oupath";
// STDERR nach STDOUT umleiten (2>&1)
$output = shell_exec($command . " 2>&1");
}
}
?>
<h2>Neuen AD-Benutzer erstellen</h2>
<form method="post" action="create_single_user.php">
Benutzername (SamAccountName): <input type="text" name="username"><br>
Temporäres Passwort: <input type="text" name="password"><br>
Vorname: <input type="text" name="givenname"><br>
Nachname: <input type="text" name="surname"><br>
<input type="submit" value="Benutzer erstellen">
</form>
<?php if ($output): ?>
<h3>Ausgabe:</h3>
<pre><?php echo htmlspecialchars($output); ?></pre>
<?php endif; ?>
```
---
### 6.4 Modul 4: CSV-Massenimport
**Datei:** `bulk_create_users.php`
```php
<?php
// bulk_create_users.php
session_start();
if (!isset($_SESSION['admin_user'])) {
header("Location: login.php");
exit;
}
$output = '';
if (isset($_FILES['user_csv'])) {
$upload_dir = 'C:\\Web\\AdAdminPanel\\storage\\uploads\\';
$temp_csv_path = $upload_dir . uniqid('users_') . '.csv';
if (move_uploaded_file($_FILES['user_csv']['tmp_name'], $temp_csv_path)) {
$ps_script = 'C:\\Web\\AdAdminPanel\\scripts\\powershell\\Bulk-Create-ADUsers.ps1';
$csv_path_arg = escapeshellarg($temp_csv_path);
$oupath_arg = escapeshellarg("OU=WebAppUsers,DC=yourdomain,DC=com");
$command = "powershell.exe -ExecutionPolicy ByPass -NoProfile -File $ps_script " .
"-CsvPath $csv_path_arg -OUPath $oupath_arg";
$output = shell_exec($command . " 2>&1");
// Temporäre CSV mit Klartext-Passwörtern löschen
unlink($temp_csv_path);
} else {
$output = "Fehler beim Hochladen der Datei.";
}
}
?>
<h2>AD-Benutzer per CSV-Massenimport erstellen</h2>
<p>Die CSV-Datei muss die Spaltenüberschriften <code>SamAccountName</code>, <code>GivenName</code>, <code>Surname</code> und <code>Password</code> enthalten.</p>
<form method="post" action="bulk_create_users.php" enctype="multipart/form-data">
CSV-Datei auswählen: <input type="file" name="user_csv" accept=".csv"><br>
<input type="submit" value="Import starten">
</form>
<?php if ($output): ?>
<h3>Import-Ergebnis:</h3>
<pre><?php echo htmlspecialchars($output); ?></pre>
<?php endif; ?>
```
---
## Teil 7: Abschließendes Sicherheits-Audit und Risikobewertung
Die Lösung ist mächtig, aber sensibel, da sie eine Brücke zwischen Web und Active Directory bildet. Die folgenden Risiken und Gegenmaßnahmen sind zentral:
### Risiko: Command Injection
**Beschreibung:**
Ein Angreifer könnte versuchen, über Formulare Befehle einzuschleusen, z.B.:
`username; Remove-ADUser -Identity 'Administrator'`
**Gegenmaßnahme:**
- Konsequente Verwendung von **`escapeshellarg()`** für alle Parameter, die an `powershell.exe` übergeben werden.
- Dadurch werden Eingaben als einzelne, harmlose Zeichenkette behandelt und nicht als eigenständiger Befehl interpretiert.
---
### Risiko: Eskalation von Berechtigungen
**Beschreibung:**
Eine Schwachstelle in der PHP-Anwendung könnte genutzt werden, um die Rechte des Webserverkontos zu missbrauchen.
**Gegenmaßnahme:**
- AppPool läuft als **`svc_iis_php_ad`** mit minimal notwendigen Rechten.
- AD-Delegierung beschränkt das Konto auf das Erstellen/Ändern von Benutzern in der OU `WebAppUsers`.
- Keine Mitgliedschaft in administrativen Gruppen, kein Zugriff auf andere OUs oder Systeme.
---
### Risiko: Klartext-Passwörter
**Beschreibung:**
- Passwörter liegen im Klartext in Formularen und CSV-Dateien vor.
- Übergabe an PowerShell erfolgt im Klartext.
- Temporäre CSV-Dateien enthalten Klartext-Passwörter auf der Festplatte.
**Gegenmaßnahmen:**
- **HTTPS erzwingen:** Die gesamte Weboberfläche muss über TLS gesichert werden.
- **Schnelle Konvertierung**: PowerShell wandelt Klartext-Passwörter sofort in `SecureString` um.
- **Löschung der CSV-Dateien:** Temporäre CSV-Dateien werden nach der Verarbeitung gelöscht (`unlink()`).
- **Passwortwechsel erzwingen:** `ChangePasswordAtLogon = $true` zwingt Benutzer zur Passwortänderung bei der ersten Anmeldung.
---
### Risiko: Fehlende Fehlerbehandlung
**Beschreibung:**
- PowerShell-Fehler (z.B. Kennwort-Komplexität) landen auf STDERR, das von `shell_exec()` standardmäßig ignoriert wird.
**Gegenmaßnahme:**
- Anhängen von `2>&1` an alle Befehle im `shell_exec()`-Kontext, damit Fehlermeldungen im `$output`-String landen und im Webinterface angezeigt werden können.
---
**Ende des Dokuments**