Poner una contraseña en una llamada API REST

31

Supongamos que tengo una API REST que también se usa para establecer / restablecer contraseñas. Supongamos también que esto funciona sobre conexiones HTTPS. ¿Hay alguna buena razón para no poner esa contraseña en la ruta de llamada, también digamos que la codificaré en BASE64?

Un ejemplo sería restablecer una contraseña como esta:

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

Entiendo que BASE64 no es encriptación, pero solo quiero proteger la contraseña para navegar por el hombro en este caso.

Bart Friederichs
fuente
61
¿Estás sugiriendo efectos secundarios en GET? Esa es una violación del protocolo allí mismo.
Esben Skov Pedersen
27
Esto no es realmente REST porque resetpassword/OLDPASSWD/NEWPASSWDno es un recurso. Es una invocación de un proceso. No necesita insertar todo en una URL.
usr
55
@Esben: ¿quién dijo que es un GET? El OP nunca dijo eso.
Dagnelies
3
Es cierto que no lo hizo en la pregunta. Pero su comentario a la respuesta de Netch dice "Supongo que tendré que usar POST después de todo", por lo que podemos suponer que originalmente tuvo la intención / preguntó sobre GET. Lo cual, como señala Esben, es una mala cosa. GET debería leer solo.
Mawg
44
Este artículo perspicaz que explica muchas trampas de los mecanismos de restablecimiento de contraseña posiblemente podría ayudar a comprender mejor el caso.
9000

Respuestas:

76

Un buen servidor registra todas las solicitudes que se le envían, incluidas las URL (a menudo, sin parte variable después de '?'), IP de origen, tiempo de ejecución ... ¿Realmente desea que este registro (potencialmente leído por un amplio grupo de administradores) contenga ¿Información críticamente segura como contraseñas? Base64 no es un obstáculo contra ellos.

Netch
fuente
42
Esta no es la razón más importante para usar POST. Es una razón de seguridad. Pero a medida que Esben allready nota din los comentarios, el cambio de estado con un GET es una violación de dicho servicio Resto
Pinoniq
2
@BartFriederichs: y el historial del navegador recordaría la URL. Y probar un montón de contraseñas de forma anónima haciendo una página web que tenga un enlace para todas las contraseñas que desea probar, y dejando que Googlebot haga las solicitudes reales ...
RemcoGerlich
2
"Pero como Esben ya notó los comentarios, cambiar el estado con un GET es una violación de dicho servicio de descanso". También noté ese comentario, pero no veo dónde alguien dice que se trata de una solicitud GET. Puede incrustar información en un URI y aún publicarla, después de todo. Sin embargo, no es realmente RESTful , ya que el URI en realidad no nombra un recurso.
Joshua Taylor
Hola, buena respuesta, pero no estoy de acuerdo con el "sin parte variable después de '?'" ... ¡hay muchos que almacenan la URL completa!
Dagnelies
2
"cambiar de estado con un GET es una violación de dicho servicio de descanso" - No nos dejemos atrapar demasiado en REST. Cambiar de estado con un GET es una violación de HTTP .
Dan Ellis
69

Lo que está proponiendo no es seguro ni RESTful.

@Netch ya ha mencionado el problema con los registros, pero también hay otro problema en el que muestra las contraseñas enviadas por HTTP, por lo que es trivial capturar contraseñas con cualquier tipo de sniffer de cable o ataque de hombre en el medio.

Cuando realiza una solicitud GET utilizando REST, los diferentes elementos en la URL representan elementos más específicos. Su URL se lee como si estuviera devolviendo una parte NEWPASSWD de una OLDPASSWD que es parte de una contraseña de restablecimiento. Eso no tiene ningún sentido semántico. Los GET no deben usarse para guardar datos.

Deberías estar haciendo algo como esto:

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

PUBLICA porque estás escribiendo datos, y https porque no quieres que los analice.

(Esta es realmente la seguridad de barra baja. El mínimo absoluto que debe hacer).

Gort the Robot
fuente
2
¿No significa esto descifrar las contraseñas en el lado del cliente? ¿Se recomienda esto?
Rowan Freeman
1
Nota: esto no permitirá cumplir los requisitos de contraseña (longitud, etc.). Es posible que eso no sea un problema en su caso, aunque es una práctica de seguridad frecuente y algunas veces lo requieren algunas entidades.
Paul Draper
8
No está creando un nuevo registro, sino que está actualizando un registro existente (por lo general), por lo que debería ser PUT en lugar de POST.
44
Esto no es muy RESTANTE. resetpassword no es un recurso y mucho menos un subrecurso. Sin embargo, /user/joe/passwordes un poco mejor pero no óptimo.
whirlwin
12
@CamilStaps No, no se puede usar PUT, porque PUTes idempotente. Pero cuando la contraseña se ha cambiado de secreta con supersecretéxito, la misma solicitud fallará la segunda vez, por lo que POSTes correcto aquí. Por supuesto, como dijo @whirlwin, este recurso no está bien nombrado.
Residuo
60

El esquema propuesto tiene problemas en varias áreas.

Seguridad

Las rutas de URL se registran con frecuencia; poner contraseñas sin usar en el camino es una mala práctica.

HTTP

La información de autenticación / autorización debe aparecer en el encabezado de Autorización. O potencialmente, para cosas basadas en el navegador, el encabezado Cookie.

DESCANSO

Los verbos como resetpassworden su URL son generalmente un signo claro de un paradigma de transferencia de estado no representativo. Una URL debe representar un recurso. ¿Qué significa OBTENER resetpassword? O ELIMINAR?

