Aplicativos em PHP/Apêndices/Segurança: diferenças entre revisões

[edição não verificada][edição não verificada]
Conteúdo apagado Conteúdo adicionado
Ribafs (discussão | contribs)
Nova página: == 16.4 - Segurança == '''Algumas Dicas sobre Segurança no Desenvolvimento de Aplicativos''' '''1 - Senhas''' Ataques contra senhas normalmente usam o método da força bruta (...
 
Ribafs (discussão | contribs)
Sem resumo de edição
Linha 1:
 
== 16.4 - Segurança ==
 
Linha 31 ⟶ 30:
 
 
'''2 - Informações'''
 
'''Cuidado com o Acesso em Lan houses'''
3 - Código
 
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.
 
 
 
'''3 - 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 Confir 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 = "<br>Criptografia tipo A para: $senha é $resultado";
echo $texto;
$texto2 = "<br>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")."<br>";
 
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!!!!
 
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