steckl: sicherheit bei perl/cgi skript mit Anmeldefunktion

Hallo,

ich habe ein Perl-Script (Web-Applikation) mit Zugriff auf eine mySql-Datenbank auf einem Apache-Server laufen, bei dem User auf bestimmte (fuer sie bestimmte) Daten zugreifen koennen. Es wird unterschieden zwischen Daten fuer einzelne User und Daten fuer ganze User-Gruppen.
Um festlegen zu koennen welche Daten angezeigt werden sollen habe ich auf der Startseite ein Formular wo die User Login und Passwort eingeben muessen.
Der Login und das verschluesselte Passwort werden dann mit der Datenbank verglichen.
Wenn diese falsch sind kommt erneut die Startseite mit einer Fehlermeldung. Andernfalls wird mit folgender Funktion eine Sessin-Id generiert:

  
sub getSid  
{  
    my $zeit = time;  
    my @sidChars = (0..9,'B'..'Z'); # Zeichen fuer SID  
    my $pid = $$;  
  
    srand($zeit);  
  
    my $sid = "";  
  
    for (my $i = 0; $i++ < 10;)  
    {  
        $sid .= $sidChars[rand(@sidChars)];  
    }  
    $sid = "${pid}A$sid";  
  
    return ($sid)  
}  

Fuer die SID wird jetzt ein SID-File auf dem Server angelegt. Hier steht u.a. die USER-ID und die Gruppen-ID. Alle Scripts lesen jetzt hier die User-Daten aus und ueberpruefen wenn noetig ob der User Zugriffsrechte fuer die anzuzeigenden Datensaetze hat.
Diese Session-Id wird jetzt immer bei allen Links und Formularen (mit hidden Feld) weitergegeben und jedesmal auf gueltige Zeichen und korrekte Laenge ueberprueft.
Die Files zu den einzelnen SIDs werden entweder durch Klick auf einen Logout-button geloescht, oder wenn sie laenger als eine Stunde nicht veraendert wurden (wird vor jedem Zugriff abgeprueft und dann gegebenenfalls geloescht).

Jetzt wuerde mich folgendes interessieren:

  • ist das Loesungskonzept einigermassen sicher?
  • Waere es ein Sicherheitsrisiko, die Login-Daten und/oder die SID in Cookies abzuspeichern?
  • ist die SID eindeutig, bzw. nicht von aussen nachvollziehbar oder leicht knackbar?
  • gibt es sonst noch irgendetwas zu beachten?
  • wie wird es bei anderen Webseiten gemacht? auch so aehnlich wie ich es mache, oder ganz anders?

