Entwickler-Ecke

Sonstiges (Delphi) - Loginsystem Sicherheit


ChrisCross - So 08.02.15 11:14
Titel: Loginsystem Sicherheit
Hallo,

ich habe ein Problem mit der Lösung eines Loginsystemes. Meine Idee war folgende:

Auf einem FTP Server liegt in einem Verzeichnis eine ini Datei mit den Benutzernamen und den Kennwörtern. Das Verzeichnis ist per .htaccess und .htpasswd vor direkten Zugriffen geschützt. Mein Programm liest jetzt über idhttp die Datei aus und Überprüft den Benutzernamen und das Passwort. Ich kann mir jetzt nur nicht vorstellen, das das so sicher ist ;)

Eine Datenbank wäre wahrscheinlich sicherer, aber die MySQL Datenbank auf dem Server erlaubt keine externen Zugriffe und somit kann ich darauf auch nicht zugreifen?!
Zweite Idee war eine Webseite mit einem Loginformular, das mit der Datenbank kommuniziert. Aus meinem Programm könnte ich die Felder ausfüllen und überprüfen ob der Login richtig war. Die Datensicherheit sieht hier aber wahrscheinlich noch schlechter aus.


Ich habe jetzt keine weiteren Ideen wie ich das sonst lösen soll. Vielleicht hat ja jemand einen Denkanstoß für mich ;)

Christoph


FinnO - So 08.02.15 13:04

Moin,

Passwörter dürfen niemals unverschlüsselt gespeichert werden. Ich entnehme deinem Post mal, dass du eine Delphi-Anwendung hast, für die sich Nutzer mit einem im Internet hinterlegten Passwort anmelden sollen. Was du hier tun möchtest, ist einen Authentifizierungsdienst zu verwenden.

Im Fall eins bedeutet das, dass du dir eine Anwendung schreibst, die auf deinem Server läuft und deine Anmeldungen entgegennimmt und verifiziert - hier hast du dann auch Datenbankzugriff.

Der andere - und vernünftigere Weg wäre, auf einen Drittanbieter wie das kleine Softwareunternehmen Google [https://developers.google.com/accounts/] zurückzugreifen. So können Nutzer ihren Google-Account verwenden, den sie eh bereits besitzen. Ob du denen dann Zugriff auf deine Software gewährst, musst du dann zu einem anderen Zeitpunkt entscheiden.

Das waren erstmal zwei Denkanstöße von mir.

Gruß


ChrisCross - So 08.02.15 17:54

Vielen Dank für deine Antwort, jetzt weiß ich schon mal die Richtung ;)

Zitat:
Im Fall eins bedeutet das, dass du dir eine Anwendung schreibst, die auf deinem Server läuft und deine Anmeldungen entgegennimmt und verifiziert - hier hast du dann auch Datenbankzugriff.


Also in der Art eine Datei, die aus meiner Anwendung einen Befehl gesendet bekommt (per indy?) und dann in der Datenbank nachsieht und das ausgibt und mein Programm liest das aus?

Hast du vielleicht dazu ein gutes Tutorial?


FinnO - So 08.02.15 21:40

Moin,

das kommt darauf an, welche Programmiersprache du Serverseitig einsetzen möchtest. Googlen solltest du auf jeden Fall mal RESTful Service.

Gruß
Finn


ChrisCross - Fr 13.02.15 15:46

Danke für deine Antwort. Bisher habe ich mir folgendes Überlegt:

Im Delphi Programm


Delphi-Quelltext
1:
2:
3:
4:
procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Text := IdHttp1.Get('http://www.meine-seite.de/service.php?Benutzername=test&Kennwort=test');
end;


Die PHP Datei:


PHP-Quelltext
1:
2:
3:
4:
5:
6:
7:
<?php
$benutzername = $_GET["Benutzername"];
$kennwort = $_GET["Kennwort"];

//Datenbankabfrage wenn Daten richtig --> ja, sonst --> falsch

?>


Un das Ergebnis kann ich ja dann auswerten :)

Natürlich gibst bisher mit der Sicherheit wieder Probleme. Ist es sicherer wenn ich die Datei per .htpasswd schütze? Und wie sieht es mit IdHttp1 aus?
Oder muss ich ganz anders herangehen. Ich hoffe was ich mir überlegt habe, kommt nicht ganz so stümperhaft herüber ;)


BenBE - Fr 13.02.15 19:06

user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
Hallo,

ich habe ein Problem [...]

