Por que armazenar senhas de usuários?


10

Ocasionalmente, vejo perguntas perguntando como armazenar com segurança senhas de usuários para um aplicativo da Web (usando um RDBMS, não estou falando do Facebook ou Twitter). A resposta usual é "salte a senha e, em seguida, faça o hash com um algoritmo forte, como TDES ou SHA512".

Minha pergunta é: como usuário do RDBMS, por que eu deveria me preocupar com o armazenamento de senhas problemático, uma vez que a maioria dos mecanismos possui um mecanismo de autenticação interno.

Por exemplo, se algum usuário X quiser criar uma senha de usuário da conta Y no meu aplicativo da web, como está emitindo a seguinte consulta incorreta:

CREATE USER X WITH ENCRYPTED PASSWORD Y IN GROUP baseuser;

Então, dentro do meu aplicativo, o usuário pode abrir uma conexão com o banco de dados usando suas credenciais e não preciso me preocupar com o gerenciamento de senhas.

Vejo várias vantagens para esse método:

  • Se o RDBMS decidir que o algoritmo de criptografia precisa ser alterado, não preciso tocar em nada, apenas para aplicar as atualizações de segurança;
  • É fácil para mim gerenciar as autorizações dos usuários. Se um usuário é promovido para a função de administrador, basta adicionar o usuário ao grupo correspondente;
  • As injeções de SQL agora não têm sentido, pois eu gerencio permissões para permitir exatamente o que eu quero permitir para cada usuário no banco de dados (por exemplo, em um fórum como SO, adicionar novas postagens, responder a postagens, comentar e editar / excluir suas próprias perguntas / respostas / comentários);
  • Uma conta de usuário "anônima" pode ser usada para conexões não autenticadas ao meu aplicativo;
  • Cada usuário é o proprietário dos dados que ele forneceu.

Mas em praticamente todas as perguntas que vejo sobre esse tópico, parece haver um consenso geral de que não é assim que as coisas devem ser feitas. Minha pergunta é: por que?

Nota: O terceiro ponto é permitido pelas políticas no PostgreSQL e pelas políticas de segurança no Microsoft SQL Server. Percebo que esses conceitos são novos, mas enfim, agora que eles estão aqui, por que a técnica que descrevo não se torna a maneira padrão de lidar com as contas de usuários?


2
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Paul White 9

Aliás, a resposta usual está errada. Você deve salgar a senha e, em seguida, fazer o hash com um algoritmo lento , como bcrypt ou argon2.
Tgr 13/05

Respostas:


27

Porque, para muitos aplicativos, eles não querem contas de usuário individuais conectadas ao banco de dados. Os usuários / senhas / direitos / permissões são todos tratados na camada de aplicativo e uma única conta de serviço dedicada é usada para conectar-se ao back-end do banco de dados.

Como DBA, eu não quero gerenciar, no nível do banco de dados, os 10.000 usuários ativos de algum aplicativo Web de tamanho médio, ou talvez os mais de 2 milhões de usuários de algum aplicativo popular de repente.

Em um sentido muito real, essa é uma diferença na filosofia entre desenvolvedores de aplicativos e DBAs / desenvolvedores de banco de dados.

Muitos / a maioria dos desenvolvedores de aplicativos não deseja passar a responsabilidade pelos principais aspectos da funcionalidade do aplicativo e / ou regras de negócios para a camada do banco de dados. Em vez disso, eles visualizam o banco de dados como uma ferramenta para simplesmente armazenar e recuperar dados.

Em alguns casos, isso pode ser míope; muitos RDBMSs possuem recursos impressionantes que podem facilitar a vida dos desenvolvedores de aplicativos (segurança no nível de linha, índices colunares, armazenamento de filestream etc.).

Mas alguns desses recursos mais interessantes estão disponíveis apenas em versões mais recentes e as organizações nem sempre são rápidas em atualizar os ambientes existentes (consulte este gráfico a partir de 2014 ).

E, em outros casos, é preferível lidar com essas coisas na camada de aplicativos (e não apenas para a portabilidade da plataforma de banco de dados, acho que essa afirmação é exagerada).


2
Existe uma 'guerra santa' existente, quer a lógica de negócios entre no aplicativo ou no banco de dados. Se você criou um aplicativo em que toda a lógica de negócios (em particular a segurança) está no banco de dados (usando procedimentos armazenados, visualizações etc.), pode haver um argumento para utilizar os mecanismos de segurança do banco de dados, porque ninguém pode se conectar diretamente e contornar o problema. segurança. Concordo plenamente que você deve evitar a "reconstrução" de um mecanismo de segurança (por exemplo, login / senha) em uma tabela personalizada. Por que fazer isso quando você já possui o diretório ativo / segurança do O365.
Nick.McDermaid

