Como proteger senhas de banco de dados em PHP?

404

Quando um aplicativo PHP faz uma conexão com o banco de dados, é claro que geralmente precisa passar um login e senha. Se eu estiver usando um único login de permissão mínima para o meu aplicativo, o PHP precisará conhecer esse login e senha em algum lugar. Qual é a melhor maneira de proteger essa senha? Parece que apenas escrevê-lo no código PHP não é uma boa ideia.

user18359
fonte
11
Para ser totalmente seguro, você precisará configurar uma conexão SSL, caso contrário, qualquer pessoa na sua rede ainda poderá cheirar a senha digitada.
Charles Ma
11
Você quer dizer as senhas do usuário ou a senha do banco de dados usada na cadeia de conexão?
Ozgur Ozcitak 18/09/08
4
Senha do banco de dados usada na cadeia de conexão. Obrigado!
user18359

Respostas:

238

Várias pessoas interpretaram isso incorretamente como uma pergunta sobre como armazenar senhas em um banco de dados. Isso esta errado. É sobre como armazenar a senha que lhe permite obter a base de dados.

A solução usual é mover a senha do código-fonte para um arquivo de configuração. Em seguida, deixe a administração e a proteção desse arquivo de configuração para os administradores do sistema. Dessa forma, os desenvolvedores não precisam saber nada sobre as senhas de produção e não há registro da senha no seu controle de origem.

user11318
fonte
8
Obrigado. Se eu entendi isso corretamente, o arquivo php terá uma inclusão no arquivo de configuração, permitindo que ele use a senha. por exemplo, eu crio um arquivo chamado 'app1_db_cfg.php' que armazena o login, a senha e o nome do banco de dados. Em seguida, minha página application.php inclui 'app1_db_cfg.php' e estou no negócio!
user18359
28
Concordo que a configuração precisa ser adequadamente protegida. No entanto, saber como fazer isso é assunto de administradores de sistemas, não de desenvolvedores. Não concordo com o valor da criptografia forte neste caso. Se você não pode proteger seu arquivo de configuração, o que faz você pensar que pode proteger suas chaves?
user11318
10
Prefiro usar uma conta de banco de dados que só tenha acesso ao banco de dados a partir do servidor web. E então eu não me incomodo em criptografar a configuração, apenas a armazeno fora da raiz da web.
Gnud 25/04/09
11
Eu uso uma variável de ambiente apache para definir o caminho para que até o caminho do arquivo seja desconhecido no código-fonte. Isso também bem permite ter uma senha diferente para desenvolvimento e produção com base no que as configurações do Apache estão no serverm
geedew
15
Lembre-se de que mesmo os arquivos armazenados fora do diretório acessível pela web devem ser lidos pelo script que os utiliza. Se alguém incluir esse arquivo, depois despejar os dados, ele verá a senha.
Rick Mac Gillis
104

Se você estiver hospedando no servidor de outra pessoa e não tiver acesso fora do seu webroot, sempre poderá colocar sua senha e / ou conexão com o banco de dados em um arquivo e bloqueá-lo usando um .htaccess:

<files mypasswdfile>
order allow,deny
deny from all
</files>
Kellen
fonte
3
Obrigado, era exatamente o que eu estava procurando.
David Gladfelter 11/08/09
28
Definitivamente, mas se alguém tiver acesso ao shell, toda a sua conta estará comprometida.
Kellen
4
Essa é uma prática ruim, porque você pode acidentalmente confirmar suas credenciais em um repositório.
Porlune
2
@ Ankit: Se for possível para um não amigável fazer upload de um arquivo para o servidor e executá-lo, o servidor não está configurado corretamente.
Kellen
6
@Porlune: Os desenvolvedores devem fazer com que seu sistema de controle de versão ignore o arquivo de senha, ou seja, usando um .gitignore. Mas sim, deve-se tomar cuidado com arquivos que contêm dados confidenciais.
Kellen
45

A maneira mais segura é não ter as informações especificadas no seu código PHP.

Se você estiver usando o Apache, significa definir os detalhes da conexão no arquivo httpd.conf ou no arquivo de hosts virtuais. Se você fizer isso, poderá chamar mysql_connect () sem parâmetros, o que significa que o PHP nunca exibirá suas informações.

É assim que você especifica esses valores nesses arquivos:

php_value mysql.default.user      myusername
php_value mysql.default.password  mypassword
php_value mysql.default.host      server

Então você abre sua conexão mysql assim:

<?php
$db = mysqli_connect();

Ou assim:

<?php
$db = mysqli_connect(ini_get("mysql.default.user"),
                     ini_get("mysql.default.password"),
                     ini_get("mysql.default.host"));
