Colocando uma Senha em uma Chamada da API REST

31

Suponha que eu tenha uma API REST que também seja usada para definir / redefinir senhas. Suponhamos também que isso funcione em conexões HTTPS. Existe algum bom motivo para não colocar essa senha no caminho da chamada, digamos também que eu a codificarei no BASE64?

Um exemplo seria redefinir uma senha como esta:

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

Entendo que o BASE64 não é criptografia, mas só quero proteger a senha para navegar no ombro neste caso.

Bart Friederichs
fonte
61
Você está sugerindo efeitos colaterais no GET? Isso é uma violação de protocolo ali.
Esben Skov Pedersen
27
Este não é realmente o REST, porque resetpassword/OLDPASSWD/NEWPASSWDnão é um recurso. É uma invocação de um processo. Você não precisa colocar tudo em um URL.
usr
5
@ Esben: quem disse que é um GET? O OP nunca disse isso.
Dagnelies
3
É verdade que ele não fez a pergunta. Mas seu comentário à resposta de Netch diz "Acho que vou precisar usar o POST, afinal", para que possamos assumir que ele originalmente pretendeu / perguntou sobre o GET. O que, como Esben aponta, é uma coisa ruim. GET deve ser somente leitura.
MAWG

Respostas:

76

Um bom servidor registra todas as solicitações enviadas a ele, incluindo URLs (geralmente, sem parte variável após '?'), IP de origem, tempo de execução ... Deseja realmente que esse log (potencialmente lido por um amplo grupo de administradores) contenha informações criticamente seguras como senhas? Base64 não é uma rolha contra eles.

Netch
fonte
42
Este não é o maior motivo para usar o POST. É uma razão de segurança. Mas, como Esben allready nota din os comentários, alterar o estado com um GET é uma violação de tal serviço Resto
Pinoniq
2
@BartFriederichs: e o histórico do navegador lembram o URL. E experimentar um monte de senha anonimamente, fazendo uma página web que tem um link para todas as senhas que você quer tentar, e deixando Googlebot fazer os pedidos reais ...
RemcoGerlich
2
"Mas como Esben já observou os comentários, a mudança de estado com um GET é uma violação de um serviço de descanso". Notei esse comentário também, mas não vejo onde alguém está dizendo que essa foi uma solicitação GET. Você pode incorporar informações em um URI e ainda assim POSTAR, afinal. No entanto, não é realmente RESTful , pois o URI não está realmente nomeando um recurso.
Joshua Taylor
Oi, boa resposta, mas eu discordo do "sem parte variável depois de '?'" ... existem muitos que armazenam a URL completa !!!
Dagnelies
2
"alterar o estado com um GET é uma violação de um serviço de repouso" - Não vamos ficar muito envolvidos no REST. Alterar o estado com um GET é uma violação do HTTP .
Dan Ellis
69

O que você está propondo não é seguro nem RESTful.

O @Netch já mencionou o problema nos logs, mas também existe outro problema, no qual você está mostrando senhas enviadas por HTTP, tornando trivial capturar senhas com qualquer tipo de farejador de fio ou ataque man-in-the-middle.

Quando você faz uma solicitação GET usando REST, os diferentes elementos na URL representam elementos mais refinados. Seu URL parece que você está retornando uma parte NEWPASSWD de um OLDPASSWD que faz parte de uma senha redefinida. Isso não faz nenhum tipo de sentido semântico. GETs não devem ser usados ​​para salvar dados.

Você deveria estar fazendo algo assim:

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

POST porque você está gravando dados e https porque você não deseja que ele cheire.

(Essa é realmente a segurança de barra baixa. O mínimo absoluto que você deve fazer.)

Gort the Robot
fonte
2
Isso não significaria hash de senhas no lado do cliente? Isso é recomendado?
Rowan Freeman
1
Nota: isso não permitirá impor requisitos de senha (comprimento, etc.). Isso pode não ser um problema no seu caso, embora seja uma prática frequente de segurança e às vezes exigida por algumas entidades.
Paul Draper
8
Você não está criando um novo registro, mas atualizando um registro existente (normalmente), portanto, deve ser PUT em vez de POST.
4
Isso não é muito RESTful. resetpassword não é um recurso e muito menos sub-recurso. No entanto, /user/joe/passwordé um pouco melhor, mas não é o ideal.
whirlwin
12
@CamilStaps Não, você não pode usar PUT, porque PUTé idempotente. Mas quando a senha for alterada de secretpara com supersecretêxito, a mesma solicitação falhará na segunda vez, portanto, POSTestá correta aqui. Obviamente, como @whirlwin disse, esse recurso não é bem conhecido.
Residuum
60

O esquema proposto tem problemas em várias áreas.

Segurança

Os caminhos de URL são freqüentemente registrados; colocar senhas unidas no caminho é uma prática ruim.

HTTP

As informações de autenticação / autorização devem aparecer no cabeçalho da autorização. Ou, potencialmente, para itens baseados em navegador, o cabeçalho Cookie.

DESCANSAR

Os verbos como resetpasswordno seu URL geralmente são um sinal claro de um paradigma de transferência de estado não representacional. Um URL deve representar um recurso. O que significa GET resetpassword? Ou excluir?

