Ein Kennwort in einen REST-API-Aufruf einfügen

31

Angenommen, ich habe eine REST-API, die auch zum Festlegen / Zurücksetzen von Kennwörtern verwendet wird. Nehmen wir auch an, dass dies über HTTPS-Verbindungen funktioniert. Gibt es einen guten Grund, dieses Passwort nicht in den Aufrufpfad zu schreiben? Sagen wir auch, dass ich es in BASE64 verschlüsseln werde?

Ein Beispiel wäre, ein Passwort wie folgt zurückzusetzen:

http://www.example.com/user/joe/resetpassword/OLDPASSWD/NEWPASSWD

Ich verstehe, dass BASE64 keine Verschlüsselung ist, aber ich möchte in diesem Fall nur das Passwort für das Schulter-Surfen schützen.

Bart Friederichs
quelle
61
Schlagen Sie Nebenwirkungen auf GET vor? Das ist genau dort eine Protokollverletzung.
Esben Skov Pedersen
27
Dies ist nicht wirklich REST, da resetpassword/OLDPASSWD/NEWPASSWDes sich nicht um eine Ressource handelt. Es ist ein Aufruf eines Prozesses. Sie müssen nicht alles in eine URL einfügen.
USR
5
@Esben: wer hat gesagt, es ist ein GET? Das hat die OP nie gesagt.
dagnelies
3
Stimmt, er war nicht in der Frage. Aber sein Kommentar zu Netch's Antwort lautet "Vermutlich muss ich doch POST verwenden", sodass wir davon ausgehen können, dass er ursprünglich beabsichtigt / nach GET gefragt hat. Was, wie Esben betont, eine schlechte Sache ist. GET sollte nur lesen.
Mawg
4
Dieser aufschlussreiche Artikel, in dem viele Fallstricke beim Zurücksetzen von Passwörtern erläutert werden, könnte möglicherweise helfen, den Fall besser zu verstehen.
9000

Antworten:

76

Ein guter Server protokolliert alle an ihn gesendeten Anforderungen, einschließlich URLs (häufig ohne variablen Teil nach '?'), Quell-IP, Ausführungszeit ... Soll dieses Protokoll wirklich (möglicherweise von einer großen Gruppe von Administratoren gelesen) enthalten sein Kritisch sichere Infos als Passwörter? Base64 ist kein Stopper gegen sie.

Netch
quelle
42
Dies ist nicht der Hauptgrund für die Verwendung von POST. Es ist ein Sicherheitsgrund. Aber wie Esben bereits in den Kommentaren vermerkt hat, ist ein Zustandswechsel mit einem GET eine Verletzung eines solchen
Rastdienstes
2
@BartFriederichs: und Browserverlauf würden sich die URL merken. Und probieren Sie eine Reihe von Passwörtern anonym aus, indem Sie eine Webseite erstellen, die einen Link für alle Passwörter enthält, die Sie ausprobieren möchten, und Googlebot die tatsächlichen Anforderungen
ausführen lassen
2
"Aber wie Esben bereits in den Kommentaren vermerkt hat, ist das Ändern des Status mit einem GET eine Verletzung eines solchen Restdienstes." Ich habe diesen Kommentar auch bemerkt, aber ich sehe nicht, wo jemand sagt, dass dies eine GET-Anfrage war. Sie können Informationen in einen URI einbetten und dennoch POSTEN. Es ist jedoch nicht wirklich RESTful , da der URI tatsächlich keine Ressource benennt.
Joshua Taylor
Hallo, gute Antwort, aber ich bin mit dem "ohne variablen Teil nach '?'" Nicht einverstanden ... es gibt viele viele, die die vollständige URL speichern !!!
dagnelies
2
"Ein Zustandswechsel mit einem GET ist ein Verstoß gegen einen solchen Rest-Service" - Lassen Sie uns nicht zu sehr in REST verwickeln. Das Ändern des Status mit einem GET verstößt gegen HTTP .
Dan Ellis
69

Was Sie vorschlagen, ist weder sicher noch restvoll.

@Netch hat das Problem mit den Protokollen bereits erwähnt, aber es gibt auch ein anderes Problem, bei dem Sie Passwörter anzeigen, die über HTTP gesendet werden, was es trivial macht, Passwörter mit jeder Art von Wire Sniffer oder Man-in-the-Middle-Angriff zu erfassen.

Wenn Sie eine GET-Anforderung mit REST ausführen, stellen die verschiedenen Elemente in der URL feinkörnigere Elemente dar. Ihre URL sieht so aus, als würden Sie einen NEWPASSWD-Teil eines OLDPASSWD zurückgeben, der Teil eines Reset-Passworts ist. Das ergibt keinen semantischen Sinn. GETs sollten nicht zum Speichern von Daten verwendet werden.