Lars Nyström
fonte
11
Verifique os valores adequados de ini_get ('valores padrão') php.net/manual/en/class.mysqli.php
Val
4
sim, mas qualquer usuário (ou um hacker que esteja abusando de scripts php mal escritos) pode ler a senha via ini_get().
Marki555
11
@ Marki555 but any user (or a hacker abusing badly written php script) can read the password via ini_get()Como você lida com isso?
tonix 22/05
2
Marki555 está dizendo que um invasor que pode executar código PHP também pode chamar funções PHP, o que é obviamente verdadeiro e impossível de se fazer. Eu também gostaria de acrescentar que não sigo mais os conselhos que dou nesta resposta, mas, em vez disso, uso variáveis ​​de ambiente. O conceito é semelhante: não armazene suas credenciais no código, mas injete-as de alguma forma. Realmente não importa se você usa ini_get()ou getenv().
Lars Nyström 23/05
2
@DeepBlue Se você pode injetar ini_get (), também pode injetar file_get_contents (anypath). Desde que o php tenha uma maneira de acessar a senha, qualquer código malicioso também será.
varesa
40

Armazene-os em um arquivo fora da raiz da web.

da5id
fonte
29
E também, como mencionado em outro lugar, fora do controle de origem.
Frank Farmer
6
poderíamos incluí-lo? por exemplo, em PHP, podemos fazer include('../otherDirectory/configfile.conf')?
mtk
11
Você está sugerindo armazenar credenciais fora do wwwroot. Ok, eu entendo o histórico de segurança. Mas como ele deve ser armazenado no controle de versão (configuração de amostra)? Normalmente, o wwwroot é a raiz do git repo, portanto, se houver algo fora - ele estará fora do VC. Imagine um novo desenvolvedor tentando configurar uma instância local para o desenvolvimento - como ele deve saber mágica como "pegue esse arquivo, copie-o para fora e preencha-o"?
The Godfather
@TheGodfather A idéia é que um novo desenvolvedor tenha suas próprias credenciais para seu próprio ambiente de desenvolvimento. Embora seja uma boa prática ter um leia-me com instruções ou comentários no código, indicando como você deve configurá-lo (mas não os dados reais).
PhoneixS
@TheGodfather, arquivo leia-me?
Pacerier
35

Para sistemas extremamente seguros, criptografamos a senha do banco de dados em um arquivo de configuração (que é protegido pelo administrador do sistema). Na inicialização do aplicativo / servidor, o aplicativo solicita ao administrador do sistema a chave de descriptografia. A senha do banco de dados é então lida no arquivo de configuração, descriptografada e armazenada na memória para uso futuro. Ainda não é 100% seguro, pois é armazenado na memória descriptografado, mas você precisa chamá-lo de 'seguro o suficiente' em algum momento!

pdavis
fonte
85
e se o administrador morrer?
Radu Murzea
36
@RaduMurzea isso é ridículo. Quando você ouviu falar de administradores de sistemas morrendo? Eles são como McDonalds, eles simplesmente aparecem / desaparecem do nada!
ILikeTacos
13
@ Radu Murzea Basta ter 2 ou mais administradores, então você tem paridade como uma matriz de ataque. As chances de mais de uma unidade falhar ao mesmo tempo são muito menores.
element11
5
e quando os servidores reiniciarem? Quanto tempo leva para ativar o administrador para que ele digite a senha em ..etc.etc. lol
John Hunt
11
Não sabe ao certo o que você quer dizer com 'armazenado na memória'. Geralmente, os aplicativos Web PHP não armazenam nada na memória por mais tempo que o necessário para responder a uma solicitação individual para ver uma página.
precisa saber é
15

Esta solução é geral, pois é útil para aplicativos de código aberto e fechado.

  1. Crie um usuário do SO para seu aplicativo. Veja http://en.wikipedia.org/wiki/Principle_of_least_privilege
  2. Crie uma variável de ambiente do SO (sem sessão) para esse usuário, com a senha
  3. Execute o aplicativo como esse usuário

Vantagens:

  1. Você não verificará suas senhas no controle de origem por acidente, porque não pode
  2. Você não irá estragar acidentalmente as permissões de arquivo. Bem, você pode, mas isso não afetará isso.
  3. Só pode ser lido pela raiz ou por esse usuário. O Root pode ler todos os seus arquivos e chaves de criptografia de qualquer maneira.
  4. Se você usa criptografia, como está armazenando a chave com segurança?
  5. Works x-platform
  6. Certifique-se de não passar o envvar para processos filho não confiáveis

Este método é sugerido por Heroku, que é muito bem-sucedido.

Neil McGuigan
fonte
11

se for possível criar a conexão com o banco de dados no mesmo arquivo em que as credenciais estão armazenadas. Inline as credenciais na instrução de conexão.