11
@ Nick.McDermaid Eu gosto de ter certeza de que misturo a lógica de negócios entre o DB e o aplicativo de maneira justa para ajudar a eliminar o potencial de futuros desenvolvedores terem alguma idéia do que está acontecendo, mas também me ajuda a manter minha segurança no trabalho, pois ninguém é louco o suficiente para querer gerenciá-lo.
Der Kommissar

Jogue um serviço web e um servidor de aplicativos tomcat lá e você poderia trabalhar para IBM
Nick.McDermaid

8

A linha divisória está ficando um pouco fofa nesse ponto, mas eu consideraria usuários e permissões no nível do banco de dados como parte do esquema e não parte dos dados, e em geral os aplicativos não têm lugar para modificar o esquema (existem, como sempre, exceções a esta regra).

Prefiro não conceder ao (s) aplicativo (s) as permissões necessárias para gerenciar objetos de esquema (logins, usuários e permissões) à medida que novos usuários forem necessários e antigos forem embora, porque se esse aplicativo for hackeado, o invasor terá acesso a essas permissões facilitando ainda mais a abertura do banco de dados. No MS SQL Server, a menos que você esteja usando bancos de dados totalmente contidos, os logins são objetos no nível do servidor; portanto, você precisa distribuir direitos além do banco de dados de aplicativo único, tornando-o ainda mais arriscado.

Além disso, mesmo se você tiver contas de usuário por aplicativo no nível do banco de dados, ainda precisará de contas de usuário no nível do aplicativo para lidar com solicitações não autenticadas - ou seja, se o aplicativo precisar de informações do banco de dados antes que o usuário do aplicativo seja autenticado com êxito (talvez para exibir informações de status na tela de boas-vindas / login?).

Além disso, se a portabilidade entre os mecanismos de banco de dados for um objetivo (talvez seu aplicativo queira executar no mysql e no postgres?), Seus aplicativos precisarão abstrair as funções de gerenciamento de usuário / login de cada mecanismo, pois elas não são padrão entre eles. - se você estiver realizando esse esforço, poderá implementar as próprias senhas e obter as opções de gerenciamento que deseja, em vez de aceitar o menor conjunto de recursos comuns que os mecanismos oferecem.


11
Ok, então o "aplicativo não deve alterar o esquema" é, na minha opinião, o primeiro ponto positivo contra a técnica sugerida. No entanto, a conta anônima capaz de criar novos usuários precisa apenas criar novos usuários com acesso mais básico. Somente administradores podem promover usuários e descartar usuários, o que eu acho que é o que se deseja em aplicativos :-) Os dados gerais são visíveis ao usuário anônimo, portanto essa conexão pode ser usada para conteúdo em que a autenticação não é necessária. A portabilidade é também um bom ponto, mas eu acho que os comandos do usuário são agora parte do padrão (não tenho certeza, não era anos atrás)
Fabian Pijcke

A propósito, com essa configuração, se um hacker obtiver acesso ao banco de dados como usuário anônimo, ele poderá criar quantos usuários quiser, mas isso não é um problema de segurança; ele também pode inchar o banco de dados usando um dos seguintes métodos: as contas criadas, isso seria chato, mas novamente não problema de segurança (e ele poderia fazê-lo usando a interface padrão também de qualquer maneira, seria forma mais lenta, porém: p)
Fabian Pijcke

3
Concordo que, do ponto de vista da segurança, os usuários no nível do banco de dados seriam melhores. No entanto, isso é basicamente impossível de gerenciar com, por exemplo, lojas on-line onde você pode ter 50 milhões de usuários registrados. Cada um precisaria ser um usuário de banco de dados. Além disso: isso normalmente não funciona bem com aplicativos da Web usando um pool de conexões.
a_horse_with_no_name

6

Reiterando a pergunta

Por que armazenar senhas de usuários?

A resposta mais simples é que você precisa fazer isso. Você ainda armazena senhas no seu método alternativo. Só que você usa o sistema interno do banco de dados para gerenciar o armazenamento. Portanto, seu método é tão bom quanto o do seu banco de dados. Isso ainda pode ser melhor do que você pode fazer de outra forma, mas não está evitando o armazenamento. É realmente apenas evitando o armazenamento de codificação.