Sie sollten so etwas tun:

POST https://www.example.com/user/joe/resetpassword/
{oldpasswd:[data], newpasswd:[data]}

POST, weil Sie Daten schreiben, und https, weil Sie nicht möchten, dass sie abgehört werden.

(Dies ist wirklich die Low-Bar-Sicherheit. Das absolute Minimum, das Sie tun sollten.)

Gort den Roboter
quelle
2
Würde dies nicht bedeuten, die Passwörter auf der Clientseite zu hacken? Ist das zu empfehlen?
Rowan Freeman
1
Hinweis: Dadurch können die Kennwortanforderungen (Länge usw.) nicht durchgesetzt werden. Dies ist in Ihrem Fall möglicherweise kein Problem, obwohl dies eine häufige Sicherheitspraxis ist und manchmal von einigen Stellen verlangt wird.
Paul Draper
8
Sie erstellen keinen neuen Datensatz, sondern aktualisieren (normalerweise) einen vorhandenen Datensatz. Daher sollte dies PUT und nicht POST sein.
4
Das ist nicht sehr RUHIG. resetpassword ist keine Ressource, geschweige denn eine Subressource. Allerdings /user/joe/passwordist ein wenig besser , aber nicht optimal.
Whirlwin
12
@CamilStaps Nein, du kannst es nicht verwenden PUT, weil PUTes idempotent ist. Wurde das Kennwort jedoch erfolgreich von geändert secret, supersecretschlägt dieselbe Anforderung beim zweiten Mal fehl. Daher POSTist dies hier richtig. Natürlich ist diese Ressource, wie @whirlwin sagte, nicht gut benannt.
Residuum
60

Die vorgeschlagene Regelung weist in mehreren Bereichen Probleme auf.

Sicherheit

URL-Pfade werden häufig protokolliert. Das Einfügen von nicht gehashten Passwörtern in den Pfad ist eine schlechte Praxis.

HTTP

Authentifizierungs- / Autorisierungsinformationen sollten in der Kopfzeile der Autorisierung angezeigt werden. Oder möglicherweise für browserbasierte Inhalte den Cookie-Header.

SICH AUSRUHEN

Verben wie resetpasswordin Ihrer URL sind im Allgemeinen ein deutliches Zeichen für ein nichtrepräsentatives Zustandsübertragungsparadigma. Eine URL sollte eine Ressource darstellen. Was bedeutet es für GET resetpassword? Oder LÖSCHEN?

API

Für dieses Schema muss immer das vorherige Kennwort bekannt sein. Sie werden wahrscheinlich mehr Fälle zulassen wollen; zB das Passwort ist verloren.


Sie können die Standardauthentifizierung oder die Digestauthentifizierung verwenden , bei denen es sich um allgemein bekannte Schemata handelt.

PUT /user/joe/password HTTP/1.0
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Content-Type: text/plain
Host: www.example.com

NEWPASSWD

Es werden keine hochsensiblen Informationen in den Pfad eingefügt, und es werden HTTP- und REST-Konventionen befolgt.

Wenn Sie einen anderen Autorisierungsmodus zulassen müssen (z. B. einen Token, der über einen überprüften Kanal gesendet wurde, um das Kennwort zurückzusetzen), können Sie einfach einen anderen Autorisierungsheader verwenden, ohne etwas anderes ändern zu müssen.

Paul Draper
quelle
4

Abgesehen von der Sicherheit besteht das Problem darin, dass es sich nicht um einen sehr restvollen Ansatz handelt.

OLDPASSWDund NEWPASSWDstehe nicht für irgendetwas in deiner Ressourcenhierarchie und noch schlimmer, die Operation ist nicht idempotent.

Sie können also nur POSTals Verb verwenden und sollten die beiden Kennwörter nicht in Ihren Ressourcenpfad aufnehmen.

biziclop
quelle
1
Sie erstellen keinen neuen Datensatz, sondern aktualisieren (normalerweise) einen vorhandenen Datensatz. Daher sollte dies PUT und nicht POST sein.
2
@CamilStaps Wenn es nur das Passwort festlegte, könnte es sein. Da aber vermutlich auch das alte Passwort überprüft werden muss, wird die Operation nicht idempotent und somit PUTals Verb disqualifiziert. Es könnte umgestaltet werden, um damit zu arbeiten, PUTaber in seiner jetzigen Form ist dies nicht der Fall.
biziclop
OLDPASSWD ist eine Authentifizierungsinformation und sollte überhaupt nicht in der URL enthalten sein.
Es ist nicht unbedingt üblich, zusätzlich zur Authentifizierung explizit nach dem alten Kennwort zu fragen.
biziclop
3

