2009-06-02 5 views
21

Ich habe ein Szenario, in dem Benutzer einer Site, die ich erstelle, einige grundlegende Informationen in ein Webformular eingeben können, ohne sich anmelden zu müssen. Die Site wird mit ASP.NET/C# entwickelt und verwendet MSSQL 2005 für seine relationalen Daten.Implementieren sicherer, eindeutiger "single-use" -Aktivierungs-URLs in ASP.NET (C#)

Die Benutzer erhalten eine E-Mail von der Site, die ihnen einen eindeutigen Link zur Eingabe der spezifischen Informationen gibt, die sie benötigen. Die E-Mail wird dem Stil der E-Mails sehr ähnlich sein, die wir bei der Registrierung für Websites wie Foren erhalten. Sie enthalten einen nach dem Zufallsprinzip generierten URL-Parameter, der sich ausschließlich auf einen bestimmten Zweck bezieht (z. B. die Überprüfung einer E-Mail-Adresse für ein Forum).

Meine Abfragen betreffen die sichere Implementierung dieses Problems. Ich habe darüber nachgedacht, eine GUID als eindeutigen Identifikator zu verwenden, bin mir jedoch unsicher, welche Auswirkungen dies in der Sicherheitswelt haben wird.

  1. Ist eine GUID ausreichend lang genug, dass Werte nicht leicht erraten werden können (oder im Laufe der Zeit brutal erzwungen)?
  2. Ist die GUID-Implementation von .NET ausreichend zufällig in dem Sinne, dass es eine gleiche Chance auf die Erzeugung aller möglichen Werte im "Schlüsselraum" gibt?

  3. Wenn die Verwendung einer GUID ein akzeptabler Ansatz ist, sollte die Site dann auf die Informationen per URL-Umschreiben umleiten oder die Informationen in einer Datentabelle mit der GUID als Referenz verknüpfen?

  4. Wird mit einem URL-Rewriting die wahre Quelle der Daten ausgeblendet?

  5. Sollte ich TSQL SELECT NEWID() als GUID-Generator über die .NET-Implementierung in Betracht ziehen?

  6. Bin ich völlig falsch mit meiner Herangehensweise an dieses Problem?

Vielen Dank,

Carl

Antwort

12
  1. Ja, 2 lang genug ist.
  2. Nein, GUID-Implementierungen wurden entwickelt, um eindeutige GUIDs anstelle von zufälligen zu generieren. Sie sollten einen cryptographically secure Zufallszahlengenerator (z. B. RNGCryptoServiceProvider) verwenden, um 16 zufällige Bytes zu generieren und damit eine Guid Struktur zu initialisieren.
  3. Ja, es ist insgesamt ein akzeptabler Ansatz. Beide werden funktionieren.
  4. Ja, wenn Sie geben keine weiteren Hinweise
  5. Nein, goto 2
  6. Nein, es ist ziemlich OK. Sie müssen nur einen kryptografisch sicheren Zufallszahlengenerator verwenden, um die GUID zu generieren.
+0

Sorry, aber das ist wirklich falsch und irreführend - GUIDs sind nicht gut für diese Art von Sache, siehe meine Antwort unten. – AviD

+0

