Mark: webapplication Konzeptfrage Sessionverwaltung

Moin!

Für mein Script nutze ich eine SessionID zur Verfolgung der Session (ohne Cookies, nur per GET). Gut und schön. Nun habe ich das Problem, das ich in meinem Script, in dem Fall um den es geht, eine Ein-Benutzer-Lizenz abbilden muss. Sprich, es soll immer nur ein Client auf die Applikation zugreifen können. Intern habe ich eine Usertabelle in der DB, in welcher die SessionID des gerade aktiven Users abgespeichert ist - so kann ich u.a. kontrollieren, das immer nur ein Client aktiv ist. Theoretisch. Denn ein halbwegs gewiefter User bräuchte seinem Arbeitskollegen ja nur die SessionID mitteilen und dann könnten quasi 2, bzw. beliebig viele Clients, mit der gleichen Session aktiv sein. Weiß jemand wie man das Problem umgehen kann? Gibts es Lösungsansätze? Ich müsste also irgendwie den Client eindeutig identifizieren können. Ich habe auch schon daran gedacht, in der Usertabelle zusätzlich die IP mit abzuspeichern, aber das dürfte wohl kaum zuverlässig funktionieren....

Im Idealfall würde das dann so aussehen, das Benutzer 1 eingeloggt ist und die Applikation nutzt und Benutzer 2, welcher Clever ist und ohne Einloggvorgang einfach die SessionID von Benutzer 1 an die URL anhängt, abgelehnt wird..

Wer weiß Rat?