Também pode não ser melhor. Se eu comprometer um banco de dados e obter cópias dos arquivos, poderei acessar as tabelas diretamente. Portanto, não faz sentido usar um brilhante sistema de gerenciamento de senhas. Porque se alguém puder acessar a tabela no nível do sistema que contém as senhas ou senhas com hash, elas também poderão acessar os arquivos diretamente. Por que se preocupar com quebra de senhas quando você já possui os dados? Também não é possível criptografar os dados com facilidade. Cada usuário precisa poder acessá-lo. Portanto, a criptografia no nível do usuário não funcionará tão bem.

Mas o que você realmente parece estar perguntando é

Por que usar um esquema de autenticação em nível de aplicativo quando o banco de dados já possui um?

Segurança

Ignorando a possibilidade de você poder escrever uma versão mais segura, outros sugeriram vários motivos. Eu geralmente concordo. Mas há outra que ninguém mencionou ainda. Você está comprometendo a segurança do seu banco de dados.

Nesse sistema, todo usuário do seu aplicativo possui um usuário e uma senha do banco de dados. Claro, é um usuário limitado, mas ainda é um usuário que pode se conectar ao banco de dados. Mesmo se você usar a segurança no nível da linha, ainda estará permitindo que os usuários finais conheçam as informações de conexão do banco de dados. Se houver alguma exploração em que um usuário possa ter acesso uniforme no nível da tabela, você abriu seu banco de dados para atacar. E algumas explorações foram além disso para acessar o administrador.

Nas camadas da cebola de segurança, o sistema normal é:

  • Banco de dados, que permite apenas conexões de determinadas máquinas.
  • Banco de dados, permite apenas conexões como certas combinações de usuário / senha.
  • Servidores com permissões de banco de dados que permitem conexões de aplicativos.
  • Os aplicativos são executados no servidor.
  • Os aplicativos têm informações de conexão com o banco de dados com privilégios limitados.
  • Os aplicativos autenticam os usuários antes de permitir que eles modifiquem o banco de dados (exceto possivelmente para criar novas contas).

Neste sistema:

  • Os usuários finais conhecem combinações de usuário / senha que funcionarão no banco de dados, reconhecidamente com privilégios muito baixos.
  • Só é necessário compor um servidor ou fingir ser um servidor autorizado.

Perdemos todo o nível de segurança do aplicativo. E desistimos voluntariamente de parte da camada do banco de dados. Então, precisamos apenas de duas explorações:

  1. Falsifique ou comprometa um servidor autorizado.
  2. Aumente o acesso da conta de privilégios limitados.

Se pudermos fazer essas duas coisas, teremos acesso ao banco de dados. Então, passamos de três camadas para duas. (A terceira camada no sistema normal é que você precisa de um usuário válido para se conectar ao banco de dados.)

Você vai gerenciar muito essas contas. E se você cometer um erro? Em vez de limitar o usuário X a apenas determinadas linhas, você concede a todos acesso a uma tabela crítica. Normalmente, esse tipo de coisa é feita manualmente e existem apenas algumas delas, sendo fáceis de auditar. Mas em seu sistema, você pode estar executando milhares, milhões ou até bilhões de contas de usuário, cada uma com seu próprio acesso idiossincrático.

Nesse caso, o sistema do banco de dados é dimensionado para milhões ou bilhões de usuários? Se sim, e o número de regras? Se cada usuário adotar uma centena de regras ou mais sobre o que pode e não pode acessar, isso aumenta para um bilhão de usuários? Você está usando um sistema de pequena escala normalmente e o tornando em grande escala. Isso não vai necessariamente funcionar. Você pode encontrar limitações do sistema à medida que cresce.


5

O que você descreve tratará da autenticação, mas não da autorização (quais dados o usuário pode acessar) ou apenas no caso mais simples de uso.

Para continuar no seu exemplo com stackexechange: Como você tornaria a postagem excluída visível apenas para o usuário com alta reputação? Portanto, para regras de acesso, você ainda precisará de lógica no código. Em vez de compartilhar a lógica de acesso entre regras de banco de dados e regras de acesso de aplicativos, a maioria dos desenvolvedores prefere tê-las no mesmo local.

Você também precisará de uma tabela para armazenar as informações do usuário: um usuário não é apenas um nome de usuário + senha. Ele tem um email, uma reputação e assim por diante. Portanto, você ainda precisa da tabela de usuários, que você deve garantir que esteja sincronizada com a tabela de usuários do banco de dados, desde que você possa acessá-la.

