WebsiteBaker Support (2.13.x) > General Help & Support

How to protect images on a "Logged In Only" page

<< < (2/3) > >>

sternchen8875:
(english)
Regarding the linked script:

In the form presented there, it serves to check permissions for a protected area that requires a user login. After a successful check via the session, the file linked in the request is delivered to the browser. If the user is not logged in, a 403 error is sent to the browser.

However, the script also has a few shortcomings:

    Biggest Flaw: Directory Traversal
    The method used to prevent "Directory Traversal" is vulnerable and easily bypassed. With simple techniques, it's possible to access files outside of the intended directory—potentially across the entire server.

    Lack of Granular Permissions
    The script does NOT check which user is logged in, only that a successful login has occurred. This means if you have different protected areas for different user groups, you won't get the security you're expecting. However, if there's only one protected area "for all" registered users, this would be acceptable.

    Poor Error Handling
    The error messages lack detail. An "Oups" is not very meaningful for the site administrator. For the visitor, a proper 404 ("Not Found") error would certainly be a better message.

    Performance
    The script uses file_get_contents(). This function reads the entire target file into PHP's memory. This can already cause issues with a single large image file and can severely impact server performance when used for a protected gallery with many concurrent requests.


[german)
Zum verlinkten Skript:

In der dort vorgestellten Form dient es zur Prüfung von Berechtigungen in einem geschützten Bereich, der einen User-Login erfordert. Nach erfolgreicher Prüfung über die Session wird die im Aufruf verlinkte Datei dem Browser zur Verfügung gestellt. Ist der User nicht eingeloggt, wird ein 403-Fehler an den Browser gesendet.

Ein paar Mankos hat das Skript aber auch:

    Größter Schwachpunkt: Directory Traversal
    Die Methode zur Verhinderung von "Directory Traversal" ist anfällig und leicht auszutricksen. Mit einfachen Mitteln ist es möglich, auf Dateien außerhalb des vorgesehenen Verzeichnisses zuzugreifen – potenziell auf dem gesamten Server.

    Fehlende Berechtigungsstufen
    Das Skript prüft NICHT, welcher User eingeloggt ist, sondern nur, ob ein erfolgreicher Login stattgefunden hat. Wer also unterschiedliche, geschützte Bereiche für verschiedene Benutzergruppen nutzt, erhält hier nicht den Schutz, den er sich verspricht. Gibt es aber nur einen geschützten Bereich "für alle" registrierten User, wäre das okay.

    Mangelhaftes Error-Handling
    Die Fehlerausgaben sind nicht detailliert. Ein "Oups" ist für den Verwalter der Seite wenig aussagekräftig. Und auch für den Besucher wäre ein 404-Fehler ("Nicht gefunden") sicherlich eine bessere Aussage.

    Performance
    Das Skript verwendet file_get_contents(). Diese Funktion liest die komplette Zieldatei in den Arbeitsspeicher von PHP. Das kann bereits bei einer einzelnen großen Bilddatei zu Problemen führen und bei einer geschützten Galerie mit vielen Zugriffen die Serverleistung stark beeinträchtigen.


Hier eine verbesserte Version, ausgelegt für ein mit .htaccess geschütztes Verzeichnis /media/private. Geprüft wird nur eine korrekte Anmeldung, ausreichend für eine einzelnen geschützten Bereich

Example (The script does NOT check which user is logged in, only that a successful login has occurred)

(english)
Explanation:

    Location of secure_file.php: I have placed this file in the /media directory. If you choose a different location, remember to adjust the path to the config.php file accordingly.

    This solution requires a /private/ folder to be created as a subfolder within /media.

    Protect this 'private' folder with an .htaccess file (code provided below). Test your setup by trying to access a file in this folder directly in your browser; the expected response is a "403 - Forbidden" error.

    Currently, only the image formats *.gif, *.png, *.jpeg, and *.webp are permitted. If you need to allow other file types, you must add them to the script's whitelist.

    If something doesn't work, enable the debug mode in the script (change false to true) and then access the link to the image directly in your browser to see the error message.

Usage / Implementation:

In CKEditor - source code view:

--- Code: ---<img alt="" class="img-responsive" height="473" src="http://wb1620/media/secure_file.php?file=tiger1.webp" style="" width="760">
--- End code ---

In a gallery module, for example:
$image = WB_URL.'/media/secure_file.php?file='.$aGalleryItem['path'].$file;

Important Note: Due to the .htaccess protection in the /media/private folder, the thumbnail preview function for files in ckeditor within this protected folder will not work in some file browsers (like the one in CKEditor). Since every user can have a different folder structure, a universal fix for this is not practical. This functionality would only be possible if this technique were integrated directly into the CMS core.

A temporary workaround is to either rename the .htaccess file in the private folder or to comment out the deny directive within it while you are working.

(german)
Erklärung:
- Dateiablage der secure_file.php: bei mir im media-Verzeichnis - bei Änderung Pfad zur config.php beachten
- erfordert einen Ordner /private/ als Unterordner von /media
- diesen Ordner 'private' mit einer .htaccess schützen (Code siehe unten) - prüfe mit Direktaufrufen einer Datei im Browser, erwartete Antwort ein 403 - Forbidden
- aktuell sind nur Grafikformate *.gif, *.png, *.jpeg und *.webp erlaubt. Bei weiteren Dateitypen bitte ergänzen
- sollte etwas nicht funktionieren, den Debug-Mode in der Datei einschalte (true statt false), Link zum Bild direkt im Browser aufrufen