Gruß
Mark

  1. Hallo,

    auf die Gefahr hin, daß ich Dich falsch verstanden habe: mach das System doch einfach dicht (Variable in kleines Textfile schreiben, o.ä.), sobald jemand angemeldet ist und gib´s frei, wenn sich derjenige abgemeldet hat, oder eine Stunde inaktiv ist.

    Gruß, Lirpa

    1. Hallo Lirpa,

      sei mir bitte nicht böse, aber auch nach mehrmaligem Lesen Deines Postings habe ich nicht verstanden, worauf Du hinaus willst, bzw, wie ich Deinen Hinweis verstehen soll..

      Gruß
      Mark

      1. Hallo Mark,

        möglicherweise sind Semaphoren gemeint.
        Schreibe etwas in eine Datei oder DB-Tabelle o.ä.,
        wenn sich jemand anmeldet.
        Lösche das wieder, wenn er sich abmeldet.

        Wenn eine Anmeldung kommt, fragst du ab, ob der Eintrag (z.B. in der DB) vorhanden ist,
        wenn ja, verweigerst du die Anmeldung,
        wenn nein gehts von vorne los.

        Das geht allerdings dann schief, wenn der Nutzer die Abmeldung
        vergisst. Deshalb schlägt Lirpa vor zu löschen, wenn der Nutzer längere Zeit nicht aktiv war.

        Gruß Mia

        1. Hi Mia,

          jetzt habe auch ich es geschnallt ;-) Hoffe ich zumindest. Das klingt an und für sich ganz gut, das Problem sehe ich nur darin, das ein Benutzer sich gar nicht anmelden muss, wenn er die SessionsID eines bereits angemeldeten Users benutzt... Er wird natürlich als bereits angemeldet betrachtet, sobald er eine gültige SessionID benutzt.... Oder steh ich jetzt völlig auf der Leitung? lol

          Gruß
          Mark

          1. Hallo Mark,

            da hast du schon recht. Die Lösung ist sicher nicht wasserdicht.
            War ein Versuch des Erklärung.

            Viel Erfolg
            Mia

  2. Halihallo Mark

    Für mein Script nutze ich eine SessionID zur Verfolgung der Session (ohne Cookies, nur per GET). Gut und schön. Nun habe ich das Problem, das ich in meinem Script, in dem Fall um den es geht, eine Ein-Benutzer-Lizenz abbilden muss. Sprich, es soll immer nur ein Client auf die Applikation zugreifen können.

    Nur um die Aufgabe richtig verstehen zu können:
    Ein User hat Benutzer-Id und -Passwort und kann sich zu einer
    bestimmten Zeit nur an einem Computer einloggen. Ist es hierbei
    wichtig, dass der Benutzer nur von *genau* einem Client auf deinen
    Service zugreifen kann? - Wenn ja: vergiss es, leider. Es geht nicht.
    Ein Computer lässt sich nicht eindeutig identifizieren, zumindest
    nicht über HTTP, TCP/IP.

    Falls es nur darum geht, dass ein Benutzer während seiner Session
    an einem Computer sitzen muss und den Service von keinem anderen
    (über Kopieren der SessionId) Computer aus aufgerufen werden kann
    (mit dem selben Login). Dann jain. Dies geht bedingt. Da HTTP selbst
    Verbindungslos ist, kannst du niemals sicherstellen, dass der Request
    von demselben Computer ausgelöst wurde. Wohl aber kannst du
    sicherstellen, dass die Wahrscheinlichkeit klein ist, dass von zwei
    Computern zur selben Zeit eine Anfrage kommen kann.

    Intern habe ich eine Usertabelle in der DB, in welcher die SessionID des gerade aktiven Users abgespeichert ist - so kann ich u.a. kontrollieren, das immer nur ein Client aktiv ist. Theoretisch. Denn ein halbwegs gewiefter User bräuchte seinem Arbeitskollegen ja nur die SessionID mitteilen und dann könnten quasi 2, bzw. beliebig viele Clients, mit der gleichen Session aktiv sein.

    Faktum: Du kannst den Client nicht sicher zu 100% identifizieren.
    Faktum: HTTP ist Verbindungslos
    Faktum: Clients/Parameter/Verbindungen können simuliert/emuliert
            werden => Clientcomputer kann wechseln ohne dass dein Server
            dies merkt.

    Fazit: wie oben bereits beschrieben kannst du mit grosser
    Wahrscheinlichkeit verhindern, dass zwei Computer mit demselben Login
    zur selben Zeit aktiv sein können. Dies funktioniert wie folgt:

    Wenn sich ein Benutzer einloggt, wird eine SessionId generiert. Diese
    ändert jedoch bei jedem Zugriff. Wenn der Benutzer also die aktuelle
    SessionId (oder in diesem Zusammenhang wohl treffender: RequestId)
    dem "Nachbarn" weitergibt, könnte dieser zwar weiter den Dienst
    benutzen, aber du würdest dich selber aus dem Dienst "ausschliessen",
    da mit jeder RequestId lediglich ein Request angefordert werden kann.
    Sobald ein Service (PHP-Seite) durch eine RequestId angefordert
    wurde, wird diese RequestId deaktiviert und die nächste RequestId
    generiert (und ggf. an alle Links und Formulare reingeschrieben).

    Bei diesem Vorgehen gibt es jedoch grosse Probleme:
    Man denke nur an die Situation, dass jemand auf die "Zurück"-Taste
    des Browsers drückt und das Formular erneut absendet: Die im Formular
    hardkodierte RequestId ist ja bereits deaktiviert und die Folge:
    der User wird automatisch ausgeloggt...

    Weiß jemand wie man das Problem umgehen kann? Gibts es Lösungsansätze? Ich müsste also irgendwie den Client eindeutig identifizieren können. Ich habe auch schon daran gedacht, in der Usertabelle zusätzlich die IP mit abzuspeichern, aber das dürfte wohl kaum zuverlässig funktionieren....

    Richtig. Genausowenig zuverlässig ist meine obengenannte Lösung.
    Es gibt meiner Meinung nach auch keine zuverlässige Lösung über
    HTTP... Ggf. solltest du über ein Clientseitiges Programm nachdenken,
    dass eine persistente Verbindung zum Server öffnet, damit wäre es
    dann sogar relativ einfach umzusetzen...

    Im Idealfall würde das dann so aussehen, das Benutzer 1 eingeloggt ist und die Applikation nutzt und Benutzer 2, welcher Clever ist und ohne Einloggvorgang einfach die SessionID von Benutzer 1 an die URL anhängt, abgelehnt wird..

    Naja, "Ideal" wäre es, aber technisch Umsetzbar? - IMHO nein, nicht
    100% ;-)

    Viele Grüsse

    Philipp

    1. Hallo Philipp,

      Ist es hierbei
      wichtig, dass der Benutzer nur von *genau* einem Client auf deinen
      Service zugreifen kann? - Wenn ja: vergiss es, leider. Es geht nicht.
      Ein Computer lässt sich nicht eindeutig identifizieren, zumindest
      nicht über HTTP, TCP/IP.

      Falls es nur darum geht, dass ein Benutzer während seiner Session
      an einem Computer sitzen muss und den Service von keinem anderen
      (über Kopieren der SessionId) Computer aus aufgerufen werden kann
      (mit dem selben Login). Dann jain. Dies geht bedingt. Da HTTP selbst
      Verbindungslos ist, kannst du niemals sicherstellen, dass der Request
      von demselben Computer ausgelöst wurde. Wohl aber kannst du
      sicherstellen, dass die Wahrscheinlichkeit klein ist, dass von zwei
      Computern zur selben Zeit eine Anfrage kommen kann.

      »»

      Hmm, eigentlich soll eben immer nur ein User zur Zeit aktiv sein können. Ähhh, ja, hmm, wie sag ich das denn nun? Also sobald in meiner User-Tabelle ein Benutzer vorhanden ist, sprich eingeloggt ist, soll sich kein weiterer einloggen können und auch nicht das Login des ersten Benutzers verwenden dürfen (eben z.B. durch Kopieren der SessionID)

      Dies funktioniert wie folgt:
      Wenn sich ein Benutzer einloggt, wird eine SessionId generiert. Diese
      ändert jedoch bei jedem Zugriff. Wenn der Benutzer also die aktuelle
      SessionId (oder in diesem Zusammenhang wohl treffender: RequestId)
      dem "Nachbarn" weitergibt, könnte dieser zwar weiter den Dienst
      benutzen, aber du würdest dich selber aus dem Dienst "ausschliessen",
      da mit jeder RequestId lediglich ein Request angefordert werden kann.
      Sobald ein Service (PHP-Seite) durch eine RequestId angefordert
      wurde, wird diese RequestId deaktiviert und die nächste RequestId
      generiert (und ggf. an alle Links und Formulare reingeschrieben).

      Bei diesem Vorgehen gibt es jedoch grosse Probleme:
      Man denke nur an die Situation, dass jemand auf die "Zurück"-Taste
      des Browsers drückt und das Formular erneut absendet: Die im Formular
      hardkodierte RequestId ist ja bereits deaktiviert und die Folge:
      der User wird automatisch ausgeloggt...

      Dein Vorschlag gefällt mir eigentlich sehr gut. Den Nachteil mit dem evtl. gedrückten zurück-Button kann ich verschmerzen, da es wie gesagt eh eine reine Webapplikation ist und es eigentlich auch keine 'Standard-Browser-Buttons' im Fenster gibt. Und wenn es jemand unbedingt ausprobieren will und übers Kontextmenü auf zurück klickt kann ich damit leben, wenn der Benutzer dann fliegt...

      Wechselnde SessionID - man, da hätte ich auch selbst drauf kommen können... Schäm... Gibt es sonst noch andere Nachteile dieses Verfahrens? Mir fallen sonst eigentlich keine weiteren Nachteile auf...Aber das heißt nichts.. ;-)

      Eine Mothode zur 100% erkennung würde mir noch einfallen: Wenn es per Javascript möglich wäre, die MAC-Adresse einer evtl. vorhandenen Netzwerkkarte auszulesen, würden wir der Sache mit der 100%-igen Identifizierung eines Clients näher kommen.. Ich ich glaube nicht, das ich so einfach die MAC-Adresse auslesen kann, sonst wäre das wohl schon längst gang und gäbe - von den Usern ohne Netzwerkkarte mal ganz abgesehen...

      Vorab schonmal vielen vielen Dank!

      Gruß
      Mark

      1. Halihallo Mark

        Hmm, eigentlich soll eben immer nur ein User zur Zeit aktiv sein können. Ähhh, ja, hmm, wie sag ich das denn nun? Also sobald in meiner User-Tabelle ein Benutzer vorhanden ist, sprich eingeloggt ist, soll sich kein weiterer einloggen können und auch nicht das Login des ersten Benutzers verwenden dürfen (eben z.B. durch Kopieren der SessionID)

        Das ist mir klar. Nur geht das technisch über HTTP nicht (zu 100%).
        Wie gesagt, über ein clientseitiges Programm liesse sich dies relativ
        einfach umsetzen.

        Wechselnde SessionID - man, da hätte ich auch selbst drauf kommen können... Schäm... Gibt es sonst noch andere Nachteile dieses Verfahrens? Mir fallen sonst eigentlich keine weiteren Nachteile auf...Aber das heißt nichts.. ;-)

        Naja, es gibt einige technische Problemchen...
        Z.B. speicherst du ja wohl Daten mit der Session (wie z.B. UserId).
        Diese Daten werden normalerweise in einer anderen Tabelle
        gespeichert, die über die SessionId (RequestId) mit der Session
        verknüpft werden. Falls sich also die RequestId ständig ändert,
        müssen alle Daten in der RequestData Tabelle auch geändert werden.
        Abhilfe schafft hier folgender Trick: Man generiert eine staatische
        SessionId, die in der Session gleich bleibt. Daten der Session werden
        über diese SessionId in der DB gespeichert. Die RequestId gehört nun
        einfach zu den Daten der Session und nur diese wird geändert. Bei
        jedem Request wird also die RequestId (ein Bestandteil der
        Sessiondaten) geändert und in alle Formulare/Links angehängt. Beim
        nächsten Zugriff wird der Wert aus dem Formular mit der RequestId in
        den Sessiondaten verglichen. Bei Nichtübereinstimmung wird die
        Session deaktiviert.
        Zweitens: Du musst das Caching vollkommen deaktivieren, da sonst
        alte Seiten im Cache weiterverwendet werden und deshalb bereits
        deaktivierte RequestId's enthalten.
        Drittens: Es entsteht natürlich etwas Aufwand die RequestIds jedesmal
        zu generieren, dann in die DB zu schreiben und wieder auszulesen. Das
        dürfte aber nicht merkbar sein (der Overhead gegenüber staatischer
        SessionId ist IMHO sehr, sehr gering).

        Naja, bei guter Programmierung sehe ich nicht wirklich bedenkliche
        Nachteile. Aber ist ist natürlich sehr viel einfacher eine staatische
        SessionId zu verwenden, die sich während der Sessionzeit nicht
        ändert.

        Aaaaber: Dies ist keine Lösung für dein Problem, denn wenn jemand die
        Request- und SessionId aus dem Formular rauskopiert und an einem
        anderen Computer diese einfügt, dann kannst du dies nicht bemerken.
        Sprich: Du kannst nicht sicherstellen, dass ein User nur von einem
        Computer aus arbeitet. Dazu bräuchtest du - wie du sagst - ein
        eindeutiges Merkmal des Computers wie z.B. die MAC Adresse seiner
        Netzwerkkarte.

        Eine Mothode zur 100% erkennung würde mir noch einfallen: Wenn es per Javascript möglich wäre, die MAC-Adresse einer evtl. vorhandenen Netzwerkkarte auszulesen, würden wir der Sache mit der 100%-igen Identifizierung eines Clients näher kommen.. Ich ich glaube nicht, das ich so einfach die MAC-Adresse auslesen kann, sonst wäre das wohl schon längst gang und gäbe - von den Usern ohne Netzwerkkarte mal ganz abgesehen...

        Tja. Das wäre schon besser, nur, dass es nicht möglich ist die MAC
        Adresse über js oder gar serverseitige Programme auszulesen. Es gibt
        zwar einen Exploit für [...] dort die MAC Adresse auslesen kann.
        Aber das ist sicher keine Lösung und grenzt an Kriminalität (deshalb
        ist es Zensiert :-))...

        Viele Grüsse

        Philipp

        1. Hallo Philipp

          Naja, es gibt einige technische Problemchen...

          Ich habs befürchtet...

          Z.B. speicherst du ja wohl Daten mit der Session (wie z.B. UserId).

          Wie man's nimmt. Die per GET Methode von Seite zu Seite weitergereichte SessionID dient lediglich zur Identifizierung eines berechtigten Users. Konkret heißt das: Wenn ein User sich einloggt, erhält er eine SessionID, welche ich zusammen mit einer UserID in einer Tabelle der Db speichere und wie gesagt per GET weiter reiche. Bei jedem Script kann ich so abfragen, ob die per GET übergebene SessionID in der besagten (Useronline) Tabelle ist. Wenn ja, is alles gut, wenn nicht kommt die "Bitte einloggen" Meldung....
          Insofern müsste ich doch nur die SessionID in der Useronline Tabelle aktualisieren, sobald ich eine neue SessionID erzeugt habe.. Oder?

          Das wäre ein ziemlich gutes Verfahren, denn wenn jemand einfach die SessionID seines Arbeitskollegen nimmt, muss er die so bei jeder Seitenaktualisierung bzw. jedem Seitenaufruf seines Arbeitskollegen wieder neu kopieren, was wohl zuviel Aufwand sein würde...

          Gruß
          Mark

          1. Halihallo Mark

            Wie man's nimmt. Die per GET Methode von Seite zu Seite weitergereichte SessionID dient lediglich zur Identifizierung eines berechtigten Users.

            Also werden keine anderen Daten in dieser Session gespeichert. Ja,
            dann ist das kein Problem (ist auch anders kein Problem, nur, dass
            bei wechselnder SessionId, die SessionId der verknüpften Daten auch
            immer geändert werden muss).

            Konkret heißt das: Wenn ein User sich einloggt, erhält er eine SessionID, welche ich zusammen mit einer UserID in einer Tabelle der Db speichere und wie gesagt per GET weiter reiche. Bei jedem Script kann ich so abfragen, ob die per GET übergebene SessionID in der besagten (Useronline) Tabelle ist. Wenn ja, is alles gut, wenn nicht kommt die "Bitte einloggen" Meldung....

            IO.

            Insofern müsste ich doch nur die SessionID in der Useronline Tabelle aktualisieren, sobald ich eine neue SessionID erzeugt habe.. Oder?

            Richtig. Visualisiert dachte ich an folgendes:

            Tabelle Session

            SessionId
            UserId

            Tabelle SessionData

            Id
            SessionId
            Name
            Value

            Beispiel:
            Session: {15,'userA'}
            SessonData: {1,15,'usr_login','userA'},{2,15,'usr_name','my_name'}

            Falls nun die SessionId immer wechselt, müssen auch alle SessionId's
            in SessionData geändert werden, da sonst die Daten nicht mehr an die
            SessionId zugeordnet sind...

            Egal, ich verwirre dich wohl mehr, als dass ich dir mit meinen
            Kommentaren helfe :-)  Deine Aussagen sind richtig. :-)

            Das wäre ein ziemlich gutes Verfahren, denn wenn jemand einfach die SessionID seines Arbeitskollegen nimmt, muss er die so bei jeder Seitenaktualisierung bzw. jedem Seitenaufruf seines Arbeitskollegen wieder neu kopieren, was wohl zuviel Aufwand sein würde...

            Richtig. Es lässt sich mit grosser Wahrscheinlichkeit ausschliessen,
            dass mit zwei Computern parallel gearbeitet wird. Aber ein Wechsel
            der Arbeitsstation kannst du nicht erkennen (das geht eben ganz
            einfach, indem die SessionId kurz bei der anderen Arbeitsstation
            eingegeben wird).
            Naja, eigentlich ist das ja auch sinnvoll, denn es soll ja eine
            Ein-Benutzer-Lizenz sein und keine Eine-Arbeitsstation-Lizenz,
            insofern haben wir die Aufgabenstellung fast geschaft :-)

            Viele Grüsse

            Philipp