RESTful-Webdienst - Wie authentifiziere ich Anfragen von anderen Diensten?

117

Ich entwerfe einen RESTful-Webdienst, auf den Benutzer, aber auch andere Webdienste und -anwendungen zugreifen müssen. Alle eingehenden Anforderungen müssen authentifiziert werden. Die gesamte Kommunikation erfolgt über HTTPS. Die Benutzerauthentifizierung funktioniert basierend auf einem Authentifizierungstoken, das durch POSTEN des Benutzernamens und des Kennworts (über eine SSL-Verbindung) an eine vom Dienst bereitgestellte / Sitzungsressource erworben wurde .

Bei Webdienst-Clients steht hinter dem Client-Dienst kein Endbenutzer . Die Anforderungen werden durch geplante Aufgaben, Ereignisse oder andere Computeroperationen ausgelöst. Die Liste der Verbindungsdienste ist im Voraus bekannt (offensichtlich, denke ich). Wie soll ich diese Anfragen von anderen (Web-) Diensten authentifizieren? Ich möchte, dass der Authentifizierungsprozess für diese Dienste so einfach wie möglich implementiert wird, jedoch nicht auf Kosten der Sicherheit. Was wären die Standard- und Best Practices für ein solches Szenario?

Optionen, an die ich denken kann (oder die mir vorgeschlagen wurden):

  1. Lassen Sie die Client-Dienste auf einen "falschen" Benutzernamen und ein falsches Kennwort zurückgreifen und authentifizieren Sie sie auf die gleiche Weise wie Benutzer. Ich mag diese Option nicht - sie fühlt sich einfach nicht richtig an.

  2. Weisen Sie dem Client-Service eine permanente Anwendungs-ID zu, möglicherweise auch einen Anwendungsschlüssel. Soweit ich verstanden habe, ist dies genau das Gleiche wie Benutzername + Passwort. Mit dieser ID und diesem Schlüssel kann ich entweder jede Anforderung authentifizieren oder ein Authentifizierungstoken erstellen, um weitere Anforderungen zu authentifizieren. In beiden Fällen gefällt mir diese Option nicht, da jeder, der die Anwendungs-ID und den Schlüssel erhalten kann, sich als Client ausgeben kann.

  3. Ich könnte der vorherigen Option eine IP-Adressprüfung hinzufügen. Dies würde es schwieriger machen, gefälschte Anfragen auszuführen.

  4. Client-Zertifikate. Richten Sie meine eigene Zertifizierungsstelle ein, erstellen Sie ein Stammzertifikat und erstellen Sie Clientzertifikate für die Clientdienste. Es fallen jedoch einige Probleme ein: a) Wie erlaube ich den Benutzern immer noch, sich ohne Zertifikate zu authentifizieren, und b) wie kompliziert ist die Implementierung dieses Szenarios aus Sicht des Client-Service?

  5. Noch etwas - es muss andere Lösungen geben?

Mein Dienst würde auf Java ausgeführt, aber ich habe bewusst Informationen darüber ausgelassen, auf welchem ​​spezifischen Framework er aufbauen würde, da ich mich mehr für die Grundprinzipien und nicht so sehr für die Implementierungsdetails interessiere - ich gehe von der besten Lösung für diesen Willen aus unabhängig vom zugrunde liegenden Framework implementiert werden können. Ich bin jedoch etwas unerfahren mit diesem Thema, daher werden auch konkrete Tipps und Beispiele zur tatsächlichen Implementierung (wie nützliche Bibliotheken, Artikel usw. von Drittanbietern) sehr geschätzt.

Tommi
quelle
Wenn ich vorschlagen darf, machen Sie sich mit den Big-Box-Website-Diensten vertraut und wählen Sie aus, was Ihnen gefällt. Ihre Benutzer würden auch Ähnlichkeiten mit den Best Practices anderer RESTful-Services feststellen.
Yzmir Ramirez
Fand eine andere Frage (fast zwei Jahre alt), die ein ähnliches Thema berührt: stackoverflow.com/questions/1138831/…
Tommi
Auf welchem ​​Betriebssystem werden die Dienste (sowohl das Web als auch die anderen) gehostet? Laufen sie auf Servern, die Teil derselben Infrastruktur sind?
Anders Abel
Das Betriebssystem kann variieren: Win, * nix usw. Die Client-Services befinden sich möglicherweise in derselben Infrastruktur wie mein Service.
Tommi

Antworten:

34