API

Esse esquema exige sempre o conhecimento da senha anterior. Você provavelmente desejará permitir mais casos; por exemplo, a senha é perdida.


Você pode usar a autenticação Básica ou Digest , que são esquemas bem compreendidos.

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

NEWPASSWD

Ele não coloca informações ultra-sensíveis no caminho e segue as convenções HTTP e REST.

Se você precisar permitir algum outro modo de autorização (por exemplo, algum token enviado por um canal verificado para redefinir a senha), você pode simplesmente usar um cabeçalho de Autorização diferente sem precisar alterar mais nada.

Paul Draper
fonte
4

Além da segurança, o problema é que não é uma abordagem muito RESTful.

OLDPASSWDe NEWPASSWDnão represente nada na hierarquia de recursos e, pior ainda, a operação não é idempotente.

Portanto, você só pode usar POSTcomo seu verbo e não deve incluir as duas senhas no caminho do recurso.

Biziclop
fonte
1
Você não está criando um novo registro, mas atualizando um registro existente (normalmente), portanto, deve ser PUT em vez de POST.
2
@CamilStaps Se estivesse apenas definindo a senha, poderia ser. Mas como presumivelmente a senha antiga também precisa ser verificada, ela torna a operação não idempotente e, portanto, PUTé desqualificada como verbo. Poderia ser rejeitado para trabalhar, PUTmas na sua forma atual não funciona.
biziclop
OLDPASSWD é uma informação de autenticação e não deve estar na URL.
Não necessariamente, é prática comum solicitar explicitamente a senha antiga em cima da autenticação.
biziclop
3

O problema é evitar senhas de texto sem formatação em suas solicitações. Existem duas opções para atender aos requisitos de serviço da web.

1. Hash do lado do cliente

  • Eu acho que você está armazenando suas senhas como, por exemplo, hash (senha + salt)
  • Você pode fazer o hash da nova senha com um sal no lado do cliente
  • Isso significa: Crie um novo salt no lado do cliente, crie um hash, por exemplo, hash (newPassword + newSalt)
  • Envie o novo hash criado mais o sal para o seu serviço da web
  • Envie a senha antiga também como hash (oldPassword + oldSalt)

2. Criptografia

  • Crie um recurso "chave única" (otk) para um usuário como / otk / john
  • Este recurso retorna uma chave única aleatória segura segura, por exemplo, kbDlJbmNmQ0Y0SmRHZC9GaWtRMW0ycVJpYzhMcVNZTWlMUXN6ZWxLdTZESFRs e um ID exclusivo, por exemplo, 95648915125
  • Seu serviço web repousante deve armazenar esse otk aleatório para a próxima comunicação segura com o ID 95648915125
  • Criptografe sua senha nova e antiga com o otk, por exemplo, AES (por motivos de segurança, você deve usar dois otks separados para a senha antiga e nova)
  • Envie as senhas criptografadas para o recurso de alteração de senha com o ID 95648915125
  • Uma combinação otk e ID pode funcionar apenas uma vez; portanto, você deve excluir essa combinação depois de procurar a senha
  • Possível opção melhor: envie a senha atual / antiga por Basic-Auth.

Nota: HTTPS é necessário para ambas as opções!

maz258
fonte
1
Por que criptografar duas vezes (seu esquema de criptografia com o OTK e HTTPS) a troca de senha? Qual é o vetor de ataque aqui que não é coberto pelo HTTPS?
Bart Friederichs
1
O único objetivo é evitar possíveis logs do servidor. Um corpo de solicitação HTTP (por exemplo, com uma nova senha em texto sem formatação) também pode ser registrado, embora HTTPS seja usado. Outro ataque possível é o uso de um certificado autoassinado, especialmente usado para fins internos.
maz258
2

Quais são os recursos de uma operação de redefinição de senha?

  1. Isso muda alguma coisa.
  2. Há um valor que está definido como.
  3. Somente algumas pessoas têm permissão para fazer isso (o usuário, um administrador ou qualquer um, talvez com regras diferentes sobre como fazê-lo).

O ponto 1 aqui significa que você não pode usar GET, você deve POST algo representando a operação de alteração de senha em um URI que representa um recurso que lida com alterações de senha ou PUT algo que representa a nova senha em um URI que representa a senha ou representa algo (por exemplo, o usuário) cuja senha é um recurso.

Geralmente, nós POSTAMOS, não menos importante, porque pode ser estranho COLOCAR algo que não podemos obter mais tarde e, é claro, não podemos obter a senha.

O ponto 2, portanto, serão os dados que representam a nova senha, no que é POSTADO.

O ponto 3 significa que precisaremos autorizar a solicitação, o que significa que, se o usuário for o usuário atual, precisaremos que a senha atual seja comprovada (embora não necessariamente receba a senha atual, se, por exemplo, um desafio baseado em hash foi usado para provar o conhecimento dele sem enviá-lo).

O URI deve, portanto, ser algo como <http://example.net/changeCurrentUserPassword>ou <http://example.net/users/joe/changePassword>.

Podemos decidir que queremos receber a senha atual nos dados do POST, bem como no mecanismo de autorização geral que está sendo usado.

Jon Hanna
fonte