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?
Respostas:
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).
fonte
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.
fonte
Reiterando a pergunta
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 é
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 é:
Neste sistema:
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:
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.
fonte
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:
fonte
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 :-)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
fonte