Jede Lösung für dieses Problem läuft auf ein gemeinsames Geheimnis hinaus. Ich mag auch die fest codierte Option für Benutzername und Passwort nicht, aber sie hat den Vorteil, dass sie recht einfach ist. Das Client-Zertifikat ist auch gut, aber ist es wirklich viel anders? Es gibt ein Zertifikat auf dem Server und eines auf dem Client. Der Hauptvorteil ist, dass es schwieriger ist, Gewalt anzuwenden. Hoffentlich haben Sie andere Schutzmaßnahmen, um sich dagegen zu schützen.

Ich denke nicht, dass Ihr Punkt A für die Client-Zertifikat-Lösung schwer zu lösen ist. Sie verwenden nur einen Zweig. if (client side certificat) { check it } else { http basic auth }Ich bin kein Java-Experte und habe noch nie damit gearbeitet, um clientseitige Zertifikate zu erstellen. Ein schnelles Google führt uns jedoch zu diesem Tutorial, das genau in Ihrer Gasse nachschaut.

Lassen Sie mich trotz all dieser "Was ist das Beste" -Diskussion nur darauf hinweisen, dass es eine andere Philosophie gibt, die besagt: "Weniger Code, weniger Klugheit ist besser." (Ich persönlich halte diese Philosophie). Die Client-Zertifikat-Lösung klingt nach viel Code.

Ich weiß, dass Sie Fragen zu OAuth geäußert haben, aber der OAuth2-Vorschlag enthält eine Lösung für Ihr Problem namens " Bearer Token ", die in Verbindung mit SSL verwendet werden muss. Ich denke, der Einfachheit halber würde ich entweder den fest codierten Benutzer / Pass (einen pro App, damit sie einzeln widerrufen werden können) oder die sehr ähnlichen Inhaber-Token wählen.

newz2000
quelle
27
Client-Zertifikate sind KEIN gemeinsames Geheimnis. Deshalb existieren sie. Der Client verfügt über einen privaten Schlüssel und der Server über einen öffentlichen Schlüssel. Der Client teilt niemals sein Geheimnis und der öffentliche Schlüssel ist kein Geheimnis.
Tim
5
Der Tutorial-Link führt nicht zu einem Tutorial-Artikel, sondern zu einer Java-Indexseite auf der Oracle-Site ...
Marjan Venema
2
@MarjanVenema Nun, das liegt daran, dass Sie den Link +2 Jahre nach der Antwort von newz2000 versuchen, aber Sie können immer WayBack Machine ausprobieren: web.archive.org/web/20110826004236/http://java.sun.com/…
Fábio Duque Silva
1
@MarjanVenema: Es tut mir leid, aber Sie erwarten, dass newz2000 hierher kommt und den Link aktualisiert, nachdem er tot ist? Wie Sie sagten, es ist Link Rot, also passiert es früher oder später. Entweder versuchen Sie, auf das Archiv zuzugreifen, um zu sehen, was der Autor zu diesem Zeitpunkt gesehen hat, oder Sie finden den neuen Link und leisten einen positiven Beitrag. Ich verstehe nicht, wie Ihr Kommentar jemandem geholfen hat. Aber hier folgen Sie diesem Link: oracle.com/technetwork/articles/javase/… (Vorsicht, dass es irgendwann auch verrotten wird)
Fábio Duque Silva
2
@ FábioSilva: Nein. Ich erwarte nicht, dass er das tut. Ich habe das Archiv gelesen, aber ich hatte keine Zeit, nach einem neuen Link zu suchen, also habe ich das nächstbeste getan: Geben Sie einen Kommentar ein, dass es tot ist, damit jemand anderes aus der Community den neuen Speicherort finden und den aktualisieren kann Post. Da Sie offensichtlich die Zeit hatten, den neuen Link zu finden, warum haben Sie den Link im Beitrag nicht aktualisiert, anstatt ihn in einen Kommentar zu schreiben, der mich anspricht?
Marjan Venema
36

Nachdem Sie Ihre Frage gelesen haben, würde ich sagen, generieren Sie ein spezielles Token, um die erforderlichen Anforderungen zu erfüllen. Dieser Token wird in einer bestimmten Zeit leben (sagen wir an einem Tag).

Hier ist ein Beispiel zum Generieren eines Authentifizierungstokens:

(day * 10) + (month * 100) + (year (last 2 digits) * 1000)

Zum Beispiel: 3. Juni 2011

(3 * 10) + (6 * 100) + (11 * 1000) = 
30 + 600 + 11000 = 11630

dann mit dem Benutzerkennwort verketten, Beispiel "my4wesomeP4ssword!"

11630my4wesomeP4ssword!

Führen Sie dann MD5 dieser Zeichenfolge aus:

05a9d022d621b64096160683f3afe804