Das Problem ist, Klartext-Passwörter in Ihren Anfragen zu vermeiden. Es gibt zwei Möglichkeiten, um die Restful-Webservice-Anforderungen zu erfüllen.

1. Client-Side-Hashing

  • Ich nehme an, Sie speichern Ihre Passwörter wie zB Hash (Passwort + Salt)
  • Sie können das neue Passwort mit einem Salt auf der Client-Seite hacken
  • Das heißt: Erstellen Sie ein neues Salt auf der Client-Seite, erstellen Sie einen Hash, zB einen Hash (newPassword + newSalt)
  • Senden Sie den neu erstellten Hash plus das Salz an Ihren erholsamen Webservice
  • Sende das alte Passwort auch als Hash (oldPassword + oldSalt)

2. Verschlüsselung

  • Erstellen Sie eine "Einmalschlüssel" -Ressource (otk) für einen Benutzer wie / otk / john
  • Diese Ressource gibt einen sicheren zufälligen eindeutigen Einmalschlüssel zurück, z. B. kbDlJbmNmQ0Y0SmRHZC9GaWtRMW0ycVJpYzhMcVNZTWlMUXN6ZWxLdTZESFRs und eine eindeutige ID, z. B. 95648915125
  • Ihr erholsamer Webservice muss diese zufällige Information für die nächste sichere Kommunikation mit der ID 95648915125 speichern
  • Verschlüsseln Sie Ihr neues und altes Passwort mit dem otk, zB AES (aus Sicherheitsgründen sollten Sie zwei separate otks für das alte und das neue Passwort verwenden)
  • Senden Sie die verschlüsselten Kennwörter mit der ID 95648915125 an Ihre Änderungskennwortressource
  • Eine Kombination aus otk und ID darf nur einmal funktionieren, daher müssen Sie diese Kombination nach dem Ändern des Passworts löschen
  • Möglicherweise bessere Option: Senden Sie das aktuelle / alte Passwort per Basic-Auth.

Hinweis: Für beide Optionen ist HTTPS erforderlich!

maz258
quelle
1
Warum sollte ich den Passwortaustausch doppelt verschlüsseln (Ihr Verschlüsselungsschema mit dem OTK und HTTPS)? Was ist hier der Angriffsvektor, der nicht von HTTPS abgedeckt wird?
Bart Friederichs
1
Der einzige Zweck besteht darin, mögliche Serverprotokolle zu vermeiden. Ein HTTP-Anforderungshauptteil (zum Beispiel mit einem neuen Passwort im Klartext) kann ebenfalls protokolliert werden, obwohl HTTPS verwendet wird. Ein weiterer möglicher Angriff ist die Verwendung eines selbstsignierten Zertifikats, das speziell für interne Zwecke verwendet wird.
Maz258
2

Was sind die Merkmale eines Passwort-Reset-Vorgangs?

  1. Es ändert sich etwas.
  2. Es gibt einen Wert, auf den es festgelegt ist.
  3. Nur einige Personen dürfen dies tun (der Benutzer, ein Administrator oder einer von beiden, möglicherweise mit unterschiedlichen Regeln, wie dies getan werden kann).

Punkt 1 bedeutet hier, dass Sie GET nicht verwenden können. Sie müssen entweder etwas, das die Kennwortänderungsoperation darstellt, in einen URI POSTEN, der eine Ressource darstellt, die Kennwortänderungen behandelt, oder etwas, das das neue Kennwort darstellt, in einen URI PUTEN, der das Kennwort darstellt oder etwas darstellt (z. B. den Benutzer), dessen Passwort ein Merkmal ist.

Im Allgemeinen würden wir POSTEN, nicht zuletzt, weil es unangenehm sein kann, etwas zu schreiben, das wir später nicht bekommen können, und natürlich können wir das Passwort nicht bekommen.

Punkt 2 werden daher Daten sein, die das neue Passwort darstellen, in dem was gepostet wird.

Punkt 3 bedeutet, dass wir die Anfrage autorisieren müssen. Wenn der Benutzer der aktuelle Benutzer ist, muss das aktuelle Kennwort überprüft werden (es muss jedoch nicht unbedingt das aktuelle Kennwort erhalten werden, wenn z. B. eine hashbasierte Abfrage vorliegt wurde verwendet, um das Wissen darüber zu beweisen, ohne es zu senden).

Die URI sollte daher so ähnlich wie <http://example.net/changeCurrentUserPassword>oder sein <http://example.net/users/joe/changePassword>.

Möglicherweise möchten wir das aktuelle Kennwort sowohl in den POST-Daten als auch im verwendeten allgemeinen Autorisierungsmechanismus erhalten.

Jon Hanna
quelle