[...] einem FTP Server [...]

Okay, den möchte man loswerden ;-)

user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
[...] eine Datei mit den Benutzernamen und den Kennwörtern.

Du möchtest mindestens SCrypt-SHA2-512 für die Sicherung der Passworte nutzen ...

user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
Das Verzeichnis ist per .htaccess und .htpasswd vor direkten Zugriffen geschützt. Mein Programm liest jetzt über idhttp die Datei aus und Überprüft den Benutzernamen und das Passwort.

Ich frag jetzt lieber NICHT nach, ob da sowas wie "Verschlüsslung" verwendet wird. Das wird mir sonst zu deprimierend ... :mrgreen:

user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
Ich kann mir jetzt nur nicht vorstellen, das das so sicher ist ;)

Captain Obvious ruft grad an, er möchte seinen Job wieder. :twisted:

user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
Eine Datenbank wäre wahrscheinlich sicherer, aber die MySQL Datenbank auf dem Server erlaubt keine externen Zugriffe und somit kann ich darauf auch nicht zugreifen?!

Script drauf, was für einen Nutzernamen den zugehörigen Salt/SCrypt-Parameter und für Nutzernamen + SCrypt-Hash (mit diesen Parametern) den Erfolgsstatus zurückgibt. Das ganze über eine entsprechend gesicherte TLS 1.2+-Verbindung mit CAMELLIA256/AES256 GCM/CCM und SHA2-384 garniert mit nem vernünftigen Zertifikat (RSA 4096 für nahe Zukunft, RSA 8192 für Mittelfristig und RSA 15364 für was, was länger halten darf) welches mit SHA2 (SHA2-512 FTW) oder SHA3 signiert ist.

user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
Zweite Idee war eine Webseite mit einem Loginformular, das mit der Datenbank kommuniziert. Aus meinem Programm könnte ich die Felder ausfüllen und überprüfen ob der Login richtig war. Die Datensicherheit sieht hier aber wahrscheinlich noch schlechter aus.

Kommt drauf an. Simples REST, was einfach per POST zwei Parameter nimmt und mit 0 Bytes bei Fehler antwortet ist sicherlich sehr rudimentär, aber tut seinen Job. Und solange Du nicht bei Robert's Schule [http://xkcd.com/327/] den Code klaust, sollte auch der Schutz der DB zu schaffen sein. BTW: MySQL kann für die Connection auch TLS, nutzt nur meist keiner.

user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
Ich habe jetzt keine weiteren Ideen wie ich das sonst lösen soll. Vielleicht hat ja jemand einen Denkanstoß für mich ;)

Christoph

Wieviel Wert schätzt Du die Daten?
Was sichern sie denn ab?

Alternativ, solltest Du Dir einmal OAuth anschauen.



user profile iconFinnO hat folgendes geschrieben Zum zitierten Posting springen:
Moin,

Passwörter dürfen niemals unverschlüsselt gespeichert werden.

Hausaufgabe für den Threadersteller: Diesen Satz bis morgen 100 Mal an die Tafel schreiben.

user profile iconFinnO hat folgendes geschrieben Zum zitierten Posting springen:
Ich entnehme deinem Post mal, dass du eine Delphi-Anwendung hast, für die sich Nutzer mit einem im Internet hinterlegten Passwort anmelden sollen. Was du hier tun möchtest, ist einen Authentifizierungsdienst zu verwenden.

Oder es wie oben beschrieben abgesichert selber bauen. Wobei noch hinzu kommt, dass man auf einige Side-Channel achten muss (und z.B. nicht leaken sollte, ob es einen Nutzer gibt oder nicht), aber das ist Schritt 2.

user profile iconFinnO hat folgendes geschrieben Zum zitierten Posting springen:
Im Fall eins bedeutet das, dass du dir eine Anwendung schreibst, die auf deinem Server läuft und deine Anmeldungen entgegennimmt und verifiziert - hier hast du dann auch Datenbankzugriff.

Bitte Anmerkungen von oben beachten.

