Sichere MySQL-Abfragen mit der angepassten MySQLLib3 und PDO

    • Allgemein

    Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von PAL92.

      Sichere MySQL-Abfragen mit der angepassten MySQLLib3 und PDO

      Hallo zusammen

      Ich wurde angegangen, dass ich doch meine Erfahrungen mit der Anpassung an Dodos MySqlLib3 posten soll. Dies tu ich nun.

      Allgemeines

      Ausgangslage:
      Vor einiger Zeit hat Dodo netterweise eine MySQL-Library (MySqlLib3) erstellt, die es schnell und einfach ermöglicht, aus VB MySQL-Queries auszuführen. Um die Library sicherer zu machen, hat er eine Verschlüsselung eingebaut (mittels einem SHA1-Key). Es hat sich allerdings herausgestellt, dass diese Verschlüsselung mit etwas Fachwissen gut zu knacken ist. Das entsprechende Tool wurde hier auch veröffentlicht. Mehr dazu in folgendem Thema: Bringt eure Datenbanken in Sicherheit

      Die MySqlLib3 hat aber auch ein anderes grundlegendes Problem: alle SQL-Statements werden komplett in VB zusammengestellt, sprich: VisualBasic übergibt das fertige Query an die Library. Dies ist aber äusserst gefährlich. Mit etwas Aufwand ist es recht leicht möglich, den Datenverkehr zu manipulieren und andere Queries zur Datenbank zu schicken. Auch ist der Schutz vor SQL-Injection nicht komplett gegeben.
      Für mein aktuelles Projekt habe ich nun versucht - auch der Basis der MySqlLib3 - die ganze Sache etwas sicherer zu gestalten. Denn: die Idee mit der Verschlüsselung ist grundsätzlich eine Gute und bietet zumindest ansatzweise einen Schutz. Aber leider nicht den Schutz, den ein öffentlich verfügbares Programm mit Zugriff auf eine MySQL-Datenbank eigentlich benötigt.

      Umsetzung:
      Die Umsetzung erfolgt folgendermassen:
      - Die MySqlLib3 von Dodo wird grundsätzlich beibehalten. Die DLL-Datei wurde in keinster Weise angepasst, ebensowenig die Einbindung in VisualBasic
      - Die Übergabe der Daten an die MySqlLib erfolgt anders
      - Serverseitig wird die class.query.php massiv angepasst

      Einfache Nutzung:
      Meine Anpassungen führen dazu, dass bei neuen SQL-Statements leicht mehr Arbeit anfällt. Es reicht nicht, diese in VB zu erstellen, sondern die Statements müssen jeweils im PHP-File eingetragen werden. Eventuell gibt es auch einfachere Ansätze, für mich hat die hier vorliegende Version erstmal gestimmt.

      Disclaimer:
      mir ist bewusst, dass dies nicht die endgültige Weisheit ist, was ich hier zeige. Es ist ein Ansatz und unknackbar wird er auch nicht sein ;).

      So, nun aber zum Eingemachten.

      Ausführung
      VisualBasic
      Ich gehe hier davon aus, dass die MySqlLib3 von Dodo installiert und in das Projekt eingebunden ist. Die Funktionsweise ändert sich auch nicht gross. Einzig das Übergeben eines Queries wird nur mit Argumenten erledigt. Im Beispielprojekt wird die Query folgendermassen erstellt bzw. ausgeführt:

      Spoiler anzeigen

      VB.NET-Quellcode

      1. db.Encoding = Encoding.GetEncoding("ISO-8859-1")
      2. Try
      3. Dim rc As MySqlLib.ResultCollection = db.Query("SELECT * FROM `files` WHERE id='80';")
      4. If rc.Row.Count > 0 Then
      5. For Each kp As Generic.KeyValuePair(Of String, String) In rc.Row(0).Columns
      6. TextBox1.Text &= kp.Key & " -> " & kp.Value & Environment.NewLine
      7. Next
      8. End If
      9. Catch scriptEx As MySqlLib.ScriptException
      10. MessageBox.Show(scriptEx.Message, "Script Fehler")
      11. Catch mysqlEx As MySqlLib.MySqlException
      12. MessageBox.Show(mysqlEx.ToShortString(), "MySql Fehler")
      13. Catch ex As Exception
      14. MessageBox.Show(ex.Message, "Allgemeiner Fehler")
      15. End Try


      Für uns wichtig ist aber eigentlich nur der Aufruf der Abfrage:

      VB.NET-Quellcode

      1. Dim rc As MySqlLib.ResultCollection = db.Query("SELECT * FROM `files` WHERE id='80';")


      Man kann also mit db.Query("") jedes beliebige Query absetzen. Meine Variante ist leicht anders: Diese sieht es vor, dass nur eine Querynummer (wird im PHP-Script ausgelesen) sowie die nötigen Argumente weitergeben werden. Am Beispiel oben:

      VB.NET-Quellcode

      1. Dim rc As MySqlLib.ResultCollection = db.Query("1|80")

      Die 1 steht dabei als Parameter, dass das PHP-Script später die richtige Query aufruft, 80 ist der übergebene Parameter. Als separator dient das |-Zeichen. Das |-Zeichen im übrigen deshalb, weil es z.B. in Dateinamen etc. nicht erlaubt ist. Je nach dem, was man in der Query übergibt, kann das wichtig sein.

      Wenn man unterschiedliche Queries absetzen möchte, macht es Sinn, den Aufruf mittels Variable zu lösen:

      VB.NET-Quellcode

      1. var_query= "1|80"
      2. Dim rc As MySqlLib.ResultCollection = db.Query(var_query)

      So kann man den Aufruf und das Einlesen irgendwo in einer Function oder einer Sub ablegen und das Query von sonstwo aus befüllen und die Funktion aufrufen.

      Das war schon alles, was es in VisualBasic grundsätzlich zu beachten gibt.

      PHP
      PHP-seitig wird es etwas komplizierter. Deshalb stelle ich ein Beispielscript in den Anhang. Auf dieses beziehe ich mich
      Für die Anpassungen muss die Datei "class.query.php" im Ordner "lib" der Scriptfiles angepasst werden. Macht euch bitte eine Sichrheitskopie, bevor ihr loslegt.
      Im ersten Schritt werden die beiden benötigten Dateien (config und die class.request) eingebunden. Dies hat sich nicht verändert. Im zweiten Schritt wird die Datenbankverbindung mit PDO getestet:

      PHP-Quellcode

      1. require("config/config.inc.php");
      2. require("lib/class.request.php");
      3. //DB Verbindung testen
      4. try
      5. {
      6. $objDb = new PDO($strDbLocation, $strDbUser, $strDbPassword);
      7. }
      8. catch (PDOException $e)
      9. {
      10. echo 'Datenbank-Fehler: ' . $e->getMessage();
      11. }


      Anschliessend folgt ein grosser, unveränderter Block der MySqlLib3, der die Nutzung ausschliesslich durch die Library prüft, sprich dann auch den SHA1-Key kontrolliert:
      Spoiler anzeigen

      PHP-Quellcode

      1. $scriptversion = "3.0";
      2. $dt = new decryptText;
      3. $match = array();
      4. if(!preg_match("#ZoniCom MySqlLib/(?P<version>\d+.\d+) \((?P<hash>\w+)\)#", $_SERVER['HTTP_USER_AGENT'], $match)) {
      5. header('HTTP/1.1 401 Unauthorized');
      6. echo "<h1>Access denied</h2>\n\nThis script can only be used by ZoniCom&copy; MySqlLib Version: $scriptversion";
      7. exit();
      8. }
      9. if($match['version'] != $scriptversion) {
      10. echo "MySqlLib/".$scriptversion." 102 OK\n";
      11. echo "Different version";
      12. exit();
      13. }
      14. if($debugMode && $match['hash'] != sha1("")) {
      15. echo "MySqlLib/".$scriptversion." 102 OK\n";
      16. echo "Script has debug mode";
      17. exit();
      18. }
      19. if(!$debugMode && $match['hash'] == sha1("")) {
      20. echo "MySqlLib/".$scriptversion." 102 OK\n";
      21. echo "Application has debug mode";
      22. exit();
      23. }
      24. $pKey = "";
      25. $keyFound = false;
      26. $keyFilePath = "keys/keys.sha1";
      27. if(!file_exists($keyFilePath)) {
      28. echo "MySqlLib/".$scriptversion." 102 OK\n";
      29. echo "Key file does not exists";
      30. exit();
      31. }
      32. $appkey = file($keyFilePath, FILE_SKIP_EMPTY_LINES);
      33. foreach($appkey as $value) {
      34. $value = trim($value);
      35. if($match['hash'] == sha1($value)) {
      36. $dt->pKey = $value;
      37. break;
      38. }
      39. }
      40. if(empty($dt->pKey) && !$debugMode) {
      41. echo "MySqlLib/".$scriptversion." 102 OK\n";
      42. echo "Key does not match";
      43. exit();
      44. }


      Anschliessend werden die Daten aus VisualBasic via Post ausgelesen und entschlüsselt:

      PHP-Quellcode

      1. //Einlesen und Entschlüsseln der übergebenen Parameter
      2. $queryString = $dt->XOrDecode($_POST['pack'], $debugMode);


      Den String müssen wir jetzt erstmal zerlegen. Danach werden die einzelnen Werte einem Array zugeführt (query_array)

      PHP-Quellcode

      1. //String zerlegen
      2. $string = $queryString;
      3. $query_array = explode ( '|', $string );


      Wie bereits erwähnt, wird der erste Parameter dafür zuständig sein, das korrekt Query zu erkennen. Deshalb wird diese Zahl gleich mal in eine eigene Variable abgelegt.

      PHP-Quellcode

      1. //Querynummer in eigene Variable schreiben
      2. $querynumber = $query_array[0];


      Der Hauptteil der Arbeit besteht nun aus dem Bauen der Queries. Wie schon erwähnt, müssen wir alle unterschiedlichen Queries in die PHP-Datei einfügen. Ich habe dies hier direkt in der class.query.php erledigt, es ist aber - zur besseren Übersicht - natürlich auch möglich, dies in eine eigene Datei auszulagern und mittels include an der entsprechenden Stelle einzubinden.
      Hier der Teil mit meinen drei Beispielqueries:

      Spoiler anzeigen

      PHP-Quellcode

      1. //Queries
      2. //*********************************
      3. //In diesem Abschnitt werden die einzelnen Queries zusammengesetzt
      4. //Kann beliebig erweitert werden
      5. if($querynumber == "1")
      6. {
      7. //Beispiel zum erstellen eines Benutzers
      8. $query_adapted = $objDb->prepare("INSERT INTO users (username, password, email, uploadlimit) VALUES (:param1, :param2, :param3, '2048')");
      9. }
      10. elseif($querynumber == "2")
      11. {
      12. //Beispiel zum unscharfen Suchen eines Benutzers
      13. $query_array[1] = "%".$query_array[1]."%";
      14. $query_adapted = $objDb->prepare("SELECT * FROM users WHERE username LIKE :param1");
      15. }
      16. elseif($querynumber == "3")
      17. {
      18. //Beispiel zum Prüfen von Benutzernamen und Passwort
      19. $query_adapted = $objDb->prepare("SELECT * FROM users WHERE username = :param1 AND password = :param2");
      20. }
      21. else
      22. {
      23. //Hier kann der Fehler abgefangen werden
      24. }



      Die If-Abfragen muss ich nicht gross erklären. Hier wird jeweils die Query-Nummer abgefragt:

      PHP-Quellcode

      1. if($querynumber == "1")

      Wie ihr sehen könnt, ist der Aufbau einer Query nicht gross anders. Auf einige Dinge möchte ich aber hinweisen. Dazu nehmen wir folgendes Query:

      PHP-Quellcode

      1. $query_adapted = $objDb->prepare("INSERT INTO users (username,
      2. password, email, uploadlimit) VALUES (:param1, :param2, :param3,
      3. '2048')");

      - Hier nutzen wir PDO (PHP Data Objects) mit sogenannten prepared Statements. Dies ist eine sichere Sache, vor allem für unsere Parameter, die übermittelt wurden. PDO sichert dabei die Statments und die Parameter selbständig gegen SQL-Injections ab, so dass wir beispielsweise die Variablen nicht mehr escapen müssen.
      - Die Variablen sind direkt im Statement und müssen nicht erst mit Schlusszeichen, Punkt, Variable, Punkt, Anführungszeichen eingegliedert werden. Auch die Variablen benötigen selbst keine Hochkommatas mehr. PDO fügt diese selbständig hinzu.
      - Die Parameter werden im obigen Beispiel mit :param1 und :param2 eingebunden. Ihr könnt die auch anders nennen, aber da ich bei 14 Abfragen nicht für jeden Wert eine eigene Variable setzen will, habe ich den Begriff allgemein gehalten und die nötigen Parameter nummeriert.

      Ich möchte euch hier noch auf eine Besonderheit hinweisen, wenn es um Unscharfe Abfragen geht. Normalerweise würde man dies ja in der Query so handhaben:

      SQL-Abfrage

      1. LIKE '%" .$variable . "%'
      .
      Bei PDO müssen wir die % bereits davor in den Parameter hineinverfrachten. Deshalb habe ich beim Query 2 diese noch vor dem Query erstellen angehängt:

      PHP-Quellcode

      1. $query_array[1] = "%".$query_array[1]."%";


      Das war schon alles, was die Abfragen anbelangt.
      Jetzt fragt ihr euch vielleicht, woher die Parameter :param1 und :param2 kommen sollen.
      Nur die Ruhe, diese werden jetzt, NACH dem Erstellen der Queries definiert. Wenn die Parameter vor den Queries gesetzt werden, führt dies zu einem Fehler.
      Und so wirds gemacht:

      PHP-Quellcode

      1. $query_adapted->bindParam(':param1', $query_array[1]);


      Ich binde also meinen Wert an den Parameter :param1. Es ist der zweite Wert in meinem Array mit meinen übergebenen Parametern (ihr erinnert euch ans Aufsplitten und ins Array einfügen weiter oben). Das Gleiche macht ihr mit allen restlichen Parametern, die vorkommen könnten. Jetzt kommt noch der leichte Haken: "könnten". In meinem Script für ein Projekt habe ich derzeit 14 Queries mit einer unterschiedlichen Anzahl Parametern. Das Maximum ist 7, das Miniumum ist einer. Ich muss also bis zu 7 Parameter abfangen und anbinden. Das Problem ist, dass binParam keine leeren Variablen verträgt, sonst zickt PHP rum. Um das zu verhindern, habe ich einfach hier auch nochmal bisschen mit IFs um mich geworfen und prüfe, ob die entsprechenden Variablen überhaupt da sind:

      PHP-Quellcode

      1. if (!is_null($query_array[1]))
      2. {
      3. $query_adapted->bindParam(':param1', $query_array[1]);
      4. }


      So, alles bereit... dann wollen wir das Query ausführen. Egal ob wir schreibend oder lesend auf die DB zugreifen, der Aufruf ist immer:

      PHP-Quellcode

      1. //Query ausführen
      2. $query_adapted->execute();


      Ich unterscheide nun, ob es sich um ein lesendes oder ein schreibendes Query handelt. Dazu frag ich einfach ab, welche Querynummer wir haben:

      PHP-Quellcode

      1. //bei schreibenden Queries
      2. if($querynumber == "1")
      3. {
      4. echo "MySqlLib/".$scriptversion." 100 OK\n";
      5. echo $dt->XOrEncode(join("\n", $result), $debugMode);
      6. }

      Die erste Zeile kann natürlich mit OR um alle schreibenden Queries erweitert werden. Anschliessend wir lediglich das OK an die MySqlLib zurückgeschickt.

      Der zweite Teil ist etwas umfangreicher. Hier möchte ich ja auch die Ergebnisse zurückgeben:
      Spoiler anzeigen

      PHP-Quellcode

      1. else
      2. //bei lesenden Queries
      3. {
      4. $queryResult = $query_adapted->fetchAll();
      5. if (!empty($queryResult))
      6. {
      7. foreach($queryResult as $row)
      8. {
      9. $resultString = array();
      10. foreach($row as $column => $value) {
      11. $resultString[] = "\"".addslashes($column)."\":\"".addslashes($value)."\"";
      12. }
      13. $result[] = "{".join(",", $resultString)."}";
      14. }
      15. echo "MySqlLib/".$scriptversion." 100 OK\n";
      16. echo $dt->XOrEncode(join("\n", $result), $debugMode);
      17. }
      18. else
      19. {
      20. echo "MySqlLib/".$scriptversion." 103 OK\n";
      21. echo "noresult";
      22. }
      23. }


      Darauf möchte ich noch kurz eingehen: im ersten Schritt wird das Restultat abgerufen (fetch) und in eine Variable gespeichert. Danach prüfe ich, ob überhaupt etwas drin steht:

      PHP-Quellcode

      1. $queryResult = $query_adapted->fetchAll();
      2. if (!empty($queryResult))

      Wenn ja, wird jede Zeile ausgelesen und wiederum in ein Array geschrieben:

      PHP-Quellcode

      1. foreach($queryResult as $row)
      2. {
      3. $resultString = array();


      Der restliche Teil entspricht wieder dem MySqlLib3-Standard und bringt nun das Resultat in eine Form, um es zurückzugeben. Anschliessend wird noch der OK-Status mitgegeben.

      War der Rückgabewert bei der IF-Abfrage leer, so wird ein OK - No Result zurückgegeben:

      PHP-Quellcode

      1. echo "MySqlLib/".$scriptversion." 103 OK\n";
      2. echo "noresult";


      That's it - nothing further to do

      Demo-PHP-Script (korrigierte Version) zum Download:
      class.query_sample.zip

      Schluss:
      Die MySQL-Abfragen so zu gestalten ist etwas aufwändiger. Jede unterschiedliche Abfrage muss separat erfasst werden und in VB müssen die Parameter übergeben werden. Auch wenn es mehr arbeit macht, so ist diese Variante doch sicherer. Wie bereits eingangs erwähnt, ist dies definitiv nicht der Weisheit letzter Schluss, zumal meine PHP- Kentnisse recht eingerostet sind ;). Also dürft ihr gerne Vorschläge für Verbesserungen und Kritig anbringen :).

      Gruss,
      KlyX
      Chris' Weblog - Mein Blog rund um Vieles :D

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „KlyX“ ()

      Ich habe gerade festgestellt, dass mir im PHP-Script noch ein kleiner Fehler unterlaufen ist:
      die Prüfung der einzelnen Arrayteilen beim zuweisen an die gebundenen Parameter, die ich so vornehme

      PHP-Quellcode

      1. if (!empty($query_array[1]))
      2. {
      3. $query_adapted->bindParam(':param1', $query_array[1]);
      4. }

      ist falsch.

      Richtig wäre:

      PHP-Quellcode

      1. if (!is_null($query_array[1]))
      2. {
      3. $query_adapted->bindParam(':param1', $query_array[1]);
      4. }


      Der Unteschied zwischen !empty und !is_null: bei not empty gelten auch 0 als Integer und "0" als String als "empty". Sprich, wenn ihr in der Abfrage bzw. in den übergebenen Parametern eine 0 übergeben wollt, wird dies nicht funktionieren. not is_null prüft nur auf vorhandensein eines Werts. 0 und "0" werden als wert gehandelt und dementsprechend funktionierts. Nur "" und NULL ergeben in diesem Fall ein "True", während der ganze rest "False" als Ergebnis hat und somit die Variable via bindParam einliest.

      Ich habe das PHP-File in meinem Beispiel dahingehend angepasst.

      Gruss,
      KlyX
      Chris' Weblog - Mein Blog rund um Vieles :D
      Hi,

      überprüfen ob man schreiben oder lesen soll kann man doch noch anders machen, hab auf die schnelle was geschrieben.

      Nach

      PHP-Quellcode

      1. <?php
      2. $query_array = explode ( '|', $string );
      3. ?>


      Einfügen

      PHP-Quellcode

      1. <?php
      2. /* <Advanced option by donriff> */
      3. //Erweiterter Parametervergleich. Prüfen ob r oder w an der Zahl oder hinter der Zahl angegeben wurde um zu identifizieren ob wir schreiben oder lesen sollen.
      4. if(stristr('doRead', $query_array[0] === True)
      5. {
      6. $sql_method = "read";
      7. $query_array[0] = str_replace('doRead', '');
      8. }
      9. elseif(stristr('doWrite', $query_array[0] === True)
      10. {
      11. $sql_method = "write";
      12. $query_array[0] = str_replace('doWrite', '');
      13. }
      14. else
      15. {
      16. echo "MySqlLib/".$scriptversion." 102 OK\n";
      17. echo "FATAL: parameter fault! Wanna Read or Write?!";
      18. exit();
      19. }
      20. /* </Advanced option by donriff> */
      21. ?>


      und

      PHP-Quellcode

      1. <?php
      2. //bei schreibenden Queries
      3. if($querynumber == "1")
      4. ?>


      zu

      PHP-Quellcode

      1. <?php
      2. //bei schreibenden Queries
      3. if($sql_method == "write")
      4. ?>


      machen.

      Jetzt müsste der erste Parameter doRead1|param1 oder 1doRead|param1 heißen bzw. doWrite1|param1 oer 1doWrite|param1

      das könnte man noch zuvor einbauen indem man mit preg_split() o.ä. den geposteten string komplett auseinander nimmt. So spart man sich aber schonmal immer das OR in der If.

      Gruß,
      riff

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „donriff“ ()

      Salü donriff

      Das ist natürlich ne klasse Idee :). Da hätt ich auch selbst draufkommen können :D
      Ich werde das mal so noch übernehmen im Tutorial bzw. die Demofiles anpassen (natürlich mit dem Comment von wem die Advanced Option ist ;)).

      Danke für den Input :)

      Gruss,
      KlyX
      Chris' Weblog - Mein Blog rund um Vieles :D
      Hi,

      anstatt einer querynummer kann man auch einen Text verwenden, der den Query auch identifiziert. Je nach dem wie man es macht ist es sicherer, denn der der Hacken will kann mit einem string àla "hsdjbkjgbsdfbhsjd,doRead" o.ä nichts anfangen es sei denn er kann sich denken was das query machen soll. Ob dazu eine erweitere Änderung erforderlich ist mit dem escapen ist fragwürdig, müsste ich mal gucken. Kannst mir ja eine PN schreiben vl. können wir da noch mehr tüfteln und eine komplett neue version rausbringen.

      kind regards.
      Hi

      Eigentlich sollte mit den prepared Statements das Ganze schon gut genug gegen Injections abgesichert sein. Denke nicht, dass da noch escapen notwendig ist. Und die Strings selbst, die man dann nutzen würde, um den richtigen SQL-Befehl zu identifizieren wird ja im Endeffekt niemals in der query landen. Ansonsten muss man sicherlich nichts mehr umschreiben, denn man kann das recht unproblematisch machen, muss halt einfach auch im PHP-File die Anpassungen vornehmen.

      Was ich mir überlege ist, ob man das vielleicht automatisieren kann. Sprich: nen kleines Tool (PHP oder VB) in dem man die Abfragen, die Variablen, die Erkennungsmerkmale eingeben kann und das PHP-Script dann automatisch erstellt wird.
      Nur fehlt mir im MOment grad die Zeit, aber es wär mal ne Idee ;)
      Chris' Weblog - Mein Blog rund um Vieles :D
      Hi,

      ja ok, stimmt auch wieder :D


      sowas ist schnell programmiert, das könnte ich in PHP sowie VB erstellen. Muss nur Zeit finden, dann nehm ich dir das ab. AN sowas hate ich ja auch schon gedacht.

      kind regards.

      EDIT:

      die config im PHP-Script muss auch angepasst werden, nämlich so:

      PHP-Quellcode

      1. <?php
      2. $strDbLocation = "mysql:dbname=DATENBANKNAME;host=127.0.0.1";
      3. $strDbUser = "USERNAME";
      4. $strDbPassword = "PASSWORT";
      5. $debugMode = true;
      6. ?>


      hab ich eben so festgestellt.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „donriff“ ()

      Ich weiß das dieser Thread schon ziemlich altbacken ist, dennoch würde ich dem Thema gerne noch etwas hinzufügen:
      Um die sicherheit der Library zu erhöhen bin ich zu folgender vorgehensweise übergegangen:

      1. Ich speichere die Querys nicht in einer PHP-Datei sondern in gesonderten Tabelle innerhalb der Datenbank (id und query). Dies ist zwar mit sicherheit langsamer, allerdings habe ich dahingehend noch keine Leistungseinbußen in meiner Applikation feststellen können (kommt sicherlich aber auf die jehweilige Anwendung an.) Desweiteren bin ich dadurch flexibler bei der gestaltung der Querys.
      2. Bei jeder Query über die Lib wird ein weiterer Parameter übergeben. Dieser besteht aus einem MD5 Hash der sonstigen Parameter inklusive einiger Steuerzeichen. Dies bietet auch keine ultimative Sicherheit, macht es dennoch wieder ein wenig schwerer =)

      Ich schaue spätestens heute abend mal in mein Repository um euch die entsprechenden Codeteile zu liefern.

      Grüße
      Guten Abend,

      um das Thema mal wieder aufzugreifen, ich erhalte nach Umstellung des Debug mode auf False folgende Fehlermeldung bei
      der Abfrage:

      Die Eingabe ist keine gültige Base-64-Zeichenfolge, da sie ein Nicht-Base-64-Zeichen, mehr als zwei Leerstellen oder in den Leerstellen ein Zeichen enthält, das ungültig ist

      Das Programm führt die Abfrage korrekt aus aber vorher kommt diese Fehlermeldung. Woran kann das liegen, an dem Sha1-Key?

      Vielen Dank.

      LG PAL