API

Este esquema requiere saber siempre la contraseña anterior. Probablemente desee permitir más casos; Por ejemplo, la contraseña se pierde.


Puede usar la autenticación básica o implícita , que son esquemas bien entendidos.

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

NEWPASSWD

No pone información ultrasensible en la ruta, y sigue las convenciones HTTP y REST.

Si necesita permitir algún otro modo de autorización (por ejemplo, algún token enviado a través de un canal verificado para restablecer la contraseña), simplemente puede usar un encabezado de Autorización diferente sin tener que cambiar nada más.

Paul Draper
fuente
4

Además de la seguridad, el problema con esto es que no es un enfoque RESTANTE.

OLDPASSWDy NEWPASSWDno represente nada en su jerarquía de recursos y, lo que es peor, la operación no es idempotente.

Por lo tanto, solo puede usar POSTcomo verbo, y no debe incluir las dos contraseñas en su ruta de recursos.

biziclop
fuente
1
No está creando un nuevo registro, sino que está actualizando un registro existente (por lo general), por lo que debería ser PUT en lugar de POST.
2
@CamilStaps Si solo estuviera configurando la contraseña, podría ser. Pero dado que presumiblemente la contraseña anterior también debe ser verificada, hace que la operación no sea idempotente y, por lo tanto, PUTse descalifica como verbo. Podría ser modificado para trabajar, PUTpero en su forma actual no lo hace.
biziclop
OLDPASSWD es información de autenticación y no debe estar en la URL.
No necesariamente, es una práctica común pedir explícitamente la contraseña anterior además de la autenticación.
biziclop
3

El problema es evitar contraseñas de texto sin formato en sus solicitudes. Hay dos opciones para cumplir con los requisitos de servicio web reparador.

1. Hashing del lado del cliente

  • Supongo que está almacenando sus contraseñas como, por ejemplo, hash (contraseña + sal)
  • Puede cambiar la nueva contraseña con sal en el lado del cliente
  • Eso significa: crear una nueva sal en el lado del cliente, crear un hash, por ejemplo, hash (newPassword + newSalt)
  • Envíe el nuevo hash creado más la sal a su tranquilo servicio web
  • Envíe la contraseña anterior también como hash (oldPassword + oldSalt)

2. Cifrado

  • Cree un recurso de "clave única" (otk) para un usuario como / otk / john
  • Este recurso devuelve una clave única aleatoria segura, por ejemplo, kbDlJbmNmQ0Y0SmRHZC9GaWtRMW0ycVJpYzhMcVNZTWlMUXN6ZWxLdTZESFRs y una ID única, por ejemplo, 95648915125
  • Su servicio web tranquilo debe almacenar este otk aleatorio para la próxima comunicación segura con el ID 95648915125
  • Cifre su contraseña nueva y antigua con el otk, por ejemplo, AES (por razones de seguridad, debe usar dos otks separados para la contraseña antigua y la nueva)
  • Envíe las contraseñas cifradas a su recurso de cambio de contraseña con el ID 95648915125
  • Una combinación de otk e ID solo puede funcionar una vez, por lo que debe eliminar esa combinación después de cambiar la contraseña
  • Posible mejor opción: envíe la contraseña actual / anterior por Basic-Auth.

Nota: ¡ Se requiere HTTPS para ambas opciones!

maz258
fuente
1
¿Por qué debería encriptar dos veces (su esquema de encriptación con OTK y HTTPS) el intercambio de contraseña? ¿Cuál es el vector de ataque aquí que no está cubierto por HTTPS?
Bart Friederichs
1
El único propósito es evitar posibles registros del servidor. También se puede registrar un cuerpo de solicitud HTTP (por ejemplo, con una nueva contraseña de texto sin formato) aunque se utiliza HTTPS. Otro posible ataque es el uso de un certificado autofirmado que se utiliza especialmente para fines internos.
maz258
2

¿Cuáles son las características de una operación de restablecimiento de contraseña?

  1. Cambia algo
  2. Hay un valor en el que está configurado.
  3. Solo algunas personas pueden hacerlo (el usuario, un administrador o cualquiera de ellos, tal vez con diferentes reglas sobre cómo hacerlo).

El punto 1 aquí significa que no puede usar GET, debe PUBLICAR algo que representa la operación de cambio de contraseña en un URI que representa un recurso que maneja los cambios de contraseña, o PONER algo que representa la nueva contraseña en un URI que representa la contraseña o que representa algo (p. Ej. usuario) de los cuales esa contraseña es una característica.

En general, PUBLICAREMOS, sobre todo porque puede ser incómodo PONER algo que no podemos OBTENER más tarde y, por supuesto, no podemos OBTENER la contraseña.

El punto 2, por lo tanto, serán datos que representan la nueva contraseña, en lo que se PUBLICA.

El punto 3 significa que necesitaremos autorizar la solicitud, lo que significa que si el usuario es el usuario actual, necesitaremos que nos demuestren la contraseña actual (aunque no necesariamente recibamos la contraseña actual, por ejemplo, si se trata de un desafío basado en hash) se usó para probar su conocimiento sin enviarlo).

Por lo tanto, el URI debe ser algo como <http://example.net/changeCurrentUserPassword>o <http://example.net/users/joe/changePassword>.

Podríamos decidir que queremos recibir la contraseña actual en los datos POST, así como en el mecanismo de autorización general que se utiliza.

Jon Hanna
fuente