mysql_connect("localhost", "me", "mypass");

Caso contrário, é melhor desmarcar as credenciais após a instrução connect, porque as credenciais que não estão na memória não podem ser lidas na memória ;)

include("/outside-webroot/db_settings.php");  
mysql_connect("localhost", $db_user, $db_pass);  
unset ($db_user, $db_pass);  
Bob Fanger
fonte
9
Se alguém tiver acesso à memória, você está ferrado de qualquer maneira. Isso é inútil segurança falsa. Fora da raiz da web (ou pelo menos protegida por um .htaccess se você não tiver acesso acima da raiz da web) é a única opção segura.
uliwitness
2
@uliwitness - É como dizer que só porque alguém pode romper a fechadura do centro de operações da sua rede com uma tocha de acetileno significa que a porta também é uma segurança falsa. Manter informações confidenciais vinculadas ao escopo mais rígido possível sempre faz sentido.
Luke A. Leber
11
Que tal echo $ db_user ou imprimir o $ db_pass? Mesmo os desenvolvedores da mesma equipe não devem conseguir descobrir as credenciais de produção. O código não deve conter nada imprimível sobre as informações de login.
Mohammed Joraid
8

Suas escolhas são meio limitadas, pois você diz que precisa da senha para acessar o banco de dados. Uma abordagem geral é armazenar o nome de usuário e a senha em um arquivo de configuração separado, em vez do script principal. Guarde isso fora da árvore principal da web. Ou seja, se houver um problema de configuração da web que deixe seus arquivos php simplesmente sendo exibidos como texto, em vez de serem executados, você não expôs a senha.

Fora isso, você está nas linhas corretas com acesso mínimo para a conta que está sendo usada. Adicione a isso

  • Não use a combinação de nome de usuário / senha para mais nada
  • Configure o servidor de banco de dados para aceitar apenas conexões do host da Web para esse usuário (o host local é ainda melhor se o banco de dados estiver na mesma máquina) Dessa forma, mesmo que as credenciais estejam expostas, elas não serão úteis a ninguém, a menos que tenham outro acesso ao servidor. máquina.
  • Oculte a senha (até o ROT13 o fará), pois não haverá muita defesa se alguns tiverem acesso ao arquivo, mas pelo menos impedirá a visualização casual.

Pedro

Vagnerr
fonte
8

Se você estiver usando o PostgreSQL, ele procurará ~/.pgpasssenhas automaticamente. Veja o manual para mais informações.

Jim
fonte
8

Anteriormente, armazenávamos usuário / senha do banco de dados em um arquivo de configuração, mas desde então atingimos o modo paranóico - adotando uma política de Defesa em Profundidade .

Se seu aplicativo estiver comprometido, o usuário terá acesso de leitura ao seu arquivo de configuração e, portanto, é possível que um cracker leia essas informações. Os arquivos de configuração também podem ser apanhados no controle de versão ou copiados nos servidores.

Mudamos para o armazenamento de usuário / senha nas variáveis ​​de ambiente definidas no Apache VirtualHost. Essa configuração é legível apenas pelo root - espero que seu usuário do Apache não esteja executando como root.

O problema disso é que agora a senha está em uma variável PHP global.

Para mitigar esse risco, temos as seguintes precauções:

  • A senha está criptografada. Estendemos a classe PDO para incluir lógica para descriptografar a senha. Se alguém ler o código onde estabelecemos uma conexão, não será óbvio que a conexão está sendo estabelecida com uma senha criptografada e não com a própria senha.
  • A senha criptografada é movida das variáveis ​​globais para uma variável privada. O aplicativo faz isso imediatamente para reduzir a janela em que o valor está disponível no espaço global.
  • phpinfo()está desabilitado. PHPInfo é um alvo fácil para obter uma visão geral de tudo, incluindo variáveis ​​de ambiente.
Courtney Miles
fonte
6

Coloque a senha do banco de dados em um arquivo e torne-o somente leitura para o usuário que está servindo os arquivos.

A menos que você tenha algum meio de permitir apenas que o processo do servidor php acesse o banco de dados, isso é tudo que você pode fazer.

Chris
fonte
5

Se você está falando sobre a senha do banco de dados, ao contrário da senha proveniente de um navegador, a prática padrão parece ser colocar a senha do banco de dados em um arquivo de configuração PHP no servidor.

Você só precisa ter certeza de que o arquivo php que contém a senha possui permissões apropriadas. Ou seja, deve ser legível apenas pelo servidor web e por sua conta de usuário.

Jason Wadsworth
fonte
11
Infelizmente, o arquivo de configuração do PHP pode ser lido por phpinfo () e se alguém deixar algum script de teste para trás, um invasor sortudo poderá ler a senha. Provavelmente, é melhor deixar a senha de conexão em um arquivo fora da raiz do servidor web. Então, a única maneira de acessá-lo é com um shell ou executando código arbitrário, mas nesse cenário toda a segurança é perdida de qualquer maneira.
MarioVilas
5