Aufruf bzw Anwendung
im CKEditor - Quelltext

--- Code: ---<img alt="" class="img-responsive" height="473" src="http://wb1620/media/secure_file.php?file=tiger1.webp" style="" width="760">
--- End code ---

in einer Galerie z.b. so
$image = WB_URL.'/media/secure_file.php?file='.$aGalleryItem['path'].$file;

Hinweis: durch den Schutz mit der .htaccess im /media/private-Ordner funktioniert aktuell die Vorschaufunktion im Ckeditor in diesem geschützten Ordner nicht. Da nun jeder eine andere Ordnerstruktur haben kann oder haben wird, hilft da ein Fix auch nicht. Das ginge nur, wenn man diese Technik gleich ins CMS integriert

Abhilfe würde die temporäre Umbenennung der .htaccess im private-Ordner bringen oder ein Auskommentieren der deny Anweisung


Datei: secure_file.php


--- Quote ---<?php
// =========== DEBUGGING-SCHALTER ===========
// debug mode true or false (show's debug message in direct call
$debug = false;
// ------------------------------------------

// load config.php
require(__DIR__ . '/../config.php');

// build frontend object
if (!isset($wb) || (isset($wb) && !($wb instanceof \frontend))) {
    $wb = new \frontend();
}

// set error report method
if ($debug) {
    error_reporting(E_ALL);
    ini_set('display_errors', 1);
}

// is logged in or not
if ($wb->is_authenticated()) {

    // read path from link
    if (empty($_GET['file'])) {
        http_response_code(400); // Bad Request
        die('Error: No file specified.');
    }
    $requestedFile = urldecode($_GET['file']);
   
    $prefix = 'media/private/';
    if (strpos($requestedFile, $prefix) === 0) {
        $requestedFile = substr($requestedFile, strlen($prefix));
    }
   
    $fileName = basename($requestedFile);
    // build the correct path
    $filePath = WB_PATH . '/media/private/' . $fileName;
    // security check - file must be inside of protected folder
    $realBasePath = realpath(WB_PATH . '/media/private');
    $realFilePath = realpath($filePath);

    if ($realFilePath === false || strpos($realFilePath, $realBasePath) !== 0) {
        if ($debug) die("DEBUG: Zugriff verweigert! Pfad ungültig. Gesucht wurde: " . htmlspecialchars($filePath));
        header("HTTP/1.0 403 Forbidden");
        die("Access Denied");
    }

    // Whitelist for filetypes, add more, if needed
    $allowedTypes = ['image/gif', 'image/png', 'image/jpeg', 'image/webp'];
    $mimeType = mime_content_type($realFilePath);

    if (in_array($mimeType, $allowedTypes)) {
       
        if (!$debug) {
            header("Content-Type: " . $mimeType);
            header("Content-Length: " . filesize($realFilePath));
            header("Content-Disposition: inline; filename=\"" . basename($realFilePath) . "\"");
            header("Cache-Control: private, no-cache, must-revalidate");
            header("Pragma: no-cache");
            ob_clean();
            flush();
            readfile($realFilePath);
            exit();
        } else {
            // show debug messages
            echo "<b>DEBUG-MODUS: Success!</b><br>";
            echo "This script is working:<br>";
            echo "<b>path:</b> " . htmlspecialchars($realFilePath);
            die();
        }

    } else {
        if ($debug) die("DEBUG: forbidden filetype!");
        header("HTTP/1.0 403 Forbidden");
        die("File type not allowed.");
    }

} else {
    // if not authenticated, send 403-error
    if ($debug) die("DEBUG: not authenticated!");
    header('HTTP/1.0 403 Forbidden', TRUE, 403);
    die('<!DOCTYPE HTML><html><head><title>403 Forbidden</title></head><body><h1>Forbidden</h1><p>You don\'t have permission to access this file.</p></body></html>');
}
?>
--- End quote ---


Example for .htaccess to protect a folder (for Apache 2.4 and (fallback) older


--- Code: ---# for Apache 2.4 and newer
Require all denied

# for Apache 2.2 and older (Fallback)
<IfModule !mod_authz_core.c>
  Order Deny,Allow
  Deny from all
</IfModule>

--- End code ---



P.S.: works with images in a protected folder /media/private


CodeALot:
Thank you all for the great suggestions! I have my work cut out :-)

sternchen8875:
i add this to my Todo-list for the next WB-Version (maybe december or january) and have now a intern function with permissions control for logged users.
My idea: we add a /private/-Folder as subfolder from /media, protected with a .htaccess-file
a central controller works in our index.php in root-folder sends every request to the segret folder to a function

works fantastic in my test, but i've to talk with the others first about this

why not the method from my last posting?
it has no permission control for users

crnogorac081:
I could Make a ckeditor plugin

CodeALot:

--- Quote from: crnogorac081 on August 23, 2025, 06:04:52 PM ---I could Make a ckeditor plugin

--- End quote ---
That would be wonderful already, although I am more looking for 'protected' MiniGallery-albums. But I understand it will be very complicated to get this to work.

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version