Wenn Sie eine Anfrage aufrufen, verwenden Sie immer dieses Token.

https://mywebservice.com/?token=05a9d022d621b64096160683f3afe804&op=getdata

Dieses Token ist jeden Tag einzigartig. Ich denke, diese Art von Schutz ist mehr als ausreichend, um Ihren Service immer zu schützen.

Hoffnung hilft

:) :)

kororo
quelle
1
Ich mag die Art und Weise, wie Sie das Sicherheitstoken in jeder Anfrage angehängt haben, sehr, aber was passiert damit, wenn der Programmierer bereits Hunderte von JSP-Seiten erstellt hat und danach die Sicherheit in zuvor erstellten 100 Seiten sowie in den Seiten, die erstellt werden sollen, implementiert hat. In diesem Fall ist das Anhängen eines Tokens in jeder Anforderung nicht die richtige Wahl. Immer +1 für Ihre Technik. :)
Ankur Verma
4
Was passiert, wenn die Uhren nicht synchronisiert sind? Wird der Client in dieser Instanz nicht das falsche Token generieren? Selbst wenn beide die Datums- und Uhrzeitangabe in UTC generieren, können ihre Uhren immer noch unterschiedlich sein, was jeden Tag zu einem Zeitfenster führt, in dem das Token nicht funktionieren würde.
NickG
@NickG, ich hatte dieses Problem schon einmal, die einzige Möglichkeit, dies durch Anfordern der Serverzeit zu sichern. Dies wird 99% das Problem von UTC beseitigen. Der Nachteil ist natürlich ein zusätzlicher Anruf beim Server.
Kororo
Aber ich kann Ihren Webdienst trotzdem einen Tag lang mit diesem Token nutzen, oder? Ich sehe nicht, wie das helfen wird
Mina Gabriel
@MinaGabriel Sie können der Token-Generierung mehr Zeitrahmen hinzufügen. (Minute * 10) + (Stunde * 100) + (Tag * 1000) + (Monat * 10000) + (Jahr (letzte 2 Ziffern) * 100000)
Kororo
11

Es gibt verschiedene Ansätze.

  1. Die RESTful-Puristen möchten, dass Sie die BASIC-Authentifizierung verwenden und bei jeder Anforderung Anmeldeinformationen senden. Ihre Begründung ist, dass niemand einen Staat speichert.

  2. Der Clientdienst kann ein Cookie speichern, das eine Sitzungs-ID verwaltet. Ich persönlich finde das nicht so beleidigend wie einige der Puristen, von denen ich höre - es kann teuer sein, sich immer wieder zu authentifizieren. Es hört sich jedoch so an, als ob Sie diese Idee nicht besonders mögen.

  3. Aus Ihrer Beschreibung geht hervor, dass Sie sich vielleicht für OAuth2 interessieren. Meine bisherige Erfahrung zeigt, dass es verwirrend und blutig ist. Es gibt Implementierungen, aber es gibt nur wenige. Ich verstehe, dass es in Java in die Sicherheitsmodule von Spring3 integriert wurde . (Ihr Tutorial ist gut geschrieben.) Ich habe darauf gewartet, ob es eine Erweiterung in Restlet geben wird , aber bisher wurde sie, obwohl sie vorgeschlagen wurde und sich möglicherweise im Inkubator befindet, noch nicht vollständig integriert.

jwismar
quelle
Ich habe nichts gegen Option 2 - ich denke, es ist eine gute Lösung in einer RESTful-App - aber woher bezieht der Client-Service das Token überhaupt? Wie authentifizieren sie sich beim ersten Mal? Vielleicht denke ich das falsch, aber es scheint seltsam, dass der Client-Service dafür einen eigenen Benutzernamen und ein eigenes Passwort haben muss.
Tommi
Wenn der Endbenutzer ein Benutzer von Ihnen ist, kann der Vermittlungsdienst seine Anmeldeinformationen bei der ersten Anforderung an Sie weitergeben und Sie können ein Cookie oder ein anderes Token zurückgeben.
Jwismar
In ähnlicher Weise delegiert der Endbenutzer im OAuth-Szenario den Zugriff auf Ihren Webdienst an den Vermittlungsdienst.
Jwismar
Es scheint ein Missverständnis zu geben - hinter dem Kundenservice steht überhaupt kein Endbenutzer . Ich habe meine Frage aktualisiert, um die Situation besser zu erklären.
Tommi
1
Ich möchte nur hinzufügen, dass die oben aufgeführte Option Nr. 1 nur über HTTPS erfolgen sollte.
mr-sk
3

