Aplicativos em PHP/Apêndices/Segurança
Algumas Dicas sobre Segurança no Desenvolvimento de Aplicativos
Segurança em sites
Esta é muito importante:
Você sabia que geralmente os arquivos de site em PHP ou qualquer outra linguagem podem ser baixados?
Ou seja, faz-se download dos nossos fontes e qualquer um então poderá ver seu conteúdo e o pior ainda é que algumas vezes existem informações que não devem ser vistas por qualquer um.
Neste site, um forum do Joomla, o cara ensina muitos truque que realmente fecham esta brecha de segurança:
http://forum.joomla.org/index.php?topic=124708.0;wap2
Você deverá criar um arquivo .htaccess (caso use o Apache como servidor web) e copiar para o raiz web (DocumentRoot) do seu site.
Faça um teste antes e depois para ver se consegue fazer o download de um arquivo. Use o wget ou outro programa similar que faz download direto do servidor. Faça isso antes e depois de copiar o arquivo.
PHP Security Guide
Fundado em Janeiro de 2005, o PHP Security Consortium (PHPSC) é um grupo internacional formado por experts em PHP dedicados a promover práticas de segurança para a comunidade de PHP. Membros do PHPSC procuram educar desenvolvedores PHP sobre segurança através de uma variedade de recursos, incluindo documentação, ferramentas e padrões. Veja no linque abaixo o guia oferecido online pelo grupo:
http://phpsec.org/projects/guide/
Securing PHP: Step-by-Step
http://www.securityfocus.com/print/infocus/1706
Apache 2 with SSL/TLS: Step-by-Step, Part 1 (Infocus)
Artigo em 3 partes
http://www.securityfocus.com/infocus/1818
Securing Apache 2: Step-by-Step
http://www.securityfocus.com/infocus/1786
Securing MySQL: step-by-step (Infocus)
http://www.securityfocus.com/infocus/1726
Securing Apache: Step-by-Step (Infocus)
http://www.securityfocus.com/infocus/1694
Secure Your Apache With mod_security
http://www.howtoforge.com/apache_mod_security
Senhas
Ataques contra senhas normalmente usam o método da força bruta (brute force), portanto evite senhas simples e que constem de dicionários.
Evite senhas do tipo:
- Somente numéticas ou somente caracteres
- Seu nome, nomes de filhos, número de identidade, data de nacimento, CEP ou outro constante de dicionários
- Senhas com poucos caracteres
- Não use a mesma senha para todos os servidores
Senha recomendada:
- Com 15 ou mais caracteres
- Misture letras, algarismos e caracteres especiais, maiúsculas e minúsculas
- Muito importante: use uma senha que você possa se lembrar
- Informações
Cuidado com o Acesso em Lan houses
Segundo a PNAD 2005 (IBGE) 18,6% das residências brasileiras têm computadores e destes apenas 13,7% com acesso à Internet. Muita gente então acessa a Internet em Lan hauses.
Os especialistas em segurança recomendam não efetuar transações bancárias ou qualquer outro tipo onde sejam requeridos dados pessoais, como CPF, cartão de crédito, RG, etc.
Existem diversas formas de se monitorar os passos de alguém na Internet (programas mal intencionados) e até mesmo as câmaras existentes nas lan houses, se em mãos erradas podem representar perigo para os usuários.
Cuidado com Notebooks
Os notebooks também estão na mira dos criminosos da Internet. Evite dados sigilosos nos mesmos ou use uma boa criptografia para proteger seus dados. Use senhas fortes para o acesso ao notebook e faça backup regularmente de suas informações.
Instale e tenha sempre atualizados: bom antivirus, antispy e firewall.
Esteja atento para as atualizações do sistema operacional e de todos os softwares importantes.
- Código
- Ofereça uma quantidade fixa (3) de tentativas de login. Após as 3 o login deve ser desabilitado, por segurança.
Segurança em Aplicativos PHP
Este texto é formado pela tradução de partes de alguns textos em inglês (vide Referências) e de alguns exemplos de código e recomendações que adicionei.
Em primeiro lugar devemos atentar para uma boa análise e projeto da aplicação. Da qualidade destes depende a qualidade da aplicação. Então devemos planejar o banco de dados cuidadosamente: tabelas, relacionamentos, campos, tipos de dados, etc. Mais importantes ainda em termos de segurança é a criação de usuários que tenham acesso somente ao aplicativo e com privilégios somente para suas operações com nomes e senhas seguras, como também usuários do banco com respectivas permissões e permissões do sistema operacional.
As aplicações Web contam com formas populares de acesso global a dados, a serviços e a produtos. Enquanto este acesso global é uma das grandes vantagens da Web, qualquer regra de segurança nesses aplicativos também é globalmente exposta e freqüentemente explorada. É muito fácil escrever aplicações que contém regras de segurança. Vide aplicações famosas como phpMyAdmin, PHPShop e FreeTrade.
Algumas Recomendações a favor da Segurança
- Evitar uso de Variáveis quando acessando Arquivos
Cuidado com as funções:
- readfile
- fopen
- file
- include
- require
Caso decida assim mesmo utilizar, tome precauções. Uma boa precaução é que o valor das variáveis seja definido com o uso da função "define", garantindo que seu conteúdo seja conhecido e testado.
- Checar os nomes dos arquivos em uma lista de nomes válidos. Veja um exemplo: $valid_pages = array( "umapagina.php" => "", "outra.php" => "", "mais.php" => "");
if (!isset($valid_pages[$page])) { // Aborte o script //Você deve provavelmente escrever uma mensagem de log aqui também die("Requisição inválida"); }
- Caso realmente precise usar variáveis de um browser, cheque os valores das variáveis usando um código como o seguinte: if (!(eregi("^[a-z_./]*$", $page) && !eregi("\\.\\.", $page))) { // Abortar o script //Você deve provavelmente escrever uma mensagem de log aqui também die("Requisição inválida "); }
- Use as configurações de variáveis “allow_url” e “open_basedir” para limitar as localizações de onde os arquivos podem ser abertos.
Utilizar Caracteres de Escape em Instruções SQL
// Usar a função para testar a existência de registro if (record_exists($query)) { echo "Acesso ragantido"; } else { echo "Acesso negado"; }
O uso da diretriz “magic_quotes_gpc” setada para On no php.ini insere caracteres de escape nas Super Globais $_POST, $_GET e COOKIES.
Veja o exemplo abaixo do site oficial:
<?php echo get_magic_quotes_gpc(); // 1 echo $_POST['lastname']; // O\'reilly echo addslashes($_POST['lastname']); // O\\\'reilly
if (!get_magic_quotes_gpc()) {
$lastname = addslashes($_POST['lastname']);
} else {
$lastname = $_POST['lastname'];
}
echo $lastname; // O\'reilly $sql = "INSERT INTO lastnames (lastname) VALUES ('$lastname')"; ?>
Use addslashes e stripslashes, caso esteja usando variáveis globais (register_globals = On) e não esteja usando magic_quotes_gpc como no exemplo abaixo (adiciona antes de inserir no banco e remove antes de exibir na tela):
// Recebendo do Form
$thisCodigo_curso = addslashes($_REQUEST['thisCodigo_cursoField']); $thisNome = addslashes($_REQUEST['thisNomeField']);
$sqlQuery = "INSERT INTO curso (codigo_curso , nome ) VALUES ('$thisCodigo_curso' , '$thisNome' )";
// Enviando para a tela
eval('function cndstrips($str) {
return '.(get_magic_quotes_gpc()?'stripslashes($str)':'$str').';
}' );
Obs.: O uso simultâneo de magic_quotes_gpc = On com addslashes e stripslashes gera problemas, assim como não podemos usar register_globals = On simultaneamente com as Super Globais $_POST, $_GET.
Caso esteja usando variáveis que espera números para seu conteúdo nas instruções SQL, esteja seguro de que realmente contém números. Existem diversas funções em PHP incluindo sprintf, ereg e is_long para realizar a checagem. Também podemos utilizar o JavaScript para checar as entradas logo no formulário.
Não Confiar em Variáveis Globais
Se register_globals = On no php.ini, então o PHP criará variáveis globais para cada requisição GET, POST e variáveis Cookie.
Preste bastante atenção nas seguintes áreas:
- Código de checagem de autenticação e permissão
- Uso de variáveis antes de serem inicializadas. (Podemos ajustar error_reporting para ser alertados sempre que se usar variáveis não inicializadas.
- Uso de variáveis designadas para ser usadas por requisições GET ou POST.
Veja da documentação oficial:
Exemplos error_reporting()
<?php
// Desativa o relatório de todos os erros error_reporting(0);
// Reporta erros simples error_reporting(E_ERROR | E_WARNING | E_PARSE);
// Reportar E_NOTICE pode ser bom também (para reportar variáveis não iniciadas // ou eros de digitação em nomes de variáveis ...) error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
// Reportar todos os erros exceto E_NOTICE // Este é o valor padrão no php.ini error_reporting(E_ALL ^ E_NOTICE);
// Reporta todos os erros (bitwise 63 deve ser usado no PHP 3) error_reporting(E_ALL);
// O mesmo que error_reporting(E_ALL); ini_set('error_reporting', E_ALL);
?>
Possíveis Correções e Melhorias
Desabilitar “register_globals” no php.ini. Após esta mudança somente podemos acessar entradas de formulários usando $_POST ou $_GET. Fica mais trabalhoso um pouco mas bem mais seguro (vale a pena).
Cuidado com o recurso “Esqueceu a Senha” de formulários de Login.
Escreva código para inicializar todas as variáveis globais.
Evite Falsos Uploads
Examine todos os scripts que respondem a upload de arquivos.
Use as funções is_uploaded_file e move_upload_file que permitem ao programador estar seguro de que está trabalhando com os devidos arquivos enviados.
Caso não tenha certeza de estar rodando em uma versão atual do PHP, configure upload_tmp_dir que executa checagem de entrada que o arquivo que estamos trabalhando está neste diretório.
Idéias Adicionais
- Encripte ou use hashes de senhas quando armazenando. A função md5 é útil para isso.
Exemplo retirado do www.phpbrasil.com:
//BY ADEMIR BATISTA PEREIRA
$senha = "ribafs";
$resultado = md5($senha);
$resultado2 = bin2hex($senha);
if($senha == ""){
$texto = "";
} else{
$texto = "
Criptografia tipo A para: $senha é $resultado";
echo $texto;
$texto2 = "
Criptografia tipo B para: $senha é $resultado2";
echo $texto2;
}
Outro exemplo do mesmo site:
<?php //***** enc - Encriptador de String //***** Autor: Ricardo Antonio Duarte - ricardo NOSPAM banhado.com
function enc($string){
if((isset($string)) && (is_string($string))){ $enc_string = base64_encode($string); $enc_string = str_replace("=","",$enc_string); $enc_string = strrev($enc_string); $md5 = md5($string); $enc_string = substr($md5,0,3).$enc_string.substr($md5,-3); }else{ $enc_string = "Parâmetro incorreto ou inexistente!"; } return $enc_string;
} //***** Fim do enc
//***** des - Desencriptador de String //***** Autor: Ricardo Antonio Duarte - ricardo NOSPAM banhado.com
function des($string){
if((isset($string)) && (is_string($string))){ $ini = substr($string,0,3); $end = substr($string,-3); $des_string = substr($string,0,-3); $des_string = substr($des_string,3); $des_string = strrev($des_string); $des_string = base64_decode($des_string); $md5 = md5($des_string); $ver = substr($md5,0,3).substr($md5,-3); if($ver != $ini.$end){ $des_string = "Erro na desencriptação!"; } }else{ $des_string = "Parâmetro incorreto ou inexistente!"; } return $des_string;
} //***** Fim do des
echo "Enc: ".enc("ribafs")."
";
echo "Des: ".des("53czZWYilmc1d1");
?>
Não armazene números de cartões de crédito
Force senhas seguras. No mínimo exija senhas com 8 caracteres de tamanho e que contenham algum caractere não alfanumérico.
Avoid SQL injection
This is a function which will format the passed string depending of it's specified to be a number or a string, in order to avoid problems with SQL injections in scripts.
Type: code fragment
Version:
Requires:
Added by: bto (email author)
Entered: 19/08/2004
Last modified: 08/12/2003
Rating - (fewer than 3 votes)
Views 3024
<?php function ToDBString($string, $link, $isNumber=false) {
//If $isNumber==true we are specting a number if($isNumber) { //A correct number must be composed of: // - Zero or more integers followed by a decimal point and one or more integers (i.e.: .9 (0.9) or 9.9) // - One or more integers followed by a decimal point. (i.e.: 9. (9.0)) // - One or more integers (i.e.: 999) if(preg_match("/^d*[.,']d+|d+[.,']|d+$/A", $string)) //If it's a correct number we change the colon, quote or point ("'", "," or ".") by a decimal piont. return preg_replace( array( "/^(d+)[.,']$/" , //9. "/^(d*)[.,'](d+)$/" //.9 or 9.9 ), array( "\1." , "\1.\2" ) , $string); else //If it's not a correct number we show ERROR die("ERROR: Not a number"".$string."""); } else //If $string is a string ($isNumber==false) we return "'$string'" // correctly escaped (in this version I also strip HTML tags and modify some things in the string, change it if you wish). return "'".mysql_real_escape_string(htmlentities(strtoupper(trim(strip_tags($string)))), $link)."'";
} ?>
Example
$link=mysql_db_connect("HOST", "USER", "PASSWORD");
$foo=ToDBString($_POST["string"], $link); $bar=ToDBString($_POST["number"], $link, true);
$result=mysql_db_query("DATABASE", "SELECT * FROM secret WHERE foo LIKE $foo AND bar=$bar", $link);
//If $_POST["foo"] or $_POST["bar"] are a string of this kind: " OR 1=1" and we don't use ToDBString we will show all the info of the table!!!!
- Usar sempre extensões tipo PHP em todos os scripts.
Nunca usar extensões .inc, .class, etc. Podemos usar assim: nome.inc.php.
Arquivos com extensões .inc, .class e similares ao serem abertos no browser exibem todo o seu conteúdo, inclusive senhas de banco, trechos de código PHP e outras, já script .php, antes de serem abertos no browser são processados pelo servidor web e só chega ao browser o resultado em HTML.
Não insira Conteúdo Sigiloso no raiz do Aplicativo
Imagens de proteção de senha, documentos e outras imagens devem ficar fora do raiz do aplicativo.
Alternativamente armazene no banco de dados.
Proteger os diretórios usando características do Apache, como arquivos .htaccess, que previnem o acesso direto ao conteúdo dos diretórios.
Muita atenção aos Serviços de Hospedagem
Estes servidores compartilham suas máquinas com diversos usuários, que têm acesso aos arquivos. Como também podem criar um arquivo de session (que armazena em /tmp por default), que pode conter maliciosos users e senhas para bypassar sua autenticação.
Verifique sempre as informações do PHP (phpinfo()) do servidor, caso use esses serviços.
Idealmente use um servidor dedicado ao invés.
Assegure-se de que o servidor ative safe_mode no php.ini.
As permissões dos arquivos são outro ponto importante. Devem somente ser lidos pelo web Server. No UNIX use algo como 711.
Use sempre validações para garantir que os usuários realmente entram com informações válidas.
is_long, is_numeric, etc.
Fuja ou Evite Entrada de Usuários quando Construindo Comandos de Strings
Funções como exec e eval são muito flexíveis mas requerem muito cuidado, pois os usuários podem entrar com comandos inesperados.
Evite ao máximo as funções
- eval
- preg_replace (quando usada com /e deve interpretar parâmetros como código PHP)
- exec
- passthru
- system
- popen
- (pode ser usado para executar comandos)
Além do Código (Um projeto de segurança forte)
Projeto de Aplicação Segura
- Considere o uso do HTTPS para encriptar transmissões.
- Considere restringir acesso aos diretórios usando .htaccess do Apache. Checar através das variáveis de ambiente do PHP, como $REMOTE_ADDR
- Usar pacotes de segurança existentes, como o PHPLib.
Todas as linguagens tem seus pontos fracos, mas tomando diversos cuidados preventivos e atentando para as boas regras de segurança, estes pontos podem ser protegidos. Seguindo os passos mostrados aqui podemos desenvolver um código mais seguro que o usual.
Um dos mais importantes conceitos a se ter em mente em termos de segurança é o de nunca confiar que o usuário irá digitar exatamente o que se espera que ele digite.
Nunca inclua, requeira ou abra um arquivo cujo nome seja baseado em entrada do usuário, sem antes checar
- Seja muito cuidados quando usando “register_globals = On”.
Isso foi feito para tornar o uso do PHP algo fácil, o que realmente aconteceu. Mas em contrapartida trouxe sérios problemas de segurança. A partir da versão 4.2.0 este parâmetro já vem setado como Off por default. Neste caso para pegar o valor de variáveis lançadas pelo formulário devemos utilizar as superglobais $_POST, $_GET, $_REQUEST, $_COOKIE. Ou $_SESSION.
O recomendado é que se trabalhe com “register_globals = Off”.
Usando “error_reporting = E_ALL” no php.ini recebemos uma notificação sempre que tentarmos chamar variáveis que ainda não tenham sido definida. Sabemos que o PHP não exige a definição de variáveis, mas recomenda-se que nos acostumemos a definir todas as variáveis e inclusive a iniciá-las, em termos de segurança.
Nunca execute consultas a bancos sem usar funções de escape
O PHP traz ativa por default, uma proteção contra a entrada de caracteres especiais nos formulários, que é o “magic_quotes_gpc = On”.
- Nunca confie em dados de fontes externas.
- Toda entrada de usuário deve ser validada e formatada para garantir a segurança.
Protegendo Arquivos e Diretórios com .htaccess via Apache
- Para ativar o .htaccess em todo o servidor web, edite o httpd.conf e adicione:
<Directory />
Options FollowSymLinks Indexes AllowOverride AuthConfig
</Directory>
- Então criamos o arquivo .htaccess existente no diretório raiz e adicionamos tags de acordo com nossos propósitos. Exemplos:
- Restringindo o acesso por IP/Host
- Deixa a Intranet acessar
Order allow,deny allow from 192.168.0. deny from all
Ou
- Deixa todo mundo acessar, menos o IP 192.168.0.25
Order deny,allow deny from 192.168.0.25 allow from all
- Restringindo o acesso por user/senha
$ mkdir /etc/httpd/auth
$ cd /etc/httpd/auth
$ htpasswd -c acesso hugo
New password:
Re-type new password:
Adding password for user hugo
$ htpasswd acesso eitch
New password:
Re-type new password:
Adding password for user eitch
$ htpasswd acesso sakura
New password:
Re-type new password:
Adding password for user sakura
- Agora criar o .htaccess:
AuthName "Acesso Restrito à Usuários"
AuthType Basic
AuthUserFile /etc/httpd/auth/acesso
require valid-user
AuthUserFile /etc/httpd/auth/acesso – onde estão as senhas e users.
- Opções para arquivos e diretórios específicos:
- Restringe o arquivo_secreto.html somente para o IP 192.168.0.30
<Files arquivo_secreto.html>
Order allow,Deny Allow from 192.168.0.30 Deny from all
</Files>
Restringe o diretório admin para utilizar senhas
<Directory /admin>
AuthName "Acesso Restrito à Usuários" AuthType Basic AuthUserFile /etc/httpd/auth/acesso AuthGroupFile /etc/httpd/auth/grupos require group admin
</Directory>
- Nega o acesso dos clientes ao .htaccess (bom colocar no httpd.conf)
- - Vem com a configuração padrão do Apache
<Files ~ "^\.ht">
Order allow,deny Deny from all
</Files>
Prevenindo Injeções SQL
Uma Função para prevenir tais injeções.
Esta é uma função que deve formatar a string passada dependendo de se foi especificado um número ou uma string, para evitar o problema com injeções SQL em scripts.
Autor: bto (no site
<?php function ToDBString($string, $link, $isNumber=false) {
//If $isNumber==true we are specting a number if($isNumber) { //A correct number must be composed of: //Zero or more integers followed by a decimal point and one or more integers (i.e.: .9 (0.9) or 9.9) // - One or more integers followed by a decimal point. (i.e.: 9. (9.0)) // - One or more integers (i.e.: 999)
if(preg_match("/^d*[.,']d+|d+[.,']|d+$/A", $string)) //If it's a correct number we change the colon, quote or point ("'", "," or ".") by a decimal piont. return preg_replace( array( "/^(d+)[.,']$/" , //9. "/^(d*)[.,'](d+)$/" //.9 or 9.9 ), array( "\1." , "\1.\2" ) , $string); else //If it's not a correct number we show ERROR die("ERROR: Not a number"".$string."""); } else // If $string is a string ($isNumber==false) we return "'$string'" // correctly escaped (in this version I also strip HTML tags and modify some things in the string, change it if you wish). return "'".mysql_real_escape_string(htmlentities(strtoupper(trim(strip_tags($string)))), $link)."'";
} ?>
Example
$link=mysql_db_connect("HOST", "USER", "PASSWORD");
$foo=ToDBString($_POST["string"], $link); $bar=ToDBString($_POST["number"], $link, true);
$result=mysql_db_query("DATABASE", "SELECT * FROM secret WHERE foo LIKE $foo AND bar=$bar", $link);
//If $_POST["foo"] or $_POST["bar"] are a string of this kind: " OR 1=1" and we don't use ToDBString we will show all the info of the table!!!!
Escrevendo Código Robusto em PHP
Formulários com Imagens anti-spam - Captcha
Classe Very Simple Captcha
http://www.phpclasses.org/browse/package/4108.html
Gerando imagens de segurança (CAPTCHA)
Aartigo de Thiago Felipe testa no PHPBrasil:
http://phpbrasil.com/articles/print.php/id/1463
PHP Captcha Security Images
Sistema de imagens para controle de spam em PHP
http://www.white-hat-web-design.co.uk/articles/php-captcha.php
Link para download - http://www.white-hat-web-design.co.uk/articles/captcha.zip
PHP Captcha
http://milki.erphesfurt.de/captcha/
Download - http://milki.erphesfurt.de/captcha/captcha-2.0.tgz
iCaptcha - CAPTCHA validation asking questions about pictures
http://www.phpclasses.org/browse/package/3960.html
Toughen Forms' Security with an Image
Artigo detalhado descrevendo como criar um form seguro tipo captcha
http://www.sitepoint.com/print/toughen-forms-security-image
PHPSecInfo
Utilitário semelhante à função phpinfo(), que mostra as falhas de segurança do PHP e aponta sugestões para correção.
http://phpsec.org/projects/phpsecinfo/
Como tornar o PHP mais seguro
http://www.jorgeoliveira.com/2006/04/03/como-tornar-o-php-mais-seguro/
Convert mail to image (guard mail) from spam
http://snippets.dzone.com/posts/show/4283
<?php if(empty($_GET['sid'])){ //write html for image $mail='example@domain.com'; $text=base64_encode(serialize($mail)); echo '<img src="imgmail.php?sid='.$text.'" /><br/>'; }else{ //show coded mail image $text=$_GET['sid']; header("Content-type: image/gif"); echo mail_to_image($text); } function mail_to_image($ctext='no mail'){ $text=unserialize(base64_decode($ctext)); $size=strlen($text)*8; $im = @imagecreate($size, 20) or die("Cannot Initialize new GD image stream"); $background_color = imagecolorallocate($im, 255, 255, 255); $text_color = imagecolorallocate($im, 0, 0, 0); imagestring($im,3, 5, 5, $text , $text_color); return imagegif($im); } ?>
Parser HTML inject
Parser HTML inject. Bom para quando temos a possibilidade de receber em uma textarea código HTML ou JavaScript malicioso.
Código fuente / Source code :
function parsearHTMLInjectado($texto) { return nl2br( htmlentities($texto) ); }
Fonte: http://snippets.dzone.com/posts/show/4347
Senhas seguras: algumas técnicas e script para teste
http://www.revistaphp.com.br/print.php?id=153
Referências
1 – http://www.onlamp.com/lpt/a/3305 - Ten Security Checks for PHP
2 - http://www.onlamp.com/lpt/a/4045 - PHP Security
3 - http://www.devshed.com/c/a/PHP/PHP-Security-Mistakes/ - PHP Security Mistakes
4 - http://www.devin.com.br/eitch/htaccess/ - Uso e Segurança com o .htaccess
5 - http://www.securiteam.com/securityreviews/5DP0N1P76E.html - SQL injeções
6 - http://www.zend.com/codex.php?id=1405&single=1 - SQL injeções
7 – http://www.imasters.com.br/imprimir.php?cn=292&cc=44
8 – http://www.imasters.com.br/imprimir.php?cn=293&cc=44
9 – http://www.imasters.com.br/imprimir.php?cn=319&cc=44
10 - Escrevendo o código seguro de PHP
11 - - Writing Secure PHP