GUID, im Sinne von * eindeutige Kennung * ist nicht wirklich geeignet (wie ich in # 2 erwähnt). Aber nichts hindert Sie daran, sie als 128-Bit-Nummer zu verwenden. –

+0

Aber die Sache ist, sie sind * nicht * wirklich 128-Bit-Nummern (obwohl es 128 Bits dauert, um sie zu speichern). Wiederum ist zu viel davon entweder statisch oder leicht zu erraten. GUIDs haben keine Verwendung in sicheren Bezeichnern. Was das OP braucht, ist etwas Sicheres und lang genug im Sinne von "128 Bits of Randomness", was GUIDs nicht sind, nicht einmal annähernd. – AviD

4

Ich würde empfehlen, nur eine einfache Zufallszahl, z. Bytes von RNGCryptoServiceProvider.GetBytes. GUIDs sollen nicht völlig zufällig sein. Sie haben feste Bits und einige Versionen verwenden Ihre MAC-Adresse. GetBytes gibt Ihnen auch die Möglichkeit, mehr als 128 Bits zu verwenden (obwohl das reichlich sein sollte).

Ich denke, es ist besser, die Daten des Benutzers nicht in eine URL zu stellen. Obwohl HTTPS es während der Übertragung schützen kann, befindet es sich möglicherweise noch im Browserverlauf des Benutzers oder dergleichen. Es ist besser, POST zu verwenden und die Zufallszahl einer Zeile Ihrer Datenbank zuzuordnen.

1

Wenn möglich, sollten Sie die Brute-Force begrenzen, indem Sie den Durchsatz der Abfrage begrenzen (z. B. begrenzte Versuche pro IP pro Sekunde und begrenzte Gesamtversuche pro Sekunde).

Abhängig vom GUID-Generierungsalgorithmus ist dies möglicherweise keine gute Idee. Während neuere Implementierungen für gewöhnlich "gut genug" sein sollten, kann die Vorhersagbarkeit der Werte sehr hoch sein. Neuere Implementierungen, wie sie in .NET verwendet werden, wenden einen Hash an, andernfalls haben Sie eindeutig identifizierbare Felder für die MAC-Adresse und die Zeit seit dem Booten. Die möglichen Werte sind jedoch begrenzt, sodass Sie keine echten 128 zufälligen Bits haben.

Wenn Sicherheit oberste Priorität hat, würde ich eine cryptographically strong random number generator auswählen und eine Zeichenfolge aus zufälligen Zeichen erstellen. Dies macht oyu auch unabhängig von einem leicht zu erratenden Algorithmus (da guid.ToString() leicht als solcher erkannt wird), für den in naher Zukunft ein Angriff entdeckt werden könnte.

Wenn diese Kriterien erfüllt sind, sehe ich kein Problem mit dem Schlüssel in der Abfragezeichenfolge.

4

Es könnte zu viel des Guten, aber man konnte ihre E-Mail-Adresse mit SHA1 mit Ihrem guid Hash (NewGuid ist in Ordnung) als hash salt und Ort, in der URL. Wenn sie dann auf Ihrer Seite ankommen, können Sie sie nach ihrer E-Mail-Adresse fragen, die GUID abrufen und den Hashwert zur Validierung neu berechnen. Selbst wenn jemand wüsste, welche E-Mail-Adressen zu versuchen sind, würden sie niemals in der Lage sein, eine Hash-Kollision zu erzeugen, ohne die Guid zu kennen, mit der Sie gesalzen sind (oder es würde sie eine verdammt lange Zeit brauchen). Natürlich müssten Sie ihre E-Mail und die Hash-Guid in der Datenbank speichern.

+0

Obwohl das oben Technische direkt meine Fragen beantwortete, liebe ich Ihre Implementierungsidee. Nach einigen dachte ich drauf habe expaneded einige: URL = SHA1 (E-Mail + randomPass + Salz) serverHash = SHA1 (E-Mail + randomPass) Tabellenspalten: serverHash (Primärschlüssel) Salz Die randompass würde Auch mit der URL in der E-Mail gesendet und in der ursprünglichen URL-Berechnung verwendet werden dann verworfen. Ich glaube, dass in dieser Implementierung keine sensiblen Informationen in der Datenbank gespeichert werden.Das heißt, selbst wenn die Rohdatentabelle offengelegt wurde, konnte die URL zur Brute-Force-Generierung der E-Mail-Adresse nicht generiert werden. – Tyst

+1

Ich bin froh, Ihnen helfen zu können. Ein Hinweis zu Ihrer Lösung: Sie öffnen sich für einen neuen Anwendungsfall, da Sie die E-Mail nicht neu erstellen können. Das heißt, der Benutzer fragt an und mailt, tut nichts damit, fordert einen anderen an und klickt dann auf den Link in der ersten E-Mail (wie Sie sicher wissen, machen Benutzer solche Sachen :) Also müssen Sie jeden ablaufen ServerHash, indem Sie das Datum, an dem Sie die E-Mail gesendet haben, in der Datenbank speichern und dem Benutzer eine angemessene Reaktionszeit geben. –

+0

Yup, danke. Ich erkenne das. Die generierten E-Mails und URLs sind sehr fallspezifisch und werden generiert, damit die Benutzer sich generell etwas Geld sparen können, also sollten sie diese wahrscheinlich verwenden, aber ich werde auch etwas Handling implementieren. – Tyst

11
  1. Nein, GUIDs sind nicht vollständig zufällig, und die meisten Bits sind entweder statisch oder leicht zu erraten.
  2. Nein, sie sind nicht zufällig, siehe 1. Es gibt tatsächlich eine sehr kleine Anzahl von Bits, die tatsächlich zufällig und nicht kryptographisch stark zufällig sind.
  3. Es ist nicht, siehe 1 und 2.
  4. können Sie, aber nicht müssen ... meine Lösung am Ende zu sehen.
  5. Nein, siehe 1 und 2
  6. Ja.

Was sollten Sie statt einer GUID verwenden, ist ein kryptographisch starker Zufallszahlengenerator - verwenden System.Security.Cryptography.RNGCryptoServiceProvider, lange zu erzeugen (etwa 32 Byte) Datenfolge, dann base64 encode dass.
Auch unter der Annahme, dass es sich um eine Art Registrierung mit sensiblen Daten handelt, möchten Sie die Gültigkeit des Links zeitlich begrenzen, sagen wir 60 Minuten oder 24 Stunden - abhängig von Ihrer Site.
Sie müssen diese Werte den spezifischen Benutzern zuordnen. Dann können Sie ihn automatisch mit der richtigen Form nach Bedarf präsentieren. Du musst das URL-Rewriting nicht machen, sondern benutze das als Benutzerkennung (auf dieser Seite).
Natürlich nicht diese URL vergessen sollte sein HTTPS ...

Btw, nur eine Note - seine gute Praxis irgendeine Form von Text in der E-Mail zu stellen, zu erklären, dass die Nutzer auf Links in E-Mail anonym shouldnt klicken und in der Regel wird Ihre Website nicht senden, und sie sollten nie ihr Passwort nach dem Klicken blablabla .... eingeben

Oh, fast vergessen - ein anderes Problem, das Sie beachten sollten, ist, was passiert, wenn der Benutzer möchte, dass mehrere E-Mails an ihn gesendet werden, z. Treffer registrieren sich mehrmals. Kann er das immer wieder machen und viele gültige URLs erhalten? Ist nur der letzte gültig? Oder wird der gleiche Wert immer wieder neu gesendet? Natürlich, wenn ein anonymer Benutzer eine Anfrage für diese E-Mail stellen kann, dann kann DoS ein Problem werden ... ganz zu schweigen davon, dass er, wenn er seine eigene E-Mail einreicht, auch eine beliebige Adresse eingeben kann, um so einige arme Leute zu überfluten Posteingang und möglicherweise verursacht Ihren Mail-Server auf schwarze Liste zu bekommen ...
Keine richtige Antwort, aber muss im Zusammenhang mit Ihrer Anwendung berücksichtigt werden.

-1

erstellen Sie eine Tabelle in der Datenbank mit einer LinkID und einer Datumsent Spalte, bei der Generierung der Link senden einfügen DateTime.Now in die Tabelle und Rückgabe linkId, legen Sie die LinkID als Querystring-Parameter auf dem activationLink. Nach dem Laden der Aktivierungsseite rufen Sie die linkId ab und verwenden sie, um eine gespeicherte Prozedur aufzurufen, die das Datum zurückgibt, wenn die entsprechende linkId als Parameter übergeben wurde. Wenn Sie das Datum zurückbekommen, können Sie hinzufügen, wie lange die Verknüpfung aktiv bleiben soll Mit den AddDays() /. AddMonths sind dies C# -Methoden für datetime. dann vergleiche das Datum, an dem du zurückkommst, mit dem heutigen Datum. Wenn es die Länge von Tagen oder Monaten überschritten hat, geben Sie eine Fehlermeldung ein oder fahren Sie fort und zeigen Sie den Seiteninhalt an. Ich suggest Sie halten den Inhalt der Seite in einem Panel und und setzen Sie es zulässig = "falsch" dann nur das Panel sichtbar = "wahr", wenn das Datum immer noch in Reichweite ist.