Datei-Uploads in PHP verarbeiten: Größe erhöhen und Uploads validieren
Datei-Uploads zu verarbeiten ist eine häufige Anforderung in Webanwendungen, bringt aber eigene Herausforderungen mit sich. Dieser Leitfaden zeigt, wie Sie Datei-Uploads in PHP 8.3 verarbeiten, unter anderem wie Sie die maximale Upload-Größe prüfen und erhöhen, PHP-Konfigurationen anpassen, Dateitypen einschränken und Uploads validieren, um Sicherheit und Effizienz Ihrer Anwendungen zu steigern.
PHP-Version und Konfigurationsstandards
Dieser Leitfaden bezieht sich auf PHP 8.3. In PHP 8.3 umfassen die Standardeinstellungen für Datei-Uploads typischerweise:
upload_max_filesizeist auf 2M gesetzt.post_max_sizeliegt meist bei etwa 8M (der Wert kann je nach Konfiguration abweichen).max_file_uploadsist auf 20 gesetzt.
Prüfen Sie bei Bedarf Ihre aktuellen Einstellungen, bevor Sie Änderungen vornehmen.
So prüfen Sie die maximale Datei-Upload-Größe in PHP
In PHP 8.3 beträgt der Standardwert von upload_max_filesize 2 MB und max_file_uploads 20. Bevor Sie
Änderungen vornehmen, sollten Sie Ihre aktuellen Upload-Limits kennen. PHP steuert die Größe von
Datei-Uploads über zwei Konfigurationsdirektiven: upload_max_filesize und post_max_size. Sie können diese Werte
prüfen, indem Sie ein einfaches PHP-Skript erstellen:
<?php
try {
echo 'upload_max_filesize: ' . ini_get('upload_max_filesize') . '<br>';
echo 'post_max_size: ' . ini_get('post_max_size') . '<br>';
echo 'max_file_uploads: ' . ini_get('max_file_uploads') . '<br>';
echo 'memory_limit: ' . ini_get('memory_limit') . '<br>';
} catch (Exception $e) {
error_log('Error checking PHP configuration: ' . $e->getMessage());
echo 'Error checking configuration settings.';
}
?>
Speichern Sie dieses Skript als check_upload_size.php und führen Sie es auf Ihrem Server aus. Es zeigt die
aktuelle maximale Datei-Upload-Größe und die maximal zulässige POST-Größe an.
Alternativ können Sie die Funktion phpinfo() verwenden, um einen vollständigen Überblick über
Ihre PHP-Konfiguration zu erhalten:
<?php
phpinfo();
?>
Wenn Sie dieses Skript im Browser aufrufen, erhalten Sie detaillierte Informationen zu Ihrer PHP-Konfiguration.
So erhöhen Sie die Datei-Upload-Größe in PHP
Wenn Sie größere Datei-Uploads zulassen möchten, müssen Sie in Ihrer PHP-Konfiguration die
Direktiven upload_max_filesize und post_max_size anpassen. So gehen Sie dabei vor:
.htaccess verwenden (für Apache 2.4+ mit mod_php)
Fügen Sie Ihrer Datei .htaccess die folgenden Zeilen hinzu:
<IfModule mod_php.c>
php_value upload_max_filesize 20M
php_value post_max_size 25M
php_value memory_limit 256M
php_value max_execution_time 300
</IfModule>
Damit wird die maximale Upload-Dateigröße auf 20 Megabyte und die maximale POST-Größe auf 25 Megabyte gesetzt. Passen Sie die Werte an Ihre Anforderungen an.
PHP-FPM-Konfiguration verwenden
Wenn Sie PHP-FPM verwenden, fügen Sie diese Einstellungen in Ihre Pool-Konfigurationsdatei ein
(üblicherweise in /etc/php/8.3/fpm/pool.d/www.conf):
php_admin_value[upload_max_filesize] = 20M
php_admin_value[post_max_size] = 25M
php_admin_value[memory_limit] = 256M
So ändern Sie unter Ubuntu die maximale Datei-Upload-Größe in php.ini
Wenn Sie Zugriff auf die Datei php.ini Ihres Servers haben, können Sie die Änderungen dort
vornehmen. So gehen Sie auf einem Ubuntu-System vor:
-
Suchen Sie Ihre Datei
php.ini. Bei PHP 8.3 liegt sie üblicherweise unter/etc/php/8.3/apache2/php.inioder/etc/php/8.3/fpm/php.ini. -
Öffnen Sie die Datei mit Root-Rechten in einem Texteditor:
sudo nano /etc/php/8.3/apache2/php.ini -
Suchen und ändern Sie diese Direktiven:
upload_max_filesize = 20M post_max_size = 25M memory_limit = 256M max_execution_time = 300 max_file_uploads = 50 -
Speichern Sie die Datei und beenden Sie den Editor.
-
Starten Sie Ihren Webserver und PHP-FPM neu (falls zutreffend):
sudo systemctl restart apache2 sudo systemctl restart php8.3-fpm
Datei-Upload-Funktionalität in PHP implementieren
Nachdem die Konfiguration steht, können Sie die Datei-Upload-Funktionalität in Ihrer Anwendung implementieren.
HTML-Formular
Erstellen Sie zunächst ein HTML-Formular, über das Nutzer Dateien hochladen können:
<form action="upload.php" method="post" enctype="multipart/form-data">
<label for="file">Choose file to upload:</label>
<input type="file" name="file" id="file" required />
<input type="submit" value="Upload" />
</form>
Achten Sie darauf, enctype="multipart/form-data" im Form-Tag zu setzen, um Datei-Uploads zu verarbeiten.
PHP-Upload-Skript
Hier ist eine Implementierung von upload.php mit den Funktionen von PHP 8.3. Richten Sie
/var/lib/php-upload-demo außerhalb des Webroots ein, wobei das Verzeichnis dem PHP-Worker gehören sollte, und
geben Sie es nicht über den Webserver frei:
<?php
declare(strict_types=1);
class FileUploadHandler {
private string $uploadDirectory;
private array $allowedTypes;
private array $allowedExtensions;
private int $maxFileSize;
private array $uploadedFiles = [];
public function __construct(
string $uploadDirectory = '/var/lib/php-upload-demo/',
array $allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'],
array $allowedExtensions = ['jpg', 'jpeg', 'png', 'pdf'],
int $maxFileSize = 5242880 // 5MB
) {
$this->uploadDirectory = rtrim($uploadDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$this->allowedTypes = $allowedTypes;
$this->allowedExtensions = $allowedExtensions;
$this->maxFileSize = $maxFileSize;
$this->initializeUploadDirectory();
}
private function initializeUploadDirectory(): void {
if (!is_dir($this->uploadDirectory)) {
if (!mkdir($this->uploadDirectory, 0700, true)) {
throw new RuntimeException('Failed to create upload directory');
}
}
}
public function handleUpload(): array {
try {
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
throw new RuntimeException('Invalid request method');
}
if (!isset($_FILES['file'])) {
throw new RuntimeException('No file uploaded');
}
$file = $_FILES['file'];
$this->validateUpload($file);
$fileInfo = $this->processUpload($file);
$this->uploadedFiles[] = $fileInfo;
return [
'success' => true,
'message' => 'File uploaded successfully',
'file' => $fileInfo
];
} catch (Exception $e) {
error_log('Upload error: ' . $e->getMessage());
return [
'success' => false,
'message' => 'The file could not be uploaded. Check its type and size.'
];
}
}
private function validateUpload(array $file): void {
if (!isset($file['error'], $file['size'], $file['name'], $file['tmp_name']) ||
!is_int($file['error']) || !is_int($file['size']) ||
!is_string($file['name']) || !is_string($file['tmp_name'])) {
throw new RuntimeException('Invalid upload fields');
}
if ($file['error'] !== UPLOAD_ERR_OK) {
throw new RuntimeException($this->getUploadErrorMessage($file['error']));
}
if ($file['size'] > $this->maxFileSize) {
throw new RuntimeException('File is too large');
}
$fileType = mime_content_type($file['tmp_name']);
if (!in_array($fileType, $this->allowedTypes, true)) {
throw new RuntimeException('Invalid file type');
}
$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($extension, $this->allowedExtensions, true)) {
throw new RuntimeException('Invalid file extension');
}
if (!is_uploaded_file($file['tmp_name'])) {
throw new RuntimeException('Invalid upload attempt');
}
}
private function processUpload(array $file): array {
$hash = hash_file('sha256', $file['tmp_name']);
$extensions = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'application/pdf' => 'pdf'];
$extension = $extensions[mime_content_type($file['tmp_name'])] ?? null;
if ($extension === null) {
throw new RuntimeException('No storage extension configured for this type');
}
$newFilename = bin2hex(random_bytes(16)) . '.' . $extension;
$destination = $this->uploadDirectory . $newFilename;
if (!move_uploaded_file($file['tmp_name'], $destination)) {
throw new RuntimeException('Failed to move uploaded file');
}
chmod($destination, 0600);
return [
'name' => $file['name'],
'type' => mime_content_type($destination),
'size' => filesize($destination),
'hash' => $hash,
'filename' => $newFilename
];
}
private function getUploadErrorMessage(int $error): string {
return match($error) {
UPLOAD_ERR_INI_SIZE => 'File exceeds upload_max_filesize',
UPLOAD_ERR_FORM_SIZE => 'File exceeds MAX_FILE_SIZE',
UPLOAD_ERR_PARTIAL => 'File was only partially uploaded',
UPLOAD_ERR_NO_FILE => 'No file was uploaded',
UPLOAD_ERR_NO_TMP_DIR => 'Missing temporary folder',
UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk',
UPLOAD_ERR_EXTENSION => 'File upload stopped by extension',
default => 'Unknown upload error'
};
}
}
// Usage
header('Content-Type: application/json');
try {
$handler = new FileUploadHandler();
echo json_encode($handler->handleUpload(), JSON_THROW_ON_ERROR);
} catch (Throwable $e) {
error_log('Upload handler failed: ' . get_class($e));
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'The upload service is unavailable.']);
}
Diese Implementierung umfasst:
- Strikte Typprüfung
- Umfassende Fehlerbehandlung mit try-catch
- Einen SHA-256-Hash für spätere Integritätsvergleiche
- Dateityperkennung mithilfe der Fileinfo-Erweiterung, womit jedoch nicht belegt ist, dass der Inhalt harmlos ist
- Privater Speicherort außerhalb des Webroots mit restriktiven Berechtigungen
- Schutz vor verbreiteten Upload-Schwachstellen
Bewährte Verfahren für sichere Datei-Uploads
Verzeichnissicherheit
-
Setzen Sie passende Berechtigungen:
chmod 700 /var/lib/php-upload-demo/ find /var/lib/php-upload-demo/ -type f -exec chmod 600 {} \; -
Wenn Sie
open_basedirverwenden, nehmen Sie die Verzeichnisse für Anwendung, Speicher und temporäre Dateien auf, die Ihr Deployment benötigt. Zum Beispiel:open_basedir = /var/www/app/:/var/lib/php-upload-demo/:/tmp/ -
Organisieren Sie Uploads in zufällig benannten Unterverzeichnissen, um Dateien zu isolieren und die Angriffsfläche für Directory-Traversal-Angriffe zu verringern.
-
Implementieren Sie eine Ratenbegrenzung:
<?php session_start(); if (!isset($_SESSION['upload_count'])) { $_SESSION['upload_count'] = 0; $_SESSION['upload_time'] = time(); } if (time() - $_SESSION['upload_time'] > 3600) { $_SESSION['upload_count'] = 0; $_SESSION['upload_time'] = time(); } if ($_SESSION['upload_count'] >= 10) { throw new RuntimeException('Upload limit exceeded'); } $_SESSION['upload_count']++;
Virenprüfung
Senden Sie Dateiinhalte mit INSTREAM an ClamAV. So bleiben die oben genannten privaten
Verzeichnisse (0700) und Dateien (0600) unangetastet, auch wenn der Daemon unter
einem anderen Konto läuft:
<?php
function scanFile(string $filepath): bool {
$path = realpath($filepath);
if ($path === false || !is_file($path)) {
return false;
}
$file = @fopen($path, 'rb');
if ($file === false) {
return false;
}
$socket = @stream_socket_client('unix:///var/run/clamav/clamd.ctl', $errno, $error, 5);
if ($socket === false) {
fclose($file);
return false;
}
stream_set_timeout($socket, 30);
$writeAll = function (string $bytes) use ($socket): bool {
while ($bytes !== '') {
$written = @fwrite($socket, $bytes);
if ($written === false || $written === 0) {
return false;
}
$bytes = substr($bytes, $written);
}
return true;
};
try {
if (!$writeAll("zINSTREAM\0")) {
return false;
}
while (!feof($file)) {
$chunk = @fread($file, 8192);
if ($chunk === false || ($chunk === '' && !feof($file))) {
return false;
}
if ($chunk !== '' && !$writeAll(pack('N', strlen($chunk)) . $chunk)) {
return false;
}
}
if (!$writeAll(pack('N', 0))) {
return false;
}
$result = '';
while (strlen($result) < 4096) {
$byte = @fread($socket, 1);
if ($byte === false || $byte === '') {
return false;
}
if ($byte === "\0") {
return $result === 'stream: OK';
}
$result .= $byte;
}
return false;
} finally {
fclose($file);
fclose($socket);
}
}
?>
ClamAV muss laufen, und PHP benötigt die Berechtigung, sich mit dessen vertrauenswürdigem lokalem
Socket zu verbinden. Der Daemon benötigt keinen Zugriff auf das Upload-Verzeichnis. Konfigurieren
Sie StreamMaxLength sowie die Scan-Limits der Engine passend zu Ihren zulässigen Upload-Größen; siehe
dazu die ClamD-Protokolldokumentation.
Behandeln Sie ein Ergebnis false als Ablehnung, einschließlich nicht verfügbarer Scanner und
unvollständiger Antworten. Rufen Sie den Scanner auf, bevor Sie eine hochgeladene Datei freigeben,
und verändern Sie sie zwischen Prüfung und Freigabe nicht mehr.
Integration moderner Frameworks
Für eine robustere Verarbeitung sollten Sie moderne PHP-Frameworks in Betracht ziehen. Eine Laravel-Controller-Methode könnte zum Beispiel so aussehen:
<?php
// Laravel example
use Illuminate\Http\Request;
class UploadController extends \Illuminate\Routing\Controller {
public function upload(Request $request) {
$request->validate([
'file' => 'required|file|mimes:pdf,jpg,png|max:5120'
]);
$path = $request->file('file')->store('uploads');
return response()->json(['success' => true, 'path' => $path]);
}
}
Fortgeschrittene Upload-Techniken
Über die Grundlagen hinaus können Sie das Upload-Erlebnis weiter verbessern und anspruchsvollere Szenarien bewältigen:
-
AJAX-Uploads verarbeiten: Verwenden Sie XMLHttpRequest, um Dateien asynchron hochzuladen und den Upload-Fortschritt zu verfolgen. Fetch stellt keine entsprechenden Ereignisse für den Upload-Fortschritt bereit. Zum Beispiel:
<script> const fileInput = document.getElementById('file') fileInput.addEventListener('change', function () { const file = fileInput.files[0] const xhr = new XMLHttpRequest() xhr.upload.addEventListener('progress', function (e) { if (e.lengthComputable && e.total > 0) { const percent = (e.loaded / e.total) * 100 console.log('Upload progress: ' + percent.toFixed(2) + '%') } }) xhr.open('POST', 'upload.php') const formData = new FormData() formData.append('file', file) xhr.send(formData) }) </script> -
Chunked Uploads für große Dateien: Bei sehr großen Dateien sollten Sie die Datei in kleinere Chunks aufteilen und diese nacheinander hochladen. Dieses Verfahren kann die Zuverlässigkeit erhöhen und fortsetzbare Uploads unterstützen.
-
Weitere Dateiformate unterstützen: Erweitern Sie Ihre Validierungslogik, um andere Formate wie WebP-Bilder oder ZIP-Archive zu unterstützen. Validieren Sie immer strikt, damit der Inhalt der Datei zu ihrer Dateiendung passt.
Fazit
Mit diesen Techniken verarbeiten Sie Datei-Uploads in PHP sicher und effizient. Von höheren Upload-Größenlimits über robuste Validierung bis hin zu fortgeschrittenen Verfahren wie AJAX-Uploads und Chunked Transfers verbessern diese Praktiken sowohl die Sicherheit als auch die Nutzererfahrung. Für eine integrierte Lösung mit modernen Werkzeugen für Datei-Uploads können Sie den Service für Datei-Uploads von Transloadit erkunden.