mfG,
steckl

  1. Hell-O!

    • ist das Loesungskonzept einigermassen sicher?

    Ja, sieht so aus.

    • Waere es ein Sicherheitsrisiko, die Login-Daten und/oder die SID in Cookies abzuspeichern?

    Wenn das Cookie clientseitig nicht akzeptiert wird, besteht keine Möglichkeit der Zuordnung.

    • ist die SID eindeutig, bzw. nicht von aussen nachvollziehbar oder leicht knackbar?

    Christian Kruse hatte mir mal eine Subroutine zur Verfügung gestellt:

    sub _create_SID {  
      my $rmid = $ENV{HTTP_X_FORWARDED_FOR} || $ENV{REMOTE_ADDR} || '654.546.654.546';  
      $rmid =~ tr/./0/;  
      $rmid = pack("a[12]",$rmid);  
      my @chars = split // => 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789-_';  
      my $id = '';  
      $id .= $chars[$_] foreach split // => $rmid;  
      $id .= $chars[rand @chars] for 1..4;  
      return $id;  
    }
    

    Die scheint mir relativ sicher zu sein, zum Einsatz kommt sie bei mir in diesem Package.

    • gibt es sonst noch irgendetwas zu beachten?

    Ja, es gibt eine Latte von Session-Modulen auf cpan.org, vielleicht nehmen die dir ein Stück Arbeit ab.

    Siechfred

    --
    Ich bin strenggenommen auch nur interessierter Laie. (molily)
    Kabelkuddelmuddel || Steuerfreie Geburtsbeihilfen?  || RT 221 Erfurt-Altstadt i.V.
    1. hallo,

      erstmal danke fuer die (wie immer) schnelle antwort

      • ist das Loesungskonzept einigermassen sicher?

      Ja, sieht so aus.

      :-)

      • Waere es ein Sicherheitsrisiko, die Login-Daten und/oder die SID in Cookies abzuspeichern?
        Wenn das Cookie clientseitig nicht akzeptiert wird, besteht keine Möglichkeit der Zuordnung.

      Ist es den Aufwand wert, eine Fallunterscheidung einzubauen?

      • ist die SID eindeutig, bzw. nicht von aussen nachvollziehbar oder leicht knackbar?

      Christian Kruse hatte mir mal eine Subroutine zur Verfügung gestellt:

      sub _create_SID {

      my $rmid = $ENV{HTTP_X_FORWARDED_FOR} || $ENV{REMOTE_ADDR} || '654.546.654.546';
        $rmid =~ tr/./0/;
        $rmid = pack("a[12]",$rmid);
        my @chars = split // => 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789-';
        my $id = '';
        $id .= $chars[$
      ] foreach split // => $rmid;
        $id .= $chars[rand @chars] for 1..4;
        return $id;
      }

      heisst das, dass meine routine unsauber ist? oder ist das nur eine andere alternative dazu?  
      
      > Die scheint mir relativ sicher zu sein, zum Einsatz kommt sie bei mir in [diesem Package](http://test.anaboe.net/DB_Session.txt).  
      
      Wie ich sehe verwendest du fuer die Verwaltung der SID keine Files sondern eine Datenbank.  
      Das ist schneller, als ein Textfile, oder? und wohl noch etwas sicherer?  
      Sollte ich wohl auch so machen.  
        
      und noch ne Frage zu deinem Modul:  
      du verwendest in jeder routine mit DB-zugriff DB\_connect() und DB\_close(). waere es nicht besser die Datenbank gleich am anfang zu verbinden, dann ein Db-Handle an die einzelnen Subroutinen zu uebergeben und erst am Schluss die Verbindung wieder zu trennen? Kann aber auch gut sein, dass ich hier falsch liege.  
        
      
      > > - gibt es sonst noch irgendetwas zu beachten?  
      >   
      > Ja, es gibt eine [Latte von Session-Modulen auf cpan.org](http://search.cpan.org/search?query=Session&mode=all), vielleicht nehmen die dir ein Stück Arbeit ab.  
      
      zu spaet ... hab ja schon alles ausprogrammiert. Aber war ne gute Uebung ;)  
        
      danke,  
      steckl  
        
      
      
      1. Hell-O!

        Ist es den Aufwand wert, eine Fallunterscheidung einzubauen?

        Kommt drauf an, wie wichtig die Session ist. Ggf. kann man abfragen, ob ein Cookie gesetzt ist, und wenn nicht auf die dann fehlende Funktionalität hinweisen.

        • ist die SID eindeutig, bzw. nicht von aussen nachvollziehbar oder leicht knackbar?
          Christian Kruse hatte mir mal eine Subroutine zur Verfügung gestellt:
          heisst das, dass meine routine unsauber ist? oder ist das nur eine andere alternative dazu?

        Letzteres :-)

        Ich war seinerzeit, ähnlich wie du, auf der Suche nach einem Algorithmus zum Generieren einer möglichst eindeutigen SID. Ich habe dir den Archivthread mal rausgesucht:

        http://forum.de.selfhtml.org/archiv/2005/1/t98234/

        Wie ich sehe verwendest du fuer die Verwaltung der SID keine Files sondern eine Datenbank.

        Ja, war damals eine Fingerübung: Mein erstes Modul kombiniert mit der ersten DB-Anwendung. Von der Performance her kommt es darauf an, wieviele Sessions du verwalten willst. Naja, und bei einer DB musst du dir um Locking und ähnliche Probleme keine Gedanken machen.

        du verwendest in jeder routine mit DB-zugriff DB_connect() und DB_close(). waere es nicht besser die Datenbank gleich am anfang zu verbinden, dann ein Db-Handle an die einzelnen Subroutinen zu uebergeben und erst am Schluss die Verbindung wieder zu trennen?

        Gedacht war das Modul für eine Weblog-Software in Perl. Da war der Ablauf so, dass beim ersten Seitenzugriff eine Session erzeugt wurde, die bei weiteren Aufrufen abgefragt und ggf. mit Werten befüllt wurde. Es gibt also mehrere verschiedene Varianten. Angenommen, jemand ruft die Seite auf, ohne irgendwelche Aktionen vornehmen zu wollen. Dann wird lediglich die Session abgefragt und gut. Würde ich die DB nicht ordnungsgemäß schließen, würde ich das dem System überlassen - das fände ich nicht so gut. Und da ich beim ersten Abfragen nicht weiß, ob ich überhaupt noch was in der Session ablegen will, schließe ich sie eben. Soll dann doch was in die Session geschrieben werden, muss ich die DB eben wieder öffnen. Ob das jetzt der eleganteste Weg ist, kann ich dir nicht sagen :-)

        Siechfred

        --
        Ich bin strenggenommen auch nur interessierter Laie. (molily)
        Kabelkuddelmuddel || Steuerfreie Geburtsbeihilfen?  || RT 221 Erfurt-Altstadt i.V.
        1. Hallo,

          Kommt drauf an, wie wichtig die Session ist. Ggf. kann man abfragen, ob ein Cookie gesetzt ist, und wenn nicht auf die dann fehlende Funktionalität hinweisen.

          Das werd ich dann wohl (vorerst) bleiben lassen. Dann muessen sich die User eben  jedesmal neu einloggen. Dafuer gibts ja eh den Passwortmanager vom Browser falls jemand darauf wert legt.

          heisst das, dass meine routine unsauber ist? oder ist das nur eine andere alternative dazu?
          Letzteres :-)

          :-)

          Ich war seinerzeit, ähnlich wie du, auf der Suche nach einem Algorithmus zum Generieren einer möglichst eindeutigen SID. Ich habe dir den Archivthread mal rausgesucht:

          http://forum.de.selfhtml.org/archiv/2005/1/t98234/

          hab deine routine mal ausprobiert, aber bekomm immer ne Fehlermeldung:
          Invalid type in pack: '['
          ich verwende Version 5.005_02. ist das der Grund?

          mfG,
          steckl

          1. Hell-O!

            hab deine routine mal ausprobiert, aber bekomm immer ne Fehlermeldung:
            Invalid type in pack: '['

            Versuche mal:

            $rmid = pack("a12", $rmid);

            Ansonsten ist 5.005_02 AFAIK schon ziemlich angestaubt, aber ich habe da eine alte Dokumentation ergoogelt :-)

            Siechfred

            --
            Ich bin strenggenommen auch nur interessierter Laie. (molily)
            Kabelkuddelmuddel || Steuerfreie Geburtsbeihilfen?  || RT 221 Erfurt-Altstadt i.V.
            1. Hi,

              hab deine routine mal ausprobiert, aber bekomm immer ne Fehlermeldung:
              Invalid type in pack: '['

              Versuche mal:

              $rmid = pack("a12", $rmid);

              danke, so klappts :)

              cu,
              steckl

  2. Hallo,

    • gibt es sonst noch irgendetwas zu beachten?
    • wie wird es bei anderen Webseiten gemacht? auch so aehnlich wie ich es mache, oder ganz anders?

    im Prinzip wirds überall so gemacht, der user macht ein Login, gibt seine Credentials ein und die Session wird aufgebaut. Die ist solange gültig, wie die Session ID (SID) auf dem Server mit der SID übereinstimmt, welche der user bei jedem Request mitsendet. Weitere sinnvolle Einschränkungen der Gültigkeit sind Ablaufzeiten und|oder Sessions die nur für eine Browsersitzung gelten.

    Ob diese SID über einen Cookie oder über einen Parameter mitgegeben wird, ist sicherheitsrelevant völlig egal. Es ist jedoch so, dass ein Parameter leichter manipulierbar ist als ein Cookie im HTTP-Header des Requests.

    Auf jeden Fall ist und bleibt neben einer absolut eindeutigen und nicht reproduzierbaren SID das Login der Knackpunkt in der Frage ob eine Session überhaupt geklaut werden kann oder nicht. Die Lösung hierzu lautet SSL (Secure Socket Layer), bekannt auch als HTTPS.

    --roro

    1. Hallo,

      Weitere sinnvolle Einschränkungen der Gültigkeit sind Ablaufzeiten und|oder Sessions die nur für eine Browsersitzung gelten.

      was heißt eine browsersitzung? heißt das, dass die selbe adresse (die auch die SID enthaelt) in einem neuen Browserfenster nicht funktioniert?

      Auf jeden Fall ist und bleibt neben einer absolut eindeutigen und nicht reproduzierbaren SID das Login der Knackpunkt in der Frage ob eine Session überhaupt geklaut werden kann oder nicht. Die Lösung hierzu lautet SSL (Secure Socket Layer), bekannt auch als HTTPS.

      Das hängt nicht von den Skripts, sondern von der Serverkonfiguration ab, oder?

      mfG,
      steckl

  3. Hallo steckl,

    nachdem ich in den letzten 5 Jahren nach und nach ein ausgereiftes Session Handling entwickelt habe, habe ich hier noch ein paar Cent hinzuzufügen.

    Dein grundsätzlicher Ansatz ist richtig, es wird überall so gemacht. Deine Funktion eine Session ID zu generieren sieht für mich grundsätzlich OK aus. Mach doch mal den ultimativen Test und generiere so schnell es geht hintereinander 1 Million Session IDs und schreib sie in eine mysql Tabelle. Du kriegst dann erstmal ein Gefühl für die Performance und danach machst du noch ein SELECT DISTINCT um festzustellen, ob wirklich alle 1 Million IDs unterschiedlich geworden sind. Wenn die Routine diesen Test besteht, ist sie m.E. nach ausreichend sicher.

    Fuer die SID wird jetzt ein SID-File auf dem Server angelegt. Hier steht u.a. die USER-ID und die Gruppen-ID. Alle Scripts lesen jetzt hier die User-Daten aus und ueberpruefen wenn noetig ob der User Zugriffsrechte fuer die anzuzeigenden Datensaetze hat.

    Wie jetzt. Für jede einzelne Session legst du eine eigene Datei an? Also das würde ich nicht so machen. Es erscheint mir unhandlich und seeehr langsam. Legt doch lieber eine mysql Tabelle für die Sessions an, oder noch besser, benutze BerkleyDB. Schau dir mal das DB_File Perl Modul an. Man kann damit einen Hash an eine BerkleyDB Datei "tie"-en. Das ist sehr elegant zu handhaben, ratten schnell und als Bonus wird deine Sessionverwaltung auch noch unabhängig von einer Datenbank (als Vergleich zu der mysql Lösung).

    Jetzt wuerde mich folgendes interessieren:

    • ist das Loesungskonzept einigermassen sicher?

    Einigermassen. Es wäre noch sinnvoll zumindest die IP Adresse des Clients mit der Session abzuspeichern. Wenn sich die IP Adresse während der Session ändert, ist das ein Zeichen für ein Session Hijacking Versuch. Dann solltest du die Session zerstören und den User neu einloggen lassen.

    • Waere es ein Sicherheitsrisiko, die Login-Daten und/oder die SID in Cookies abzuspeichern?

    Nein ganz im Gegenteil. Du kannst ein Cookie verwenden, um die Sicherheit noch weiter zu erhöhen. Hast du bei einem eingeloggten User erfolgreich die Session ID in einen Cookie schreiben können und stimmt irgendwann die Session ID im Cookie nicht mit der im URL (bzw. per POST) übergebenen Session ID überein, ist irgendwas faul und du solltest die Session ausloggen.

    • ist die SID eindeutig, bzw. nicht von aussen nachvollziehbar oder leicht knackbar?

    Jemand könnte Versuchen selbst so eine Session ID zu generieren und hoffen dass er eine erwischt, die gerade aktiv ist. So ein jemand sollte aber lieber Lotto spielen, da sind seine Chancen auf Gewinn wesentlich höher. Viel einfacher könnte sich jemand eine aktive Session ID ersniffen und sich damit an deine Seite wenden und somit eine Session effektiv klauen (Session Hijacking). Dagegen helfen die oben beschriebenen Methoden ein wenig, wobei aber ein guter Hijacker locker auch den Cookie vorgaukeln und auch eine passende IP Adresse vortäuschen kann. Dagegen hilft nur noch eine SSL Verschlüsselung über die gesamte Sitzung hinweg, aber das brauchst du wirklich erst wenn es so richgtig um Geld geht.

    Gruß,
    Cruz