Com sua solução, você pode simplesmente omitir uma coluna de senha, que não é tão difícil de preencher: existem boas bibliotecas e documentação sobre como lidar com senhas / hash.

Outro ponto: como você criaria um pool de conexões? Eu sei apenas jdbc (java <-> db), e você precisa fornecer um nome de usuário + senha para obter uma conexão, para que você possa agrupar.

Pensei em alguns outros pontos que serão mais difíceis / mais complicados de implementar do que com a maneira estabelecida:

  • manipulação de recuperação de senha
  • Dois anos após a implementação inicial, o gerente de produto solicita que você adicione suporte ao esquema oauth ou permita a autenticação de fator duplo

Algo como CREATE POLICY deleted_posts_high_rep ON posts FOR SELECT TO baseuser USING ((SELECT rep FROM users WHERE name = current_user()) > 10000)responde a sua primeira pergunta. Eu nunca disse que não precisava mais de uma tabela de usuários, e garantir a sincronização com a tabela de usuários do db é um pouco complicado, mas possível. O principal ponto da técnica discutida é a segurança. Quanto ao pool de conexões, trabalho com o OCaml e devo dizer que não entendo por que precisaria de um pool de conexões. Eu uso atualmente um mapa de hash associando valores de cookies para funções 0-árias de retornar ligações :-)
Fabian Pijcke

5
O conjunto de conexões é implementado para aprimorar o desempenho: com eles, você não precisa estabelecer uma conexão para cada solicitação do usuário (para economizar cpu / io / latência na criação do link tcp + toda a conexão e troca de autenticação com o banco de dados). Quanto à definição de política: seu banco de dados estará constantemente verificando o acesso em todos os acessos aos dados, mesmo quando você já os verificou uma vez. Também 'acesso negado ou proibido' não é o mesmo que 'OK, sem resultado'. Não tenho certeza se o pessoal da segurança preferiria a versão OK.
Thierry

3
Fabian, o pool de conexões é fundamental em qualquer tipo de escala, é realmente um padrão que você nem consideraria não usar. Aqui estão alguns pontos de referência apenas para você ter uma idéia de quão importante é: aumento de desempenho de 600x com o pool de conexões ( vladmihalcea.com/2014/04/17/the-anatomy-of-connection-pool ) e aumento de desempenho de mais de 4.000x com pool de conexão ( progress.com/tutorials/jdbc/… ). São várias ordens de diferença de magnitude, não é um "bom ter", os aplicativos parariam sem ele.
Ivan McA

11
Números interessantes. E, de fato, nunca vi um aplicativo que não estivesse usando conjuntos de conexões. Mas eu não os conhecia (e esperava) que eles acelerassem tanto. O outro aspecto que você obtém do pool de conexões é o controle de recursos: graças a eles, um grande aumento no cliente que tenta se conectar de uma só vez tornará seu aplicativo lento, mas seu banco de dados não sofrerá tanto impacto (se o pool estiver bem configurado) .
Thierry

Ok, acabei de pesquisar mais informações sobre pools de conexões e concordo que esse é um ponto muito válido assim que o site precisar lidar com mais de cem conexões por segundo, o que não é tão alto. Parece que toda conexão no PostgreSQL consome cerca de 10MiB de RAM! Eu nunca pensei que fosse tanto assim.
Fabian Pijcke

1

Outro ponto: muitos [1] aplicativos da web permitem que você se registre e crie uma conta de usuário on-line através do próprio aplicativo. O que significa que seu usuário anônimo (que deveria ter menos privilégios que alguém poderia assumir) precisa ter direitos para criar usuários e conceder privilégios!

OK, pode ser possível limitar quais privilégios ele poderia conceder e oferecer aos usuários de nível superior mais opções de concessão (não sei, porém - não é "conceder privilégios" um privilégio único frequentemente). Mas, ainda assim, significa colocar on-line algo com privilégios de concessão para que todos possam invadir. Tenho certeza de que meu DBA não gostaria de mim se fizesse isso :)

Sei que, em um nível, esse problema existe de qualquer maneira, mas você tem camadas de defesa, especialmente para excluir dados, se você não permitir que o aplicativo Web gerencie usuários de banco de dados.

Além disso, isso impediria qualquer uso de conexões de banco de dados agrupadas ou reutilizadas.

[1] A maioria eu diria, mas não há números para apoiá-lo

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.