user profile iconFinnO hat folgendes geschrieben Zum zitierten Posting springen:
Der andere - und vernünftigere Weg wäre, auf einen Drittanbieter wie das kleine Softwareunternehmen Google [https://developers.google.com/accounts/] zurückzugreifen. So können Nutzer ihren Google-Account verwenden, den sie eh bereits besitzen. Ob du denen dann Zugriff auf deine Software gewährst, musst du dann zu einem anderen Zeitpunkt entscheiden.

Wobei hier die ToS von diesem Kleinunternehmen aus Kalifornien gelten, was die hiesigen Datenschutzbestimmungen und die daraus resultierenden Datenschutzerklärungen deutlich verlängert ...



user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
Danke für deine Antwort. Bisher habe ich mir folgendes Überlegt:

Im Delphi Programm:


Delphi-Quelltext
1:
2:
3:
4:
procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Text := IdHttp1.Get('http://www.meine-seite.de/service.php?Benutzername=test&Kennwort=test');
end;

Sensible Daten wie Logins NIE! mit GET übertragen, da diese ansonsten ins Access Log des Servers geleakt werden (könnten).

Für sowas nimmt man immer POST.

user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
Die PHP Datei:


PHP-Quelltext
1:
2:
3:
4:
5:
6:
7:
<?php
$benutzername = $_GET["Benutzername"];
$kennwort = $_GET["Kennwort"];

//Datenbankabfrage wenn Daten richtig --> ja, sonst --> falsch

?>


Und das Ergebnis kann ich ja dann auswerten :)

Jup, implementierungsdetails siehe weiter oben in meinem Post.

user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
Natürlich gibst bisher mit der Sicherheit wieder Probleme.

Ich seh bei dieser PHP-Datei erstmal nur das Problem, dass dieser Quelltext eine Konstante Funktion implementiert, du aber eine Punkt-Funktion zum Funktionieren brauchst :mrgreen:

user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
Ist es sicherer wenn ich die Datei per .htpasswd schütze?

Nein.

user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
Und wie sieht es mit IdHttp1 aus?

Nein.

user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
Oder muss ich ganz anders herangehen.

Verschlüsselte Verbindung oder PAKE.

user profile iconChrisCross hat folgendes geschrieben Zum zitierten Posting springen:
Ich hoffe was ich mir überlegt habe, kommt nicht ganz so stümperhaft herüber ;)

Entspricht dem Stand der Technik auf den meisten Selbstbau-Websites ... ;-)


Xearox - Sa 14.02.15 08:31

Also mein Denkansatz wäre folgender:

Mit deinem Delphi Programm auf den MySQL Server Verbinden und dort entsprechend den Usernamen und Passwort abfragen.

Das Passwort selbstverständlich als MD5 in der Datenbank hinterlegen das vom User eingegebene Passwort entsprechend mit dem in der Datenbank vergleichen.

Passt alles, kann man fortfahren. Stimmt was nicht, also ist das Passwort falsch, schickste einfach ein SWAT Team beim User vorbei, die Regeln das schon für dich ;-)

Aber im ernst, eine User und Passwort Liste auf einem Server packen ist keine gute Idee, vor allem nicht, wenn es sich um Klartext Passwörter handelt. Was machste, wenn dein Server gehackt wird und der Hacker dann diese Liste mit den ganzen Passwörtern sieht? Da freuen sich dann deine User :D

Also, wie schon oben geschrieben.

Login Daten auf MySQL Server schmeißen, mit deinem Delphi Programm entsprechend eine Datenbank Verbindung aufbauen, den Usernamen in der Datenbank suchen und das MD5 Passwort mit dem im Programm eingegeben Passwort vergleichen. Passt alles, kann der Zugang gewährt werden.

So ist es sicherlich sicherer als dein Weg.


BenBE - Mo 16.02.15 08:18

user profile iconXearox hat folgendes geschrieben Zum zitierten Posting springen:
Also mein Denkansatz wäre folgender:

Mit deinem Delphi Programm auf den MySQL Server Verbinden und dort entsprechend den Usernamen und Passwort abfragen.

M(

Und wie schützt Du den Login zur DB?

user profile iconXearox hat folgendes geschrieben Zum zitierten Posting springen:
Das Passwort selbstverständlich als MD5 in der Datenbank hinterlegen das vom User eingegebene Passwort entsprechend mit dem in der Datenbank vergleichen.

Nein!

MD5 macht man nicht, Salted MD5 nur, wenn man nicht weiß, was man tut und ansonsten nimmt man was geeignetes: SCrypt, wie ich oben bereits schrieb.

user profile iconXearox hat folgendes geschrieben Zum zitierten Posting springen:
Passt alles, kann man fortfahren. Stimmt was nicht, also ist das Passwort falsch, schickste einfach ein SWAT Team beim User vorbei, die Regeln das schon für dich ;-)

Ich freu mich schon auf das SWAT, weil die Verbindung abgerissen ist :mrgreen:

