webapplication Konzeptfrage Sessionverwaltung
Mark
- php
0 Lirpa0 Philipp Hasenfratz0 Mark
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
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
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
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
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
Hallo Mark,
da hast du schon recht. Die Lösung ist sicher nicht wasserdicht.
War ein Versuch des Erklärung.
Viel Erfolg
Mia
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
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
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
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
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:
SessionId
UserId
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