Best Practice: "Activation Code" (Registrierung+Aktivierung eines Accounts im Netz

slowfly

Erfahrenes Mitglied
Hallo zusammen

Von vorne weg: Ich weiss, das ich wahrscheinlich nicht im richtigen Unterforum bin - aber finde leider kein geeignetes und fühle mich hier wohl am "heimischsten" *g* - falls wo besser aufgehoben, ungeniert verschieben =)

Von vorne weg 2: Hui, viel Text,... sorry =(

Zur Ausgangslage:
Bei neuen Webauftritten von uns müssen wir ein paar Webservices anbieten. Eine Funktionalität ist, dass bei einer Registrierung ein "activation code" generiert wird, welcher als Link per E-Mail verschickt wird. Der Benutzer klickt auf den Link und sein Account wird aktiviert. Diese Anforderung beinhaltet, dass wir das Datum speichern müssen, wann der Account aktiviert wurde.

Datenbankmässig würden wir's so lösen, dass wir eine Tabelle User und eine Tabelle Activation haben, die Activation hält den Foreign Key zur User-Tabelle. Soweit, so gut.

Nur, wo speichern wir das Aktivierungsdatum? Eher in der Usertabelle oder in der Activation-Tabelle? Weil dann muss man sich natürlich auch Gedanken über die Abfrage machen. Wir rechnen mit einigen Zehntausend Rows, über die man dann einen Select absetzt, der einen bis zu 64-stelligen Key (da wird weiter unten drauf eingegangen) sucht. Würde man das Aktivierungsdatum in der Tabelle Activation halten, könnte man hier wenigstens noch der Abfrage mitgeben, dass das Aktivierungsdatum null sein muss - hier rechnen wir natürlich mit viel weniger Rows (also weniger Benutzer, die ihren Account noch nicht aktiviert haben).... aber die Überlegungen gehen dann noch weiter ...

Dann haben wir uns die Frage gestellt, wie soll der Aktivierungscode aufgebaut, resp. generiert werden? Ich hätt jetzt mal behauptet, wir nehmen E-Mailadresse + ":" + Erstellungstimestamp und lassen nen SHA-256 drüberlaufen. Somit ist der Aktivierungscode reproduzierbar.
Dann ist uns eingefallen "Ja, moment mal. Wenn er reproduzierbar ist, warum speichern wir ihn dann überhaupt?" Nun, wenn wir ihn nicht speichern, bekommen wir nur mühsam wieder die Information raus, welchem Benutzer dieser Aktivierungskey gehört.
Dann kam die glorreiche Idee, den Primärschlüssel in der Aktivierungstabelle irgendwie in den Aktivierungscode zu bekommen. Also sieht's dann so aus "AKTIVIERUNGSCODE" + "PRIMARYKEY". Da haben wir natürlich den Riesenvorteil, dass wir die Abfrage auf den PK machen können. Dann haben wir den Aktivierungscode, von dort kommen wir an den User und können überprüfen, ob der übergebene Aktivierungscode mit dem übereinstimmt, was wir reproduzieren (also SHA-256(email + ":" + creationtimestamp).
Nur, wir schicken sicher keinen Primary Key in einem Aktivierungscode per E-Mail. Also könnte man ja z.B. den PK bei z.B. 1'000'000 beginnen und da drüber nen XOR drüber laufen lassen, welches man dann bei der Abfrage wieder "zurück-ver-XOR-en" müsste.

Also, dann würde das am Schluss so aussehen
Registrierung ->
Key Generieren -> SHA-256(email:creationtimestamp) + XOR(primary key Aktivierungstabelle, "Verschlüsselungskey")

Aktivierung ->
Substring Activation Code -> die ersten 64 Stellen sind der Hash, der Rest ist PK
-> den "Rest PK" zurück-x-oren
-> Select auf Aktivierung anhand des zurück-ge-x-orten-wertes (also vom effektiven PK), join auf User
-> Generierung des SHA-256-Hashs anhand der Benutzerdaten
-> Überprüfung der ersten 64 Stellen des Activation Keys mit dem, was wir anhand der Tabelle generiert haben.

So, nun, eben, eigentlich haben wir eine Lösung. Nur ist das ganze doch sehr aufwendung und komplex.

Wer von euch hat schon Erfahrungen damit gemacht, und wie habt ihr es gelöst?
Gibt's Fehler in unseren Überlegungen? Oder sind wir da komplett am Ziel vorbeigeschossen, resp. übertreiben wir's hier einfach?

Besten Dank fürs Lesen der wall of text =)

Grüsse
slowy
 
Man könnte vielleicht noch einen Session-Key verwenden das die Aktivierung in der selben Session wie die Registrierung vorgenommen werden muss. Die modernen Browser unterstützen ein sharing des Session-Keys über mehrere Tabs, jedoch nicht über mehrere Instanzen was bei entsprechender Broswereinstellung passieren kann.
Ansonsten würde ich den Activation Key via pseudo-Random als relativ kurzen String mit 16 Stellen erzeugen. Da auf PC's ohne zusätzlichen true-Randominput eine Kollision sehr gering ist *da der Random-Generator meist über die Systemzeit geseeded wird* wäre es für einen gewissen Zeitraum kollisionssicher. Aber sich den umstand über SHA256 zu machen und dann noch mit XOR dranrumbasteln ... das halte ich dann doch für zu viel Aufwand.
 
Spike, danke für die Antwort und vor Allem fürs Lesen ;)

Also das mit den Session Keys ist eher ungünstig, eben wegen den Multitabs. Und dann gibt's natürlich auch Fälle, wo eine E-Mail länger unterwegs ist, oder ein Benutzer ganz vergessen hat, der Mutter die Mayonnaise fürs Abendbrot mitzubringen und ganz schnell wieder los muss *g*

Ich war gerade bei unseren DBA's und wollte wissen, wie lang dann so eine Full Text Suche geht,... als ich von geschätzten 100'000 Rows geredet habe, wurde ich nur müde belächelt *g*... "Performanceprobleme unter Oracle mit 100'000 Rows? Gibt es nicht." *g* ... mit dem Hinweis "Probier's doch aus?" hehe

Wir haben jetzt auch nochmals den "Random"-Vorschlag diskutiert und werden wohl diesen Ansatz anwenden. Was man hier beachten muss ist, dass der Wert nicht schonmal in der Tabelle vorkommt, das muss man einerseits applikatorisch (select count()) und einerseits db-mässig mit einem Constraint abfangen.

Wird ein Account aktiviert, setzen wir beim User den Status auf "aktiv" und löschen den Eintrag in der Activation Tabelle.

Für den Fall, dass einer das E-Mail nicht bekommen hat (oder gelöscht), werden wir einfach einen neuen Eintrag in der Activation Tabelle machen. Hat zur Folge, dass man einen creation timestamp halten muss, damit man die alten nicht verwendeten Einträge via Job löschen kann.
 
Ja gut ... ob nun unter Oracle , MySQL oder sonst nem ordentlichem DB-System ... die FULL-TEXT-Suche ist in meisten bereits sehr gut implementiert. Klar meckern dann die DB-Stats rum man solle doch mal die KEY's verwenden ... aber wenn man ne so große DB hat dann ist das Sortieren der KEY's aufwändiger als gleich das ganze DB-File zu durchsuchen ^^
 
Zurück