user profile iconXearox hat folgendes geschrieben Zum zitierten Posting springen:
Aber im ernst, eine User und Passwort Liste auf einem Server packen ist keine gute Idee, vor allem nicht, wenn es sich um Klartext Passwörter handelt.

Der erste (halbwegs) korrekte Satz in deinem Posting. ;-)

user profile iconXearox hat folgendes geschrieben Zum zitierten Posting springen:
Was machste, wenn dein Server gehackt wird und der Hacker dann diese Liste mit den ganzen Passwörtern sieht?

Das wird schon keinem Auffallen. Und die Chinesen freuen sich sicherlich darüber ;-)

user profile iconXearox hat folgendes geschrieben Zum zitierten Posting springen:
Da freuen sich dann deine User :D

Die natürlich auch! :twisted:

user profile iconXearox hat folgendes geschrieben Zum zitierten Posting springen:
Also, wie schon oben geschrieben.

Bei user profile iconXearox sieht man, wie man's möglichst nicht macht ;-)

user profile iconXearox hat folgendes geschrieben Zum zitierten Posting springen:
Login Daten auf MySQL Server schmeißen, mit deinem Delphi Programm entsprechend eine Datenbank Verbindung aufbauen, den Usernamen in der Datenbank suchen und das MD5 Passwort mit dem im Programm eingegeben Passwort vergleichen. Passt alles, kann der Zugang gewährt werden.

Und das Passwort zum MySQL wird Klartext über die Verbindung gejagt, damit der Angreifer gleich mitlesen kann ;-)

user profile iconXearox hat folgendes geschrieben Zum zitierten Posting springen:
So ist es sicherlich sicherer als dein Weg.

Nein.

P.S.: BSI TR-02102-1 v2015-01 [https://www.bsi.bund.de/cae/servlet/contentblob/477256/publicationFile/30924/BSI-TR-02102_V1_0_pdf.pdf] Kapitel 4 und 6.3, sowie dieses hier [https://www.dfn.de/fileadmin/3Beratung/Betriebstagungen/bt60/2AbsicherungWebanw-Rohr.pdf].


ChrisCross - So 22.02.15 15:02

Vielen Dank für eure Antworten, besonders von BenBE ;) ,

Zitat:


Sensible Daten wie Logins NIE! mit GET übertragen, da diese ansonsten ins Access Log des Servers geleakt werden (könnten).

Für sowas nimmt man immer POST.


und


Zitat:

Zitat:
Zweite Idee war eine Webseite mit einem Loginformular, das mit der Datenbank kommuniziert. Aus meinem Programm könnte ich die Felder ausfüllen und überprüfen ob der Login richtig war. Die Datensicherheit sieht hier aber wahrscheinlich noch schlechter aus.


Kommt drauf an. Simples REST, was einfach per POST zwei Parameter nimmt und mit 0 Bytes bei Fehler antwortet ist sicherlich sehr rudimentär, aber tut seinen Job. Und solange Du nicht bei Robert's Schule den Code klaust, sollte auch der Schutz der DB zu schaffen sein. BTW: MySQL kann für die Connection auch TLS, nutzt nur meist keiner.



Ich habe mich jetzt noch mal kurz mit beschäftig:

Delphi Code:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TForm1.btn1Click(Sender: TObject);
var
  data: TStringList;
  s: String;
begin
  data := TStringList.Create;
  try
    data.Values['name'] := 'test'
    data.Values['pwd'] := 'test';
    try
      s := IdHTTP1.Post('http://webseite.de/delphi-login.php', data);
      ShowMessage(s);
    except
      on E: Exception do
        ShowMessage('Fehler: ' + E.Message);
    end;
  finally
    data.Free;
  end;
end;


und PHP Code:


PHP-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
<?php 
session_start(); 
?> 