Colocá-lo em um arquivo de configuração em algum lugar é como costuma ser feito. Apenas certifique-se de:

  1. proibir o acesso ao banco de dados de qualquer servidor fora da sua rede,
  2. tome cuidado para não mostrar a senha acidentalmente aos usuários (em uma mensagem de erro ou através de arquivos PHP serem servidos acidentalmente como HTML, etc.)
Marijn
fonte
5

Nós resolvemos desta maneira:

  1. Use o memcache no servidor, com conexão aberta de outro servidor de senhas.
  2. Salve no memcache a senha (ou mesmo todo o arquivo password.php criptografado) mais a chave de descriptografia.
  3. O site chama a tecla memcache que contém a senha do arquivo de senhas e descriptografa na memória todas as senhas.
  4. O servidor de senhas envia um novo arquivo de senhas criptografadas a cada 5 minutos.
  5. Se você estiver usando o password.php criptografado em seu projeto, faça uma auditoria para verificar se esse arquivo foi tocado externamente - ou visualizado. Quando isso acontece, você pode limpar automaticamente a memória e fechar o servidor para acesso.
Asi Azulay
fonte
4

Um truque adicional é usar um arquivo de configuração separado do PHP que se parece com isso:

<?php exit() ?>

[...]

Plain text data including password

Isso não impede que você defina as regras de acesso corretamente. Porém, no caso de seu site ser hackeado, um "require" ou um "include" sairá do script apenas na primeira linha, tornando ainda mais difícil obter os dados.

No entanto, nunca deixe os arquivos de configuração em um diretório que possa ser acessado através da web. Você deve ter uma pasta "Web" contendo seu código de controle, css, fotos e js. Isso é tudo. Qualquer outra coisa entra em pastas offline.

e-satis
fonte
mas como o script php lê as credenciais armazenadas no arquivo?
Christopher Mahan
3
Você usa fopen (), como em um arquivo de texto comum.
e-satis
2
@ e-satis ok, ele impedirá o hacker de fazer require/ includemas como impedir de fazer fopen?
dmnc
"isso não impede que você defina as regras de acesso corretamente"
e-satis
@ e-satis, isso é bem inteligente. me pergunto por que ninguém pensou nisso. No entanto , ainda vulnerável ao problema de cópia do editor. feross.org/cmsploit
Pacerier
4

A melhor maneira é não armazenar a senha!
Por exemplo, se você estiver em um sistema Windows e se conectar ao SQL Server, poderá usar a Autenticação Integrada para se conectar ao banco de dados sem uma senha, usando a identidade do processo atual.

Se você precisar se conectar com uma senha, primeiro criptografe -a, usando criptografia forte (por exemplo, usando AES-256 e, em seguida, proteja a chave de criptografia, ou usando criptografia assimétrica e que o sistema operacional proteja o certificado) e, em seguida, armazene-a em um arquivo de configuração (fora do diretório da web) com ACLs fortes .

Ávido
fonte
3
Não adianta criptografar a senha novamente . Alguém que possa acessar a senha não criptografada também poderá acessar a frase secreta necessária para descriptografar a senha. No entanto, usar ACLs e .htaccess é uma boa idéia.
uliwitness
2
@uliwitness Eu acho que você pode ter entendido errado - o que você quer dizer com "criptografar novamente "? É apenas a única criptografia. E você não deseja usar senhas (destinadas ao uso humano) para criptografá-la, um gerenciamento de chaves bastante forte, por exemplo, protegido pelo sistema operacional, de forma que o simples acesso ao sistema de arquivos não conceda acesso à chave.
Avid
3
Criptografia não é mágica - em vez de proteger a chave AES com ACLs, você pode simplesmente armazenar a senha lá. Não há diferença entre acessar a chave AES ou a senha descriptografada; nesse caso, a criptografia é apenas óleo de cobra.
MarioVilas
@MarioVilas whaat? Se a senha estiver criptografada, com a chave de criptografia protegida pelo sistema operacional, como não há diferença? Criptografia não é mágica - apenas compacta todo o sigilo na chave de criptografia menor. Dificilmente o óleo de cobra, neste contexto, está apenas movendo todo esse sigilo para o sistema operacional.
Avid
6
@AviD, por que o sistema operacional pode proteger a chave, mas não os dados em si? Resposta: ele pode proteger os dois, portanto a criptografia não ajuda. Seria diferente se apenas os dados estivessem armazenados e a chave de criptografia derivada, por exemplo, de uma senha que tivesse que ser digitada por um usuário.
MarioVilas