Zum Thema Sicherheit

  • Hallo,


    nachdem ich mir nun ein paar nette Sachen zusammengebastelt habe die (jedenfalls auf meinem PC mit XAMPP) auch fehlerfrei laufen, wollte ich mir nun noch ein paar Informationen zum Thema Sicherheit einholen - möchte ja nicht irgendwelche unnötigen Sicherheitslücken wenn ich mal was online stelle.


    Jetzt hab ich mir schon einiges angelesen und dennoch einige Fragen um sicherzugehen, dass ich auch alles richtig verstanden habe.


    1. MySQLi und Prepared Statements.
    Ich habe mir angelesen, dass durch MySQLi in Verbindung mit den Prepared Statements keine Möglichkeit besteht irgendeinen Schädlichen Code in der Datenbankabfrage auszuführen, früher musste man alle Eingaben des Nutzers vor dem Ausführen in der DB auf etwaige böse Zeichen prüfen, jetzt sei das nicht mehr so, weil im Grunde erst die Anfrage an den Server geschickt wird (Vorbereitet) und der Server danach nur noch die Platzhalter ersetzt.
    Ist das so wirklich korrekt? Oder muss ich da doch irgendetwas beachten, irgendwelche Ausnahmefälle?


    2. Suchmaschinenfreundliche URLs
    Ich habe mir, weils schick aussieht und so nicht jeder direkt erkennt, es handelt sich um php diese schicken URLs gemacht wie example.com/Site/Welcome statt example.com/index.php?Site=Welcome. Funktioniert sogar tadellos.
    Nun las ich auch dazu, dass es dort Sicherheitslücken geben könnte (der Benutzer kann ja strenggenommen alles eingeben) "sichern" tu ich das im Moment nur wie folgt:


    Einmal mit: stripslashes (wird auf alle $_POST, $_GET, $_REQUEST, $_SERVER['REQUEST_URI'] etc. angewendet)
    und mit: preg_replace('/[^a-zA-Z0-9\/_-]/', '', $url) so das eben nur noch bestimmte Zeichen zugelassen werden, bzw. alle die ich nicht brauche/in keiner URL vorkommen werden, entfernt werden.
    Ist das ausreichend oder muss ich diesbezüglich vielleicht mehr beachten?


    3. Dateiaufruf verhindern oder nicht?
    Auf einigen Seiten habe ich gelesen, dass alle Dateien die includet werden sollen, vor direktem Aufruf geschützt werden sollen. Wie man das aber genau macht (ausser mit .htaccess) habe ich nicht rausgefunden. Außerdem weiß ich auch nicht ob das noch aktuell ist (diese Aussagen sind alle schon einige Jahre alt).
    Bei mir sieht das im Moment so aus: Es wird alles von der index.php aus geregelt, dort wird nur die Hauptklasse (die ich immer brauche) includet, alles andere befindet sich auch in eigenen Klassen-Dateien. Wenn ich so eine Klassen-Datei aufrufe, dann passiert ja im Grunde nichts (wie auch, werden ja keinerlei Paramter übergeben mit denen die Klasse arbeiten könnte). Wie sieht das also aus?


    4. Seite selber auf Sicherheitslücken testen
    Überall las ich zu dem Thema, man soll seine Seite selbst auf alle möglichen Lücken testen. Aber wie mache ich das? Ich bin ja keiner der den ganzen Tag versucht fremde Seiten lahmzulegen, hab also keine Kentniss darüber.
    Bisher fand ich nur so einen Javascript-Beispiel (n Alert) gefunden das man in der URL eingeben könnte - passiert bei mir aber nichts (Ausgabe mit var_dump() ergibt dann string(30) "Start3Cscript3Ealert27doof273C").
    Wie genau prüf ich denn also meine Anwendungen auf Sicherheitslücken selber?


    5. Ich bin auch dankbar für alle anderen Tipps, Infos, Erklärungsseiten/Videos, ...

  • 1. stimmt soweit: http://php.net/manual/de/mysql…t.prepared-statements.php
    (siehe "Basic workflow")
    Hatte selbst gedacht, dass die Platzhalter ersetzt werden, bevor die Query an den Sql-Server geht.
    Muss ich selbst mal nachsehen, wie genau der Ablauf ist.
    Fakt ist aber, dass bei richtiger Anwendung keine SQL-Injectons mehr möglich sind.
    Bsp falsch und richtig:


    (siehe types: http://php.net/manual/de/mysqli-stmt.bind-param.php)
    (btw: bei PDO siehe: http://php.net/manual/de/pdo.constants.php)


    Achtung: Platzhalter können (und sollen) nicht für Tabellennamen verwendet werden.



    2. Weiß nicht genau, was Dein Script macht (include $_GET ... ?),
    aber im Grunde würde ich hier auch auf Whitelists setzen.

    PHP
    if(isset($sites[$siteName])){} ...
    // sicherlich das ganze in aussagekräftige funktionen setzten wie zB
    if(isValidSiteName($siteName)){
    	$siteName = getValidSiteName($siteName);
    	...
    } ...


    EDIT: siehe auch wolf: Include



    3. hast Du schon richtig gamacht. Wenn Du Dich daran hältst, dass in Klassen-Dateien nur die Klassen enthalten sind, dann kann nix passieren.
    Es sei denn Du kommst einmal an den Punkt, an dem Du eine ("static")Klasse initialisierst:

    PHP
    <?php
    // file MyClass.php
    class MyClass{
    	public function init(){
    		// ...
    	}
    }
    // wenn dieses file geladen wird, auto-init sich diese klasse
    MyClass::init();
    // ... kann man machen, sieht man auch öfter


    Im Grunde sollten include Ordner nicht von Aussen erreichbar sein (Server config).
    Wenn Du auf nummer sicher gehen willst, dann kannst Du eine Konstante definieren:
    - index.php ganz am Anfang: define('MAIN', 1);
    - other_scripts.php ganz am Anfang: !defined('MAIN') AND exit('meldung ...');
    (könntest auch nen Redirect auf index.php machen, wenn MAIN nicht definiert ist.)
    Ohne die definierte Konstante geht also nix.



    4. Ich hatte mal https://addons.mozilla.org/en-us/firefox/addon/xss-me/ genutzt. Ist aber lange her. Falls es jemand nutzt, dann bitte vorher prüfen/lesen, ob die Extension eine "gute" ist.
    Ansonsten denke ich, dass andere Deine Fehler besser finden, als Du selbst. (BETA)
    Also einfach mal paar vertauenswürdige auf Deine Seite los lassen. So nach dem Motto: "versucht es mal kaputt zu machen" :D
    Zum String: wenn Du Daten ausgibst, dann immer masiert.
    PHP: $secure = htmlspecialchars($inSecureData);
    JS: var secure = document.createTextNode(inSecureData);


    5. (hab mir den Namen mal wieder nicht gemerkt, aber ich glaube es hieß) Reverse-Injections:
    Stell Dir vor Du:
    - bekommst eine GET var und
    - schreibst sie per :platzhalter in die db
    - beim Anzeigen der/einer Seite lädst Du diesen Wert wieder, um ihn anzuzeigen
    Dann kann folgendes passieren:
    - bei der Ausgabe vergisst Du den Wert zu maskieren:

    PHP
    <?php
    // FALSCH:
    Das hier ist der Wert, den Sie zuvor eingegeben hatten: <?php echo $dbResult['eingabe'] ?>
    // das sollte niemals passieren! 
    // Alle geladenen Daten (aus files, db, GET, POST, SESSION, w/e) gehören maskiert ausgegeben
    // wie zB
    // RICHTIG
    Das hier ist der Wert, den Sie zuvor eingegeben hatten: <?php echo htmlspecialchars($dbResult['eingabe']) ?>


    - oder Du nutzt beim Laden von Daten den Wert aus einer Tabelle in der Query:

  • Zitat

    2. Weiß nicht genau, was Dein Script macht (include $_GET ... ?),
    aber im Grunde würde ich hier auch auf Whitelists setzen.


    Nicht so ganz:


    1. Es wird die ganze Seiten-URL abgerufen und in einer Variable gespeichert (vorher werden die einzelnen Bausteine standardmäßig bei mir mit stripslashes bearbeitet)
    2. Meine feste Domain (http://example.com) wird von der zusammengebauten URL (z.B http://example.com/Seite/Willkommen) entfernt, damit nur noch /Seite/Willkommen übrig bleibt.
    3. Seite/Willkommen wird beim / getrennt und daraus ergeben sich dann die Klasse Seite und der DB-Inhalt von Willkommen
    4. Alle ungültigen Zeichen werden aus "Seite" und "Willkommen" entfernt.
    5. Es wird geprüft ob die Klassendatei existiert (wenn nicht, gibts ne Fehlerseite) und dann wird der Parameter "Willkommen" an die Klasse übergeben.
    6. Die Klasse "Seite" z.B übernimmt den Rest (holt die Daten, baut die Seite zusammen, wirft Fehler aus falls "Willkommen" nicht existiert etc)


    So eine richtige Whitelist wollte ich eigentlich nicht, weil ich mir denke, so wie ich es jetzt mache müsste es reichen?
    Und ich wollte das ganze halt möglichst "variabel" aufbauen, wenn ich also ne neue Seite einbauen will, dann will ich nur ne neue Klasse einfügen müssen und nicht in allen möglichen anderen Dateien auch noch was umschreiben.


    Zitat

    - index.php ganz am Anfang: define('MAIN', 1);
    - other_scripts.php ganz am Anfang: !defined('MAIN') AND exit('meldung ...');


    So hab ich es immer gemacht als ich nur prozedural geschrieben habe, dachte aber (und da dachte ich ja auch richtig) wenn ich alles in Klassen schreibe, dann ist das nicht mehr notwendig.


    Zitat

    Also einfach mal paar vertauenswürdige auf Deine Seite los lassen. So nach dem Motto: "versucht es mal kaputt zu machen"


    Ich seh das schon kommen, 30 Sekunden später sagt mir der erste "erledigt" :D


    Zitat

    Zum String: wenn Du Daten ausgibst, dann immer masiert.
    PHP: $secure = htmlspecialchars($inSecureData);
    JS: var secure = document.createTextNode(inSecureData);


    Das ist das erste was ich immer mache, wenn ich irgendwo was ausgebe. :D

  • Es sollte keine Probleme geben, solange du festlegst, dass diese Werte (die ja manipuliert werden können per url)
    NUR in dem include-Verzeichnis genutzt werden, in denen NUR die Seiten-Klassen liegen.
    Andere Klassen-Dateien hätten dann dort drin nichts zu suchen.


    Hätte da nen Vorchlag:
    Du hast schon eine gut Idee gehabt. Du kannst es noch erweitern in dem Du:
    - angenommen order inc/controller/ enthält nur Seiten-Klassen (siehe google Controller)
    - in diesem Verzeichnis (inc/controller/) wird pro neue Seite ein Unterordner "SeitenName" angelegt (also zB Home)
    - jeder Unterordner "SeitenName" muss eine bestimmte Struktur besitzen: in inc/controller/SeitenName/ muss die Datei SeitenNameController.php mit der Klasse SeitenNameController existieren
    - die Klasse SeitenNameController muss Methoden (Funktionen) besitzen, die auf den Content leiten/zeigen/kontrollieren ...
    - die erwähnten Methoden müssen mit dem Zusatz "Action" ergänzt werden
    Jeder Controller besitzt also eine indexAction(), und andere wie zB loginAction()
    Im Bsp:


    Im Bsp für einen Login:


    Das Prinzip kenne ich von Magento. Find ich ziemlich geil. Wird aber sicherlich noch anderswo so umgesetzt.


    EDIT:
    ungetestet:


  • An sowas Ähnliches habe ich auch gedacht (weil man ja theoretisch alle Klassen aufrufen könnte, wenn man weiß wie sie heißen), deswegen habe ich für die Klassen die irgendwelche Inhaltsseiten darstellen sollen, immer die Methode "getSiteContent" eingebaut, die auch aufgerufen werden muss aus der "Controller"-Klasse und ohne die auch nichts läuft (versuch ich also einfach eine Klasse aufzurufen über die URL, dann gibts ne Fehlermeldung, weil diese Methode ja in den anderen Klassen nicht existiert).
    Würde das strenggenommen nicht auch reichen?
    Ansonsten ist das natürlich keine schlechte Idee, auf sowas wäre ich nicht gekommen.


    Zitat

    Holt euch CodeIgniter .. da ist diese ganze Logik schon drin..


    Ich weiß, es gibt schon viele fertige Frameworks die vieles schon mitbringen was man so braucht. Aber mir gings nicht darum nur schnell ne Homepage oder so zu bauen, sondern vorallem um den Lerneffekt. Wollte mich halt mit den ganzen Möglichkeiten, Funktionen, ... von PHP näher ajseinandersetzen um mir eigene Lösungswege zu schaffen und zu finden um das ganze besser zu verstehen. Deswegen wollte ich auf sowas erstmal verzichten, zwar würde ich damit wahrscheinlich schneller ans Ziel kommen, aber wie gesagt: Mir gehts eher ums lernen.

  • Würde das strenggenommen nicht auch reichen?


    Würde ich vermeiden.
    Denn so lässt Du ja das Script volle Kanne in ein Fatal Error rennen :D


    Du solltest mindestens prüfen, ob die Klasse existiert.
    Wenn sie existiert, dann hast du sie ja sicherlich geschrieben und sie wird die benötigte Methode besitzen.
    Den "Schritt" zur Klasse könnte User aber selbst ansteuern, indem er rumprobiert.
    Und anstatt ihm dann den Fatal Error (oder eine weiße Seite) zu zeigen, würde ich einfach umleiten auf die Hauptseite (oder 404).

  • PHP
    Du solltest mindestens prüfen, ob die Klasse existiert.
    Wenn sie existiert, dann hast du sie ja sicherlich geschrieben und sie wird die benötigte Methode besitzen.


    Ja ne, leider hab ich alle Klassen in einen Ordner geworfen, und das ist mir mittlerweile zu viel um die Klassen nun noch nach dem Muster VOLLER_PFAD_KLASSE.class.php um zu benennen und so (außerdem finde ich diese Namen immer so unübersichtlich). Wenns sein muss, ok, aber wenn ich es vermeiden kann... :D
    Und von daher könnte ja im Grunde jede Klasse aufgerufen werden, egal ob nun eine echte Seitenklasse, für die es gedacht ist oder beispielsweise die super-duper-admin-klasse. Aber das könnte ich ja mit einem try-catch-block verhindern in welchem ich auch prüfe ob die Methode getSiteContent, die ja nur in den Seiten-Klassen existieren kann, auch existiert.


    Habs eben ausprobiert und es scheint zu funktionieren... oder hab ich was relevantes vergessen in meiner Logik?

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!