Ich glaube der Ansatz:

  1. Erste Anfrage, Client sendet ID / Passcode
  2. ID / Pass gegen eindeutiges Token austauschen
  3. Überprüfen Sie das Token bei jeder nachfolgenden Anforderung, bis es abläuft

ist ziemlich Standard, unabhängig davon, wie Sie implementieren und andere spezifische technische Details.

Wenn Sie den Umschlag wirklich verschieben möchten, können Sie möglicherweise den https-Schlüssel des Clients in einem vorübergehend ungültigen Zustand betrachten, bis die Anmeldeinformationen validiert sind, Informationen einschränken, falls dies niemals der Fall ist, und den Zugriff gewähren, wenn sie validiert werden, basierend auf dem Ablauf.

Hoffe das hilft

Dynrepsys
quelle
3

Was den Client-Zertifikat-Ansatz angeht, wäre die Implementierung nicht besonders schwierig, während die Benutzer ohne Client-Zertifikate weiterhin zugelassen werden.

Wenn Sie tatsächlich Ihre eigene selbstsignierte Zertifizierungsstelle erstellt und Client-Zertifikate für jeden Client-Service ausgestellt hätten, hätten Sie eine einfache Möglichkeit, diese Services zu authentifizieren.

Abhängig von dem von Ihnen verwendeten Webserver sollte es eine Methode zum Angeben der Clientauthentifizierung geben, die ein Clientzertifikat akzeptiert, jedoch keine erfordert. In Tomcat können Sie beispielsweise bei der Angabe Ihres https-Connectors 'clientAuth = want' anstelle von 'true' oder 'false' festlegen. Sie würden dann sicherstellen, dass Sie Ihr selbstsigniertes CA-Zertifikat zu Ihrem Truststore hinzufügen (standardmäßig die Cacerts-Datei in der von Ihnen verwendeten JRE, sofern Sie in Ihrer Webserver-Konfiguration keine andere Datei angegeben haben), sodass die einzigen vertrauenswürdigen Zertifikate diejenigen sind, die von ausgestellt wurden Ihre selbstsignierte Zertifizierungsstelle.

Auf der Serverseite würden Sie den Zugriff auf die Dienste, die Sie schützen möchten, nur zulassen, wenn Sie in der Lage sind, ein Clientzertifikat aus der Anforderung abzurufen (nicht null), und alle DN-Prüfungen bestehen, wenn Sie zusätzliche Sicherheit bevorzugen. Benutzer ohne Client-Zertifikate können weiterhin auf Ihre Dienste zugreifen, haben jedoch einfach keine Zertifikate in der Anforderung.

Meiner Meinung nach ist dies der "sicherste" Weg, aber er hat sicherlich seine Lernkurve und seinen Overhead und ist daher möglicherweise nicht unbedingt die beste Lösung für Ihre Bedürfnisse.

bobz32
quelle
3

5. Noch etwas - es muss andere Lösungen geben?

Du hast recht, das gibt es! Und es heißt JWT (JSON Web Tokens).

JSON Web Token (JWT) ist ein offener Standard (RFC 7519), der eine kompakte und in sich geschlossene Methode zur sicheren Übertragung von Informationen zwischen Parteien als JSON-Objekt definiert. Diese Informationen können überprüft und als vertrauenswürdig eingestuft werden, da sie digital signiert sind. JWTs können mit einem geheimen (mit dem HMAC-Algorithmus) oder einem öffentlichen / privaten Schlüsselpaar mit RSA signiert werden.

Ich empfehle dringend, sich mit JWTs zu befassen. Sie sind eine viel einfachere Lösung für das Problem im Vergleich zu alternativen Lösungen.

https://jwt.io/introduction/

justin.hughey
quelle
1

Sie können eine Sitzung auf dem Server erstellen sessionIdund bei jedem REST-Aufruf zwischen Client und Server freigeben.

  1. Erste REST-Anfrage authentifizieren : /authenticate. Gibt die Antwort (gemäß Ihrem Client-Format) mit zurück sessionId: ABCDXXXXXXXXXXXXXX.

  2. Speichern Sie dies sessionIdin der Mapaktuellen Sitzung. Map.put(sessionid, session)oder verwenden Sie SessionListener, um Schlüssel für Sie zu erstellen und zu zerstören;

    public void sessionCreated(HttpSessionEvent arg0) {
      // add session to a static Map 
    }
    
    public void sessionDestroyed(HttpSessionEvent arg0) {
      // Remove session from static map
    }
    
  3. Holen Sie sich bei jedem REST-Aufruf eine Sitzungs-ID, wie URL?jsessionid=ABCDXXXXXXXXXXXXXX(oder auf andere Weise);

  4. Abrufen HttpSessionvon der Karte mit sessionId;
  5. Überprüfen Sie die Anforderung für diese Sitzung, wenn die Sitzung aktiv ist.
  6. Antwort oder Fehlermeldung zurücksenden.
