Login mit C



  • SeppJ schrieb:

    Daher dringende Verbesserungen: Bei der Eingabe Längenbegrenzungen setzen, guck in der Dokumentation von scanf nach, wie das geht. strcmp böte sich viel eher an als memcmp.

    Wenn nun beispielsweise die Passworteingabe mit Passwörtern aus einer Datenbank abgeglichen wird und ich die Passwortlänge auf 20 festsetze, der User hingegen nur ein Passwort von 15 Zeichen hat: besteht dann nicht dasselbe Problem, dass der User mehr Zeichen angeben kann und das Passwort als richtig angesehen wird?

    Ich habe nun mal strcmp anstelle von memcmp eingebaut und eine Zeichenbegrenzung eingebaut.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(){
    
        char user[] = "USER1";
        char pass[] = "PASS1";
        char password[20];
        char username[20];
    
        printf("Please enter your username: \n");
        scanf("%19s", username);
        printf("Wie ist dein Passwort?\n");
        scanf("%19s", password);
    
        if (strcmp(user, username) == 0 && strcmp(pass, password) == 0) {
            printf("Hallo %s.", user);
        } else {
            printf("Hast du dein Passwort vergessen?");
        }
    
        return 0;
    
    }
    

    Warum genau kann man das Passwort selbst festlegen, wenn zu viele Zeichen eingegeben werden und wie?



  • CrispyTurtleAlligator schrieb:

    Warum genau kann man das Passwort selbst festlegen, wenn zu viele Zeichen eingegeben werden und wie?

    Das liegt an der Anordnung der Variablen im Speicher.

    Lokale Variablen liegen i.A. auf dem Stack und der wächst von oben (hohe Adresse) nach unten.
    Neue Variablen liegen vor den Älteren.

    Bei wäre das dann

    username[20]        password[20]        pass[]user[]
    uuuuuuuuuuuuuuuuuuuuppppppppppppppppppppPASS1_USER1_
    

    u ist der Platz für username, p für password, die _ stehen für \0

    Wie du siehst, kannst du durch schreiben über die Arraygrenzen hinaus, ganz einfach die anderen Variablen ändern.



  • Ist es dann ausreichend, das Array für Passwort und Username auf 19 bei scanf zu begrenzen? Oder ist es vielleicht sogar sinnvoller fgets zu nutzen?



  • CrispyTurtleAlligator schrieb:

    Ist es dann ausreichend, das Array für Passwort und Username auf 19 bei scanf zu begrenzen?

    Ja, das reicht.

    Aber ...
    ... es ist sehr umständlich, wenn du mal die mögliche Länge der Eingaben verändern willst.

    CrispyTurtleAlligator schrieb:

    Oder ist es vielleicht sogar sinnvoller fgets zu nutzen?

    Das speichert das '\n' von der Entertaste mit ab.

    Zudem bleibt bei beiden Versionen der Rest von der Eingabe (die mehr als 19 Zeichen) noch im Eingabestrom stehen.
    Das gibt dann Probleme bei der nächsten Zeile.
    Das kann man aber auch umgehen: https://www.c-plusplus.net/forum/p1146014#1146014



  • Die Begrenzung auf 19 ist prinzipiell richtig, wenn auch hier nicht ausreichend.
    Ob scanf oder fgets ist Geschmackssache, wenn man den nötigen Überblick hat, was die Vor-/Nachteile sind.

    scanf("%19[^\n]",username); while( getchar()!='\n' );
    scanf("%19[^\n]",password); while( getchar()!='\n' );
    

    sollte für dich erstmal reichen



  • DirkB schrieb:

    CrispyTurtleAlligator schrieb:

    Ist es dann ausreichend, das Array für Passwort und Username auf 19 bei scanf zu begrenzen?

    Ja, das reicht.

    Aber ...
    ... es ist sehr umständlich, wenn du mal die mögliche Länge der Eingaben verändern willst.

    CrispyTurtleAlligator schrieb:

    Oder ist es vielleicht sogar sinnvoller fgets zu nutzen?

    Das speichert das '\n' von der Entertaste mit ab.

    Zudem bleibt bei beiden Versionen der Rest von der Eingabe (die mehr als 19 Zeichen) noch im Eingabestrom stehen.
    Das gibt dann Probleme bei der nächsten Zeile.
    Das kann man aber auch umgehen: https://www.c-plusplus.net/forum/p1146014#1146014

    Ist dies dann hiermit getan, bzw. reicht das aus um den Rest der Eingabe zu entfernen?

    scanf("%19[^\n]",username); while( getchar()!='\n' ); 
    scanf("%19[^\n]",password); while( getchar()!='\n' );
    

  • Mod

    Ja, das sollte alle realistischen Fälle abdecken.



  • Ist das identisch zu dem Folgenden?

    while( getchar()!='\n' ) {
    scanf("%19[^\n]",username); 
    } 
    while( getchar()!='\n' ) {
    scanf("%19[^\n]",password);
    }
    

    @SeppJ
    Was wären denn unrealistische Fälle?


  • Mod

    CrispyTurtleAlligator schrieb:

    Ist das identisch zu dem Folgenden?

    😕 Nein, natürlich nicht, das ist ganz was anderes. Verstehst du nicht, was das while(bedingung); macht? Das ist einfach nur eine Schleife mit einem leeren Körper. Da hier bereits die Prüfung der Schleifenbedingung den gewünschten Effekt hat.

    @SeppJ
    Was wären denn unrealistische Fälle?

    Die ganze Idee, Passwörter einfach von der Standardeingabe zu lesen ist halt nicht besonders gut. Die Standardeingabe ist nicht zwangsläufig an eine Tastatur gebunden. Und wenn sie es nicht ist, dann funktioniert dein interaktives Eingabekonzept nicht mehr so wirklich. Wenn du interaktive Nutzerschnittstellen möchtest, dann brauchst du letztlich eine GUI oder TUI oder etwas vergleichbares. Aber das würde an dieser Stelle viel zu weit führen. Für ein paar Experimente ist das was du hier machst bei weitem ausreichend.



  • SeppJ schrieb:

    CrispyTurtleAlligator schrieb:

    Ist das identisch zu dem Folgenden?

    😕 Nein, natürlich nicht, das ist ganz was anderes. Verstehst du nicht, was das while(bedingung); macht? Das ist einfach nur eine Schleife mit einem leeren Körper. Da hier bereits die Prüfung der Schleifenbedingung den gewünschten Effekt hat.

    @SeppJ
    Was wären denn unrealistische Fälle?

    Die ganze Idee, Passwörter einfach von der Standardeingabe zu lesen ist halt nicht besonders gut. Die Standardeingabe ist nicht zwangsläufig an eine Tastatur gebunden. Und wenn sie es nicht ist, dann funktioniert dein interaktives Eingabekonzept nicht mehr so wirklich. Wenn du interaktive Nutzerschnittstellen möchtest, dann brauchst du letztlich eine GUI oder TUI oder etwas vergleichbares. Aber das würde an dieser Stelle viel zu weit führen. Für ein paar Experimente ist das was du hier machst bei weitem ausreichend.

    Ich bin nur ein wenig überrascht von der Stelle, an der die Schleife genutzt wird. So habe ich das nämlich bisher noch nirgends gesehen. 🙂

    Kannst du mir den Unterschied zwischen den beiden Codezeilen erklären?


  • Mod

    CrispyTurtleAlligator schrieb:

    Kannst du mir den Unterschied zwischen den beiden Codezeilen erklären?

    Habe ich doch.

    In C ist das Semikolon das Trennzeichen für Anweisungen. Zeilenumbrüche dienen nur der Lesbarkeit für Menschen. Wenn es dir besser passt, kannst du Wutz Code auch so schreiben:

    scanf("%19[^\n]",username); 
    while( getchar()!='\n' );
    scanf("%19[^\n]",password); 
    while( getchar()!='\n' );
    

    Nun verständlicher? Beachte meine Einrückung, beziehungsweise das Ausbleiben derselben. Alle diese Anweisungen sind auf der gleichen logischen Ebene. Der Schleifenkörper der while-Schleifen ist leer, die scanfs haben überhaupt nichts mit den whiles zu tun. Und das ist selbstverständlich ganz was anderes als dein Code, bei dem du scanfs innerhalb des Körpers von while-Schleifen hast.

    Oder noch einmal deutlicher:

    scanf("%19[^\n]",username); 
    while( getchar()!='\n' ) {
    }
    scanf("%19[^\n]",password); 
    while( getchar()!='\n' ) {
    }
    


  • Wobei für mich aber noch die Frage bleibt: Warum wird dann der Rest der Eingabe dadurch entfernt?



  • scanf hinterlässt immer das ungelesene '\n' in stdin und 'davor' die evtl. nicht gelesenen Zeichen, wenn mehr als 19 Zeichen eingegeben wurden.
    Mit while wird getchar() solange aufgerufen und damit jeweils genau 1 beliebiges Zeichen abgeräumt, bis '\n' vorkommt und somit alle Zeichen aus stdin 'entfernt' wurden, damit diese Restzeichen das folgende scanf nicht stören.



  • Pwned:

    Please enter your username:
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    Wie ist dein Passwort?
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    Hallo USER1.

    Shit happens. 😉

    (Tested and verified - VS 2005)



  • Bleibe in der Schleife, solange das (neu) gelesene Zeichen ungleich dem Zeilenende ist.

    Es wird solange gelesen, solange das ZEichen von der Entertaste nicht gefunden wurde.



  • EOP schrieb:

    Pwned:

    Please enter your username:
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    Wie ist dein Passwort?
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    Hallo USER1.

    Shit happens. 😉

    (Tested and verified - VS 2005)

    Bei meinem zuletzt geposteten Code ist das zumindest bei mir nicht mehr möglich.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(){
    
        char user[] = "USER1";
        char pass[] = "PASS1";
        char password[20];
        char username[20];
    
        printf("Please enter your username: \n");
        scanf("%19s", username);
        printf("Wie ist dein Passwort?\n");
        scanf("%19s", password);
    
        if (strcmp(user, username) == 0 && strcmp(pass, password) == 0) {
            printf("Hallo %s.", user);
        } else {
            printf("Hast du dein Passwort vergessen?");
        }
    
        return 0;
    
    }
    

    Wie siehts bei anderen aus?


  • Mod

    Es sollte nicht mehr möglich sein. Aber EOPs Beitrag zeigt sehr schön, dass mein Einwand, dass dies möglich wäre, durchaus berechtigt und keine rein theoretische Panikmache war. Prüfungen (nicht nur der Länge) von Eingaben aller Art (also auch Daten aus Dateien usw.) sind extrem wichtig in C. Das war früher der Fehler schlechthin in Computersystemen, da viel mit C gearbeitet wurde und viele Programmierer diese Prüfungen nicht gemacht haben. Selbst heute noch sind viele schwere Sicherheitslücken auf diese Art von Fehler zurück zu führen. C vertraut blind auf den Programmierer und wenn der Programmierer dem Nutzer blind vertraut (oder der Programmierer es nicht besser kann), dann hat man ein ganz großes Problem, wenn der Nutzer böses will.

    Das gilt prinzipiell natürlich auch in anderen Sprachen, wobei die Symptome aber oft andere sein können. Jedenfalls muss alles was irgendwie von außen in ein Programm kommt immer als ein mögliches Vehikel für einen Angriff angesehen werden und entsprechend kritisch behandelt werden.



  • CrispyTurtleAlligator schrieb:

    Bei meinem zuletzt geposteten Code ist das zumindest bei mir nicht mehr möglich.

    Ja, das war auf deine erste Version bezogen.
    Nur eine Demo, was SeppJ und mir so auf Anhieb einfällt und dann auch noch funktioniert.
    Hatte vorher keine Zeit dafür.

    EDIT:
    Und jetzt kannst du dir überlegen wie ich USER1 wurde.



  • [quote="EOP"]

    CrispyTurtleAlligator schrieb:

    EDIT:
    Und jetzt kannst du dir überlegen wie ich USER1 wurde.

    Ich habe ehrlich gesagt keinen blassen Schimmer.


  • Mod

    CrispyTurtleAlligator schrieb:

    Ich habe ehrlich gesagt keinen blassen Schimmer.

    Versuch doch mal mit zu denken. Alles was du brauchst, wurde bereits mehrmals gesagt. Die Erklärung ist eine Kombination aus diesen beiden Antworten:

    DirkB schrieb:

    CrispyTurtleAlligator schrieb:

    Warum genau kann man das Passwort selbst festlegen, wenn zu viele Zeichen eingegeben werden und wie?

    Das liegt an der Anordnung der Variablen im Speicher.

    Lokale Variablen liegen i.A. auf dem Stack und der wächst von oben (hohe Adresse) nach unten.
    Neue Variablen liegen vor den Älteren.

    Bei wäre das dann

    username[20]        password[20]        pass[]user[]
    uuuuuuuuuuuuuuuuuuuuppppppppppppppppppppPASS1_USER1_
    

    u ist der Platz für username, p für password, die _ stehen für \0

    Wie du siehst, kannst du durch schreiben über die Arraygrenzen hinaus, ganz einfach die anderen Variablen ändern.

    DirkB schrieb:

    In Zeile 18 solltest du den richtigen Namen (aus user) ausgeben. Dann geht das auch, wenn du den Namen mal änderst
    In Zeile 20 solltest du den keinen Namen ausgeben. Sonnst kennt der Hacker nach dem ersten Fehlversuch den Namen.

    Dein Programm mal ein bisschen umgeschrieben, so dass es deutlicher wird:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(){
    
        char user[] = "USER1";
        char pass[] = "PASS1";
        char password[20];
        char username[20];
    
        printf("Please enter your username: \n");
        scanf("%s", username);
        printf("Wie ist dein Passwort?\n");
        scanf("%s", password);
    
        if (memcmp(user, username, sizeof(user)) == 0 && memcmp(pass, password, sizeof(pass))== 0) {
            printf("Hallo %s. Dein gespeichertes Passwort ist %s", user, pass); // Hier sollten ja eigentlich "USER1" und "PASS1" ausgegeben werden
        } else {
            printf("Anmeldung fehlgeschlagen.\n"); 
        }
    
        return 0;   
    }
    

    GCC 4.8, 64 Bit (mit -fno-stack-protector, sonst funktioniert es nicht :p ):

    Please enter your username: 
    username12345678901234567890123456789012345678901234567890123456hackedpassword''username
    Wie ist dein Passwort?
    hackedpassword''username
    Hallo username. Dein gespeichertes Passwort ist hackedpassword''username
    

    Nanu? Anscheinend steht in user gar nicht mehr "USER1" und in pass steht nicht mehr "PASS1". wie das bloß passiert ist? 🕶

    (Man sieht auch, dass die genauen Details, was die Stackanordnung angeht, vom Compiler und System abhängig ist, aber nach ein paar Versuchen hat man es raus)


Anmelden zum Antworten