<?php 
$verbindung = mysql_connect("host""benutzer" , "kennwort"
or die("Verbindung zur Datenbank konnte nicht hergestellt werden"); 
mysql_select_db("datenbankname"or die ("Datenbank konnte nicht ausgewählt werden"); 

$username = $_POST["name"]; 
$passwort = md5($_POST["pwd"]); 

$abfrage = "SELECT Benutzername, Kennwort FROM tabelle WHERE Benutzername LIKE '$username' LIMIT 1"
$ergebnis = mysql_query($abfrage); 
$row = mysql_fetch_object($ergebnis); 

if($row->Kennwort == $passwort
    { 
    $_SESSION["username"] = $username
    
  echo 'LOGIN';
  exit;
    } 
else 
    { 
    echo 'FALSCH';
    } 

?>



Ich hoffe ihr reißt mit jetzt dafür nicht den Kopf ab :wink:

Sicherheit ist jetzt besser? Die Passwörter werden ja jetzt noch im Klartext versendet?! Also müsste ich das vorher noch verschlüsseln.


jfheins - So 22.02.15 16:39

Ohhhmmmm ... besser vielleicht, aber noch nicht gut ;-)

1. Du verwendest die alte mysql-Bibliothek in PHP. Du solltest besser mysqli einsetzen. (Im einfachsten Fall einfach alle Aufrufe um das 'i' ergänzen, ist aber Sicherheitstechnisch noch nicht so schlimm)
2. Du verwendest MD5? Falls du nicht PCs von 1992 zum berechnen verwendest, darf es ruhig etwas besseres sein.
Zitat:
Nein! MD5 macht man nicht

Mindestens sha1 verwenden! Besser noch (falls du ein halbwegs aktuelles PHP hast) SHA2, also z.B.:$passwort = hash('sha384'$_POST["pwd"]);

3. Und hier:

PHP-Quelltext
1:
Benutzername LIKE '$username'                    

Ich würde da das like streichen und stattdessen auf Gleichheit prüfen (ggf. case-insensitive) weil ich mit like a% schreiben kann und alle Benutzernamen matchen die mit a anfangen.

4. Wenn du die Kennwörter nicht im Klartext übertragen willst: Rufe zuerst eine PHP-Funktion auf, die eine Zufallszahl generiert. Diese wird in der Session gespeichert und zurückgegeben (nennen wir ihn 'token'). Im Delphi-Programm bildest du nun folgenden Hashwert:

Delphi-Quelltext
1:
data.Values['pwd'] := sha382(token + sha384('test'));                    

Am Server mit PHP holst du dir den token wieder aus der session und vergleichst so:

PHP-Quelltext
1:
2:
3:
$passwort = $_POST["pwd"]; 
// weiter unten
hash('sha384', token + $row->Kennwort) === $passwort


Die drei Gleichheitszeichen sind WICHTIG!!


ChrisCross - Di 24.02.15 16:22

Vielen vielen Dank für deine Hilfe,

du hast mir ganz doll weitergeholfen :)


Narses - Di 24.02.15 16:42

Moin!

user profile iconjfheins hat folgendes geschrieben Zum zitierten Posting springen:
1. Du verwendest die alte mysql-Bibliothek in PHP. Du solltest besser mysqli einsetzen. (Im einfachsten Fall einfach alle Aufrufe um das 'i' ergänzen, ist aber Sicherheitstechnisch noch nicht so schlimm)
Das würde ich konkret "sicherheitstechnisch" für völlig egal halten :P es sei denn, der MySQL-Server ist nicht auf der gleichen Maschine oder du rechnest damit, dass diese Maschine jeden Tag 3x gehacked wird. ;) Oder hast du konkrete Referenzen, was die Sicherheitsbedenken angeht (Links zur Hand)? :nixweiss:

Was du aber in dem Zusammenhang auch erwähnen solltest: wenn man mit mysqli arbeitet, sollte man auch mit persistenten Sessions [http://php.net/manual/en/mysqli.persistconns.php] arbeiten, sonst handelt man sich für jeden Request eine Straf-Sekunde Wartezeit ein... :shock:

cu
Narses


jfheins - Di 24.02.15 19:17

Dann weißt du da mehr als ich. :wink:

Ich habe irgendwo gelesen :les: , dass die mysql-Bibliothek (ohne i) veraltet sei, und man sie nicht mehr benutzen solle, und dass sie möglicherweise in Zukunft entfernt wird. :angel:

Konkreter also vielleicht "sicherheitstechnisch völlig egal, aber best practise" ?


Martok - Di 24.02.15 20:59

Mysqli ist vor allem deswegen sinnvoll, weil man damit Prepared Statements bekommt. Wenn man die dann auch konsequent benutzt, sind einfach keine SQL-Injections mehr möglich und man braucht sich über sehr viel Validation keine Gedanken mehr zu machen.

Das was user profile iconjfheins da unter "4." vorschlägt ist übrigens eine Variante eines Suche in Wikipedia HMAC und eine der wenigen Möglichkeiten, wie man ein Passwort prüfen kann ohne es an irgend einer Stelle gekannt haben zu müssen.