Arviarya
quelle
0

Ich würde eine Anwendung verwenden lassen, die einen Benutzer mit einem Anwendungs-ID-Parameter auf Ihre Site umleitet. Sobald der Benutzer die Anforderung genehmigt, wird ein eindeutiges Token generiert, das von der anderen App zur Authentifizierung verwendet wird. Auf diese Weise verarbeiten die anderen Anwendungen keine Benutzeranmeldeinformationen, und andere Anwendungen können von Benutzern hinzugefügt, entfernt und verwaltet werden. Foursquare und einige andere Sites authentifizieren sich auf diese Weise und sind sehr einfach als andere Anwendung zu implementieren.

Devin M.
quelle
Hmm, ich bin mir nicht sicher, ob ich der Erklärung folgen konnte. Über welchen Benutzer sprechen wir? Ich spreche von einer Anwendung, die mit einer anderen Anwendung kommuniziert. Ich nehme an, Sie haben das verstanden, aber ich kann es immer noch nicht ganz verstehen. Was passiert zum Beispiel, wenn dieser "Token" abläuft?
Tommi
Nun, das Token, das Sie generieren und an die andere Anwendung zurücksenden, ist ein dauerhaftes Token, das an den Benutzer und an die Anwendung gebunden ist. Hier ist ein Link zu foursquares docs developer.foursquare.com/docs/oauth.html und es ist im Grunde genommen oauth2. Schauen Sie sich das an, um eine gute Authentifizierungslösung zu finden.
Devin M
Es scheint ein Missverständnis zu geben - hinter dem Kundenservice steht überhaupt kein Endbenutzer . In der von Ihnen verlinkten foursquare-Dokumentation wird der benutzerlose Zugriff jedoch kurz erwähnt, sodass er zumindest etwas hilfreich war - danke! Aber ich kann mir immer noch kein vollständiges Bild davon machen, wie es in der Realität funktionieren würde.
Tommi
Generieren Sie einfach Schlüssel für die Anwendungen. Wenn Sie lediglich den Zugriff für Anwendungen zulassen, sollten eine einfache Anwendungs-ID und ein Anwendungsschlüssel für die Authentifizierung verwendet werden. Wenn Sie möchten, dass sie sich mit einem Token authentifizieren, prüfen Sie, ob Sie die Token-Authentifizierungsoptionen von devise verwenden, da dies nur ein Parameter ist, der mit der URL-Anforderung an Ihre Anwendung übergeben wird.
Devin M
Aber ist das dann nicht genau das gleiche wie das Szenario Benutzername + Passwort = Sitzungsauthentifizierungstoken? Sind application_id und application_key nicht nur Synonyme für Benutzername und Passwort? :) Es ist vollkommen in Ordnung, wenn dies wirklich die Standardpraxis für eine Situation wie diese ist - wie gesagt, ich bin in dieser Situation unerfahren -, aber ich dachte nur, dass es andere Optionen geben könnte ...
Tommi
-3

Neben der Authentifizierung schlage ich vor, dass Sie über das Gesamtbild nachdenken. Machen Sie Ihren Backend-RESTful-Service ohne Authentifizierung. Stellen Sie dann einen sehr einfachen, für die Authentifizierung erforderlichen Middle Layer-Dienst zwischen den Endbenutzer und den Backend-Dienst.

Dagang
quelle
Zwischen dem Endbenutzer und dem Backend-Service? Würde das den Kundenservice nicht völlig unauthentifiziert lassen? Es ist nicht was ich will. Ich kann natürlich diese mittlere Ebene zwischen meinem Webdienst und den Clientdiensten platzieren, aber das lässt immer noch die Frage offen: Was wäre das eigentliche Authentifizierungsmodell?
Tommi
Die mittlere Schicht kann ein Webserver wie Nginx sein. Dort können Sie die Authentifizierung durchführen. Das Authentifizierungsmodell kann sitzungsbasiert sein.
Dagang
Wie ich in meiner Frage zu erklären versucht habe, möchte ich kein sitzungsbasiertes Authentifizierungsschema für diese Clientdienste. (Bitte beachten Sie meine Aktualisierungen der Frage.)
Tommi
Ich schlage vor, Sie verwenden eine weiße IP-Liste oder einen IP-Bereich anstelle von Name und Passwort. Normalerweise ist die Client-Service-IP stabil.
Dagang