Sie sind auf Seite 1von 41

1

Aplicativo Web Seguro em PHP

Utilitários em PHP que ajudam a monitorar a segurança em sites PHP:


- phpSecInfo - http://phpsec.org/projects/phpsecinfo/index.html
- securityScanner - http://securityscanner.lostfiles.de/

Adotar uma política séria de senhas:


- senhas contendo letras e algarismos com o mínimo de 8 dígitos.
- para reforçar adicionar símbolos e misturar maiúsculas com minúsculas.
- trocar as senhas regularmente.
- utilizar rotinas em JavaScript que monitoram o nível de segurança das senhas escolhidas
em sua criação e orientar/estimular os usuários para que adotem senhas seguras.

Evitar acesso físico de pessoas estranhas aos servidores.

Usar Session para reforçar a Segurança

Segurança em PHP

Para tornar um aplicativo em PHP mais seguro temos que tomar várias providências. E isso não se
aplica exclusivamente ao PHP, mas a praticamente todas as linguagens ou ferramentas de
programação. Os cuidados aqui dizem respeito mais a aplicativos web.

Com PHP é possível acessar arquivos, executar comandos e abrir conexões de rede no servidor.
Essas propriedades fazem qualquer coisa executando em um servidor web inseguras por padrão.

Com a escolha correta de opções de configuração em tempo de compilação ou de execução, e


práticas corretas de programação, ela pode dar a combinação exata de liberdade e segurança que
você precisa.

Existem combinações de opções e configurações do servidor que resultam em uma instalação


insegura.

Um sistema completamente seguro é virtualmente impossível de se conseguir, então uma


abordagem freqüentemente usada em segurança é um compromisso entre risco e usabilidade. Se
cada variável enviada pelo usuário precias de duas formas de validação biométrica (como
escaneamento de retina e impressão digital), você teria um nível de checagem extremamente alto.
Demoraria meia hora para preencher um formulário mais ou menos complexo, o que incentivaria os
usuários a achar maneiras de burlar a segurana ou de abandonar o aplicativo.

A melhor segurança é frequentemente aquele preenche os requerimentos sem obstruir o usuário de


fazer o seu trabalho, ou sobrecarregando o programador com complexidade excessiva. De fato,
alguns ataques de segurança exploram esse tipo de segurança super-produzida, que tende a degradar
com o tempo.

Um sistema é tão bom quanto o elo mais fraco na corrente.

Quando estiver testando, tenha em mente que você não será capaz de testar todas as possibilidades
nem mesmo para as páginas mais simples.
2

Usando a diretiva open_basedir você pode controlar e restringir quais diretórios o PHP tem
permissão de usar. Você também pode configurar area exclusivas para o Apache, restringir todas as
atividades web para arquivo que não sejam de algum usuário ou do sistema.
Checagem mais segura do nome do arquivo

<?php
$username = $_SERVER['REMOTE_USER']; // usando um mecanismo de autenticação
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";

$filepath = "$homedir/$userfile";
if (!ctype_alnum($username) || !preg_match('/^(?:[a-z0-9_-]|\.(?!\.))+$/iD', $userfile)) {
die("Bad username/filename");
}
//etc...
?>
Validando entrada corretamente
<?php
$file = $_GET['file'];

// Whitelisting possible values


switch ($file) {
case 'main':
case 'foo':
case 'bar':
include '/home/wwwrun/include/'.$file.'.php';
break;
default:
include '/home/wwwrun/include/main.php';
}
?>

Modelo de Armazenamento Criptografado

SSL/SSH protege dados transitando de um cliente para o servidor, mas não protege os dados
guardados em um banco de dados. SSL é um protocolo on-the-wire.

Uma vez que o atacante ganha acesso direto ao seu banco de dados(desviando do servidor web), os
dados armazenados podem ser expostos ou sofre uso nocivo, a não ser que a informação seja
protegida pelo banco em si. Criptografa os dados é uma boa maneira de diminuir essa ameaça, mas
poucos bancos de dados oferecem esse tipo de criptografia de dados.

A maneira mais fácil de contornar esse problema é primeiro criar seu próprio pacote de criptografia,
e então usá-lo no seus scripts PHP. O PHP pode ajudá-lo com várias extensões, tais como Mcrypt e
Mhash, cobrindo uma grande variedade de algoritmos de criptografia. O script criptografa os dados
antes de inserí-los no banco de dados e descriptografa quando os recupera. Veja as referência para
outros exemplos de como criptografia funciona.

No caso de dados realmente escondidos, se sua representação crua não é necessária (ex.: não será
3

mostrada), usar uma função de hash pode ser levada em consideração. Um exemplo bem conhecido
disso é guardar o hash MD5 de uma senha no banco de dados ao invés da senha em si. Veja também
crypt() e md5().

Usando campo de senha hasheado


<?php

// guardando hash da senha


$query = sprintf("INSERT INTO users(name,pwd) VALUES('%s','%s');",
pg_escape_string($username), md5($password));
$result = pg_query($connection, $query);

// consultando se o usuário enviou a senha correta


$query = sprintf("SELECT 1 FROM users WHERE name='%s' AND pwd='%s';",
pg_escape_string($username), md5($password));
$result = pg_query($connection, $query);

if (pg_num_rows($result) > 0) {
echo 'Welcome, $username!';
} else {
echo 'Authentication failed for $username.';
}

?>

crypt

crypt - codificação de string (hashing) de mão única

string crypt ( string $str [, string $salt ] )

crypt() retornará uma string criptografada usando o algoritmo de encriptação Unix Standard DES-
based ou ou algoritmos alternativos disponíveis no sistema. Os argumentos são uma string para ser
criptografada e uma string opcional para basear em qual encriptação está. Veja a página de
encriptação Unix para mais informação.

Se o argumento salt não é fornecido, um argumento aleatório será gerado pelo PHP.

Alguns SO suportam mais de um tipo de codificação.

Em sistemas onde a função crypt() suporta variados tipos de codificação, as seguintes funções são
definidas para 0 ou 1 a depender se um dado tipo está disponível:

* CRYPT_STD_DES - Codificação Standard DES-based com um salt de 2 caracteres


* CRYPT_EXT_DES - Codificação Extended DES-based com um salt de 9 caracateres
* CRYPT_MD5 - Codificação MD5 com um salt de 12 caracteres começando com $1$
* CRYPT_BLOWFISH - Codificação Blowfish com um salt de 16 caracteres começando com
$2$
4

Nota: Não há função de decodificação, desde que crypt() utiliza uma algorimo de mão única.

crypt() exemplos
<?php
$password = crypt("My1sTpassword"); // let salt be generated

# You should pass the entire results of crypt() as the salt for comparing a
# password, to avoid problems when different hashing algorithms are used. (As
# it says above, standard DES-based password hashing uses a 2-character salt,
# but MD5-based hashing uses 12.)
if (crypt($user_input, $password) == $password) {
echo "Password verified!";
}
?>

md5

md5 - Calcula o "hash MD5" de uma string

string md5 ( string $str [, bool $raw_output ] )

Calcula o "hash MD5" de str usando » RSA Data Security, Inc. MD5 Message-Digest Algorithm, e
devolve esse hash. O hash é um número hexadecimal 32-character. Se o opcional raw_output está
definido para TRUE, então o md5 compreende que ao invés disso retorna um "raw binary format"
com comprimento 16.

Nota: O parâmetro opcional raw_output foi adicionado no PHP 5.0.0 e por definição é FALSE

Um exemplo de md5()
<?php
$str = 'apple';

if (md5($str) === '1f3870be274f6c49b3e31a0c6728957f') {


echo "Would you like a green or red apple?";
exit;
}
?>

Injeção de SQL

Muitos desenvolvedores web não sabem de como consultas SQL podem ser manipuladas, e
presumem que uma consulta de SQL é um comando confiável. Significa que consultas SQL são
capazes de passar indetectado por controles de acesso, portanto desviando da autenticação padrão e
teestes de autorização, e algumas vezes consultas SQL podem permitir acesso à comando em nível
do sistema operacional do servidor.

Injeção direta de comandos SQL é uma técnica onde um atacante cria ou alterar comandos SQL
existentes para expor dados escondidos, ou sobrescrever dados valiosos, ou ainda executar
5

comandos de sistema perigosos no servidor. Isso é possível se a aplicação pegar a entrada do


usuário e combinar com parâmetros estáticos para montar uma consulta SQL. Os exemplos a seguir
são baseados em estórias verdadeiras, infelizmente.

Devido a faulta de validação de entrada e conectar ao banco de dados usando o super-usuário ou um


usuário que pode criar usuário, o atacante pode criar um super-usuário no seu banco de dados.

Dividinto o result set em páginas ... e criando super-usuários (PostgreSQL)


<?php

$offset = $argv[0]; // Cuidado, sem validação de entrada!


$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
$result = pg_query($conn, $query);

?>

Técnicas para Evitar Ataques

Nunca confie em nenhum tipo de entrada, especialmente aquela que vem do lado do cliente, mesmo
que venha de um combobox, um campo de entrada escondido (hidden) ou um cookie. O primeiro
exemplo mostra como uma consulta inocente pode causar desastres.

* Nunca conecte ao banco de dados como um super-usuário ou como o dono do banco de dados.
Sem use usuários personalidados com privilégios bem limitados.
* Verifique se uma entrada qualquer tem o tipo de dados experado. O PHP tem um grande
número de funções de validação de entrada, desde as mais simples encontrada em Funções de
Variáveis e em Funções de Tipo de Caracteres (ex.: is_numeric(), ctype_digit() respectivamente)
além de usar o suporte à Expressões Regulares Compatível com Perl.
*

Se a aplicação espera por entradas numéricas, considere verificar os dados com a função
is_numeric(), ou silenciosamente mudar o seu tipo usando settype(), ou usar a representação
númeria usando a função sprintf().

Uma maneira mais segura para compor consultas de paginação


<?php

settype($offset, 'integer');
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";

// por favor perceba o %d na string de formato, usando %s seria inútil


$query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;",
$offset);

?>

* Adicione aspas para cada valor não numérico especificado pelo usuário que será passado para
o banco de dados com as funções de caracteres de escape (ex.: mysql_escape_string(),
sql_escape_string(), etc.). Se um mecanismo de escape de caracter específico par ao seu banco de
6

dados não for disponível, as funções addslashes() e str_replace() podem ser úteis (dependendo do
tipo de banco de dados). Veja o o primeiro exemplo. Como o exemplo mostra, adicionar aspas para
a parte estática da consulta não é suficiente, fazendo com que a consulta seja facilmente atacada.
* Não imprima qualquer informação específica do banco de dados, especialmente sobre o
esquema, custe o que custar. Veja também Relatório de Erros e Funções de Tratamento e Relatório
de Erros.
* Você pode usar stored procedures e cursores previamente definidas para abstrair acesso aos
dados para que os usuário não acessem tabelas ou view diretamente, mas essa solução pode ter
outros impactos.

Além disso, você ganha em relatar consultas ou dentro do script ou no próprio banco de dados, se
esse suportar. Obviamente, o relatório é para previnir qualquer tentativa danosa, mas pode ser útil
para ajudar a rastrar qual aplicação foi atacada. O resitro não é útil em si, mas atráves da informação
que ele contem. Mais detalhes geralmente é melhor que menos.

Relatando Erros

Com relação a segurança, relatório de erros é uma faca de dois gumes. Pode beneficiar o aumento
da segurança, ou fornecer informaçao ao atacante.

Uma tática padrão de ataque involve determinar como um sistema funciona entrando dados
incorretos e checando os tipos e contextos dos erros que são retornados. Isso permite que um
cracker sonde por informações sobre o servidor, para determinar possíveis fraquezas.

Os erros do PHP que são retornados normalmente podem ser úteis para um desenvolvedor que está
tentando depurar um script, indicando coisas como a função ou arquivo que falhou, o arquivo PHP
no qual a falha ocorreu, e o número da linha de código causadora da falha. Toda essa informação
pode ser explorada. Não é incomum para um desenvolvedor PHP usar show_source(),
highlight_string(), ou highlight_file() como medidas de depuração, mas em um site de produção,
isso pode expor variáveis ocultas, sintaxe incorreta, ou outra informações perigosas. Especialmente
perigoso é rodar código de fontes conhecidas com tratadores de depuração integrados, ou usar
técnicas de depuração comuns.

Existem três soluções principais para esse problema. A primeira é verificar exaustivamente todas as
funções, e tentar compensar pelo volume dos erros. A segunda é desabilitar completamente os
relatórios de erros no código de produção. A terceira é usar as funções personalizávies de tratamento
de erro do PHP para criar seu próprio tratador de erro. Dependendo da sua política de segurança,
você pode perceber que todas são aplicáveis à sua situação.

Uma maneira de perceber esse problema antes que o pior aconteça é usar a diretiva
error_reporting(), para ajudar a aumentar a segurança de seu código e achar uso de variáveis que
pode ser perigoso. Ao testar o seu código, antes de colocar em produção, com E_ALL, você pode
rapidamente encontrar áreas onde suas variáveis podem sofrer alterações nocivas ou modificações
quaisquer. Uma vez que estiver pronto para produção, você deve ou desabilitar mensagens de erro
completamente configurando a diretiva error_reporting() com o valor 0, ou desligar o envio de erros
usando a opção display_errors do arquivo php.ini, para evitar sondagem do seu código. Se você
escolher a segunda opção, você deve também definir o caminho para o arquivo de registro usando a
diretiva error_log, e ligar a diretiva log_errors.
7

Encontrado variáveis perigosas com E_ALL


<?php
if ($username) { // Not initialized or checked before usage
$good_login = 1;
}
if ($good_login == 1) { // If above test fails, not initialized or checked before usage
readfile ("/highly/sensitive/data/index.html");
}
?>

Usando a diretiva Register Globals

Tenha em mente que a diretiva em si não é insegura, o uso incorreto dela é que é.

Quando ligada, a diretiva register_globals criará para seus scripts vários tipos de variáveis, como as
variáveis oriundas de formulários HTML. Isso, combinado com o fato de que o PHP não requer
inicialização de variáveis, significa que é mais fácil escrever código inseguro. Foi uma decisão
difícil, mas a comunidade do PHP decidiu que, por padrão, essa diretiva deveria ser desabilitada.
Quando habilitada, é possível usar variáveis sem saber ao certo de onde elas vieram. Variáveis
internas que são definidas no script em si se misturam com dados enviados pelos usuários e
desabilitando a diretiva muda isso. Vamos demonstrar um exemplo de uso incorreto de
register_globals:

Exemplo de uso incorreto de register_globals = on


<?php
// define $authorized = true somente se o usuário for autenticado
if (authenticated_user()) {
$authorized = true;
}
// Porque nós não inicializamos $authorized como false, ela pode ser
// definida através de register_globals, como usando GET auth.php?authorized=1
// Dessa maneira, qualquer um pode ser visto como autenticado!
if ($authorized) {
include "/highly/sensitive/data.php";
}
?>

Dados Enviados pelo Usuário

A maior fraqueza na maioria dos programas PHP não é inerente a linguagem em si, mas meramente
um problema de código escrito desconsiderando segurança. Por essa razão, você sempre deve
investir um pouco de tempo considerando as implicações de um certo pedaço de código, para ter
certeza que o dano possível se uma variável não esperada for submetida ao mesmo.

Example#1 Uso Perigoso de Variáveis


<?php
// remove um arquivo do diretório home do usuário... ou talvez
// de outra pessoa?
8

unlink ($evil_var);

// Escreve registro do acesso... ou talvez uma entrada em /etc/passwd?


fwrite ($fp, $evil_var);

// Executa algo trivial... ou rm -rf *?


system ($evil_var);
exec ($evil_var);

?>

Você sempre deve examinar cuidadosamente seu código para se assegurar que quaisquer variáveis
sendo enviadas do navegador web estão sendo checadas de maneira correta, e faz a si mesmo as
seguintes perguntas:

* Seu script só afetará os arquivos desejados?


* Dados incomuns ou indesejados podem ser utilizados?
* Esse script pode ser usado de maneiras não intencionadas?
* Ele pode ser usado in conjunto com outros scripts de maneira negativa?
* As transações serão registradas adequadamente?

Respondendo essas perguntas adequadamente enquanto escrevendo o script, ao invés de depois,


previne a re-escrita indesejada quando você precisar aumentar a segurança. Começando com essa
linha de raciocínio, você não garante a segurança do seu sistema, mas pode ajudar a aumentá-la.

Você também pode considerar desligar as diretivas register_globals, magic_quotes, ou outras


configurações convenientes que pode confundir você em relação a validade, origem, ou valor de
uma variável qualquer. Trabalhar com PHP em modo error_reporting(E_ALL) também pode ajudar
avisando sobre variáveis sendo usadas antes de serem checadas ou inicializadas (então você pode
previnir que dados incomuns sejam operados).

Magic Quotes

Magic Quotes é um processo de inserção automática de caracteres de escape (\) em todos os dados
indo para o script PHP. É preferível escrever código com essa opção desligada e adicionar esses
caracteres manualmente quando necessário.

Quando ligada, qualquer ' (aspas simples), " (aspas duplas), \ (barra invertida) e NULL será
colocado uma barra-invertida antes (' vira \') automaticamente. Isso é identico ao que a função
addslashes() faz.

Existem três diretivas relacionadas a Magic Quotes:

A equipe de desenvolvimento já anunciou o final dessas diretivas.

* magic_quotes_gpc Afeta os dados de requisições HTTP GET, POST, e COOKIE). Não pode ser
alterada em tempo de execução e tem o valor padrão on no PHP. Veja também
get_magic_quotes_gpc().
* magic_quotes_runtime Se habilitada, a maioria das funções que retorna dados de uma fonte
9

externa, incluindo bancos de dados e arquivos de texto, serão alterados. Pode ser alterado em tempo
de execução e tem o valor padrão de off no PHP. Veja também set_magic_quotes_runtime() e
get_magic_quotes_runtime().
* magic_quotes_sybase Se habilitada, uma aspa simples é usada como caracter de escape quando
encontrar outra aspa simples (' vira ''). Se ligada, sobrepõe completamente magic_quotes_gpc. Ligar
ambas as diretivas significa que apenas aspas simples são substituídas por ''. Aspas duplas, barras
invertidas e NULLs permanecerão intocados e não serão escapados. Veja também ini_get() para
pegar esse valor.

* Conveniência Para inserir dados em um banco de dados, magic quotes essencialmente


executa a função addslashes() em todos os dados enviados por GET, POST e COOKIE
automaticamente.

* Performance Como nem todos os dados escapados são inseridos em um banco de dados,
existe uma perda de performance por escapar todos os dados. Chamar funções de escape (como
addslashes()) em tempo de execução é mais eficiente. Embora o arquivo php.ini-dist habilita essas
diretivas por padrão, php.ini-recommended desabilita ela. Essa recomendação é principalmente por
razões de performance.
* Inconviniência Porque nem todos os dados precisam ter caracteres de escape inseridos, é
irritante ver caracteres de escape onde não deviam. Por exemplo, mandar um e-mail por um
formulário, e ver um monte de \' na mensagem. Para consertar isso, pode ser necessário o uso
excessivo da função stripslashes().

Desabilitando Magic Quotes

A diretiva magic_quotes_gpc só pode ser desabilita em nível de sistema, e não em tempo de


execução. Em outras palavras, uso da função ini_set() não é uma opção.

Desabilitando magic quotes no lado do servidor

Um exemplo que configuração dessa diretiva para Off (Desligada) no arquivo php.ini. Para detalhes
adicionais, leia a seção do manual entitulada Como mudar os valores das configurações.

; Magic quotes
;

; Magic quotes para dados vindos via GET/POST/Cookie.


magic_quotes_gpc = Off

; Magic quotes para dados gerados em tempo de execução,ex.: dados vindo de SQL, de chamadas à
exec(), etc.
magic_quotes_runtime = Off

; Usar magic quotes no estilo Sybase (escapar ' com '' ao invés de \').
magic_quotes_sybase = Off

Esse método é ineficiente então é preferível configurar as diretivas apropriadas em outros lugares.

Example#2 Disabilitando magic quotes em tempo de execução


10

<?php
if (get_magic_quotes_gpc()) {
function stripslashes_deep($value)
{
$value = is_array($value) ?
array_map('stripslashes_deep', $value) :
stripslashes($value);

return $value;
}

$_POST = array_map('stripslashes_deep', $_POST);


$_GET = array_map('stripslashes_deep', $_GET);
$_COOKIE = array_map('stripslashes_deep', $_COOKIE);
$_REQUEST = array_map('stripslashes_deep', $_REQUEST);
}
?>

Mantendo-se Atualizado

PHP, como qualquer outro sistema grande, está sobre constante revisão e melhoramento. Cada
versão nova normalmente incluirá mudanças, sejam grandes ou pequenas, para melhorar a
segurança e reparar falhas, erros de configuração, e outros problemas que podem afetar a segurança
geral e estabilitade do seu sistema.

Como outras linguagens de script e programas de nível de sistema, a melhor política é atualizar
frequentemente e manter-se atento as novas versões e suas mudanças.

Alguns Cuidados Básicos

0 – Instalação e configurações dos softwares. Atentar para detalhes que reforças a segurança tantos
nos softwares quanto nos servidores onde estão instalados.
1 – Elaborar com cuidado e zelo o projeto do banco e do aplicativo, atentando para:
- padronizações
- documentação
- ter alguém responsável por se manter atualizado em relação às tecnologias envolvidas no
projeto: PHP, SGBD, ferramentas utilizadas, etc. Procurar atualizar com frequência.
- Cuidado ao aproveitar programas ou códigos de terceiros. Analise bem seu conteúdo.
- Nunca utilizar includes com extensão texto (inc, txt, etc)
2 - Backup – Efetuar cópias de segurança regularmente é algo que não pode faltar.
3 – Caso não vá usar desabilite o acesso via FTP
4 – Utilizar boas ferramentas que ajudam a monitorar a segurança como:
- phpSecInfo
- securityScanner
5 – Sempre que for utilizar senha com hash MD5, procurar no site Reverse MD5 para ver se já não
está no banco de dados.
6 – Antes de armazenar no banco de dados dados entrados pelo usuário tomar vários cuidados:
11

- usar funções que controlam caracteres especiais como htmlspecialchars e ...


- ao exibir os dados vindos do banco filtrar adequadamente com htmlspecialchars_decode
7 – Utilizar validações com JS para facilitar para o usuário, já filtrar alguma coisa mas
paralelamente sempre ter validações também com PHP.
8 – cuidar adequadamente do tratamento de erros. Na fase de testes deixá-los todos ALL e na de
produção enibir todos os erros.
9 – Quando possível utilizar conexões seguras tipo SSL (encriptam a comunicação cliente -
servidor).
10 – Configurar alguns parâmetros do php.ini que reforçam a segurança:
- register_globals = Off (isso impede o uso de variáveis globais e exige $_POST, $_GET,
$_REQUEST, etc) evitando o uso de variáveis globais diretamente.
- magic_quotes_gpc = On (cuida de dados entrados com GET/POST/Cookie, evitando
Injeção SQL, escapando caracteres especiais)
- magic_quotes_runtime = Off

Podemos usar as funções

get_magic_quotes_gpc()
get_magphpic_quotes_runtime()

Para saber o estados dos parâmetros respectivos.


Exemplo:
<?php
echo get_magic_quotes_gpc(); // 1, ou seja, está On
echo $_POST['sobrenome']; // O\'reilly
echo addslashes($_POST['sobrenome']); // O\\\'reilly

if (!get_magic_quotes_gpc()) {
$sobrenome = addslashes($_POST['sobrenome']);
} else {
$lastname = $_POST['sobrenome'];
}

echo $sobrenome; // O\'reilly


$sql = "INSERT INTO clientes (sobrenome) VALUES ('$sobrenome')";
?>

ini_set

Bons servidores de hospedagem ou revenda atualmente permitem entrar com um php.ini para cada
diretório. No caso, apenas entramos com os parâmetros desejados no php.ini.

Mas quando não tivermos este recurso nem acesso direto ao php.ini podemos usar a função
ini_set(), que não simula todos os parâmetros do php.ini mas pode alterar vários.

string ini_set ( string $propriedade, string $valormomentaneo )

ini_set("display_errors", "Off");

Outra:
12

ini_getall(), que recebe todas as configurações do php.ini:

$inis = ini_get_all();
print_r($inis);

void ini_restore ( string $varname ) - restaura todas as configurações.

Performance Tunning (disponíveis desde a versão 5.1.0)

realpath_cache_size - 16K
realpath_cache_ttl – 120

Here's a short explanation of the configuration directives.


realpath_cache_size integer

Determines the size of the realpath cache to be used by PHP. This value should be increased
on systems where PHP opens many files, to reflect the quantity of the file operations
performed.

realpath_cache_ttl integer

Duration of time (in seconds) for which to cache realpath information for a given file or
directory. For systems with rarely changing files, consider increasing the value.

Controle de erros:
Na fase de testes
error_reporting = E_ALL

Em produção:
error_reporting = E_ALL & ~E_NOTICE (o mínimo possível)
Além disso camuflar todos os possíveis erros com @ colado à esquerda do nome de funções com a
possibilidade de erro, especialmente funções de acesso a banco de dados.

log_errors = On
Ajuda a encontrar causas de problemas.

Limitar os abaixo dá maior controle:


memory_limit = 8M
post_max_size = 8M
max_execution_time = 60 ; Maximum execution time of each script, in seconds
max_input_time = 60 ; Maximum amount of time each script may spend parsing request data

Caso não tenha necessidade desabilite:


file_uploads = Off

Se precisar, então controle estes:


upload_tmp_dir = "E:\_xampp\tmp"
upload_max_filesize = 2M
13

Evitar funções de execução de executáveis no servidor do sistema operacional:


system
exec
shell_exec
passthru
popen
escapeshellcmd
pcntl_exec
backtick_operator

safe_mode = On
Safe_mode inpões uma série de restrições com o intuido de tornar os scripts mais seguros.
Com o safe_mode ativo somente podemos executar executáveis no diretório apontado por:

safe_mode_exec_dir = diretorio

Com safe_mode ativo algumas funções não funcionam:


shell_exec, backtick operator, dl( ) e variáveis PHP_AUTH.

Variáveis

– Inicializar todas as variáveis


– Criar variáveis somente quando precisar reutilizar em outros trechos do código
– Sempre validar os tipos e valores das variáveis quando entrados pelo usuário
– Controlar o path dos includes
– Validar tipos e valores dos parâmetros
– Fazer uso de constantes sempre que possível

Formulários

– Validar tipo e valores de todos os campos entrados pelos usuários


– Usar variáveis com token para validar os formulários
– Validar também com JS

Não guardar informações confidenciais nem nos scripts "nem no banco de dados".

Guardar sigilo sobre nosso código PHP e manter uma senha para acesso aos scripts e ao banco de
dados (usar a ferramenta Proteger diretório do cPanel).

O arquivo onde guardamos a senha de acesso ao banco não deve conter o código, mas deve ficar
separado, somente com o acesso ao banco, tipo conexao.inc.php e ficar fora do diretório do
aplicativo, um nível antes, chamado com require_once. Fica mais difícil de alguém mexer.

E-mail

<?php
$clean = array();
$email_pattern = '/^[^@\s<&>]+@([-a-z0-9]+\.)+[a-z]{2,}$/i';
14

if (preg_match($email_pattern, $_POST['email']))
{
$clean['email'] = $_POST['email'];
}
?>

Garantir que $_POST['color'] é red, green, ou blue:

<?php
$clean = array();
switch ($_POST['color'])
{
case 'red':
case 'green':
case 'blue':
$clean['color'] = $_POST['color'];
break;
}
?>
Garantir que $_POST['num'] is an integer:

<?php
$clean = array();
if ($_POST['num'] == strval(intval($_POST['num'])))
{
$clean['num'] = $_POST['num'];
}
?>

Garantir que $_POST['num'] is a float:

<?php
$clean = array();
if ($_POST['num'] == strval(floatval($_POST['num'])))
{
$clean['num'] = $_POST['num'];
}
?>

Form seguro:

<?php
$token = md5(time());
$fp = fopen('./tokens.txt', 'a');
fwrite($fp, "$token\n");
fclose($fp);
?>

<form method="POST">
15

<input type="hidden" name="token" value="<?php echo $token; ?>" />


<input type="text" name="message"><br />
<input type="submit">
</form>

<?php
$tokens = file('./tokens.txt');
if (in_array($_POST['token'], $tokens))
{
if (isset($_POST['message']))
{
$message = htmlentities($_POST['message']);
$fp = fopen('./messages.txt', 'a');
fwrite($fp, "$message<br />");
fclose($fp);
}
}
readfile('./messages.txt');
?>

Outro exemplo:

<?php
session_start();
if (isset($_POST['message']))
{
if (isset($_SESSION['token']) && $_POST['token'] == $_SESSION['token'])
{
$message = htmlentities($_POST['message']);
$fp = fopen('./messages.txt', 'a'); // O arquivo deve existir
fwrite($fp, "$message<br />");
fclose($fp);
}
}
$token = md5(uniqid(rand(), true));
$_SESSION['token'] = $token;
?>

<form method="POST">
<input type="hidden" name="token" value="<?php echo $token; ?>" />
<input type="text" name="message"><br />
<input type="submit">
</form>

<?php
readfile('./messages.txt');
?>

User Registration And Password Verification (sha1)


16

<?php
/* Store user details */
$passwordHash = sha1($_POST['password']);
$sql = 'INSERT INTO user (username,passwordHash) VALUES (?,?)';
$result = $db->query($sql, array($_POST['username'], $passwordHash));
?>

The next time our user logs in, we check their access credentials using similar code as follows:

<?php
/* Check user details */
$passwordHash = sha1($_POST['password']);
$sql = 'SELECT username FROM user WHERE username = ? AND passwordHash = ?';
$result = $db->query($sql, array($_POST['username'], $passwordHash));
if ($result->numRows() < 1)
{
/* Access denied */
echo 'Sorry, your username or password was incorrect!';
}else{
/* Log user in */
printf('Welcome back %s!', $_POST['username']);
}
?>

Tipos de Hash

In PHP you can generate hashes using the md5() and sha1 functions. md5() returns a 128-bit hash
(32 hexadecimal characters), whereas sha1() returns a 160-bit hash (40 hexadecimal characters). For
example:

<?php
$string = 'PHP & Information Security';
printf("Original string: %s\n", $string);
printf("MD5 hash: %s\n", md5($string));
printf("SHA-1 hash: %s\n", sha1($string));
?>

In MySQL you can generate hashes internally using the password(), md5(), or sha1 functions.
password() is the function used for MySQL's own user authentication system. It returns a 16-byte
string for MySQL versions prior to 4.1, and a 41-byte string (based on a double SHA-1 hash) for
versions 4.1 and up. md5() is available from MySQL version 3.23.2 and sha1() was added later in
4.0.2.

mysql> select PASSWORD( 'PHP & Information Security' );


mysql> select MD5( 'PHP & Information Security' );

You may decide to use MySQL to calculate your hash rather than PHP. The example of storing our
17

user's registration details from the previous section then becomes:

<?php
/* Store user details */
$sql = 'INSERT INTO user (username, passwordHash) VALUES (?, SHA1(?))';
$result = $db->query($sql, array($_POST['username'], $_POST['password']));
?>

When we validate a user's login credentials we follow the same process, only this time we use the
salt from our database instead of generating a new random one. We add the user supplied password
to it, run our hashing algorithm, then compare the result with the hash stored in that user's profile.

<?php
define('SALT_LENGTH', 9);
function generateHash($plainText, $salt = null)
{
if ($salt === null)
{
$salt = substr(md5(uniqid(rand(), true)), 0, SALT_LENGTH);
}
else
{
$salt = substr($salt, 0, SALT_LENGTH);
}
return $salt . sha1($salt . $plainText);
}
?>

Captcha – Form

captcha.php
<?php
session_start();
?>

<html><head>
<title>Gateway</title>
</head>
<body>
Please enter the value you see below:<br />
<img src="captcha-image.php" />
<form action="" method="post">
<input type="text" name="number" value="" /><br />
<button type="submit">Submit</button>
</form>

<?php
if (isset($_POST['number']))
if ($_SESSION['number'] == $_POST['number'])
18

echo "Correct";
else
echo "Wrong value entered!";
?>
</body></html>

captcha_image.php
<?php
session_start();
$number = rand(1,999); //generate a random integer
$_SESSION['number'] = $number; //store in session variable

$img_number = imagecreate(40,25);
$backcolor = imagecolorallocate($img_number,0xcc,0xcc,0xcc);
$textcolor = imagecolorallocate($img_number,255,255,255);

imagefill($img_number,0,0,$backcolor);
imagestring($img_number,10,5,5,$number,$textcolor);

header("Content-type: image/jpeg");
imagejpeg($img_number);
?>

Função para Filtrar Campos de Form

<?
//login $_POST['username']
//password $_POST['password']
//I passed $_POST through smart_quotes first before sending to SQL query.
//$_POST=mcheck($_POST)
//after mcheck(), do the SQL queries...

function mcheck($value) {
if(is_array($value)) {
if(get_magic_quotes_gpc()) {
$value=array_map("stripslashes",$value);
}
if(!array_map("is_numeric",$value)) {
$value=array_map("mysql_real_escape_string",$value);
}
} else {
if(get_magic_quotes_gpc()) {
$value=stripslashes($value);
}
if(!is_numeric($value)) {
$value="'" . mysql_real_escape_string($value) . "'";
}
}
return $value;
}
19

?>

mysql_real_escape_string() is enough for queries but before you print that, use
"htmlspecialchars();" or "htmlentities();".

For example:

echo htmlspecialchars($username);

Some general security tips:

create two functions for untrusted data:

one that puts it through mysql_real_escape_string() before using it in database queries


one that puts it through htmlentities() before outputting it

Always use them whenever you deal with untrusted information.

Also, never code with register_globals on. If your server must have register_globals on, always
have unique names for session variables (such as SESS_varname) and always declare a variable
before using it (ex: $output = ''; $output .= 'hello!'; )

<?
function secured($val)
{
if(empty($val) or strlen($val) > 40)
{
return false;
} else {
$val = strip_tags(trim(($val)));
$val = escapeshellcmd($val);
return stripslashes($val);
}
}
?>

So, using this function is simple. Any text box as a name, this is the html code
ex. <input name="securityscript" type="text" id="securityscript">
When the user clicks send button the fill box has a value. All you have to do is to filter that value
this way calling the function.

if(isset($Submit))
{
secured($securityscript);
// rest of your code
If you want to test this script type a text of your choice and it'll be shown normally. The try with
something like <img> or any tag you like. If nothing happens this mean that your input has been
20

filtered and removed by the function.

Teste com o mcrypt

Original - http://www.phpmag.net/itr/online_artikel/psecom,id,667,nodeid,114.html
<?php
/* run a self-test through every listed
* cipher and mode
*/
function mcrypt_check_sanity() {
$modes = mcrypt_list_modes();
$algorithms = mcrypt_list_algorithms();

foreach ($algorithms as $cipher) {


if(mcrypt_module_self_test($cipher)) {
print $cipher." ok.<br />\n";
} else {
print $cipher." not ok.<br />\n";
}
foreach ($modes as $mode) {
if($mode == 'stream') {
$result = "not tested";
} else
if(mcrypt_test_module_mode ($cipher,$mode)) {
$result = "ok";
} else {
$result = "not ok";
}
print $cipher." in mode".$mode." ".$result."<br />\n";
}
}
}

// a variant on the example posted in


// mdecrypt_generic
function mcrypt_test_module_mode($module,$mode) {
/* Data */
$key = 'this is a very long key, even too long for the cipher';
$plain_text = 'very important data';

/* Open module, and create IV */


$td = mcrypt_module_open($module,'',$mode, '');
$key = substr($key, 0,
mcrypt_enc_get_key_size($td));
$iv_size = mcrypt_enc_get_iv_size($td);
$iv = mcrypt_create_iv($iv_size,MCRYPT_RAND);
/* Initialize encryption handle */
if (mcrypt_generic_init($td, $key, $iv) !=-1) {
21

/* Encrypt data */
$c_t = mcrypt_generic($td, $plain_text);
mcrypt_generic_deinit($td);

// close the module


mcrypt_module_close($td);

/* Reinitialize buffers for decryption*/


/* Open module, and create IV */
$td = mcrypt_module_open($module, '',$mode, '');
$key = substr($key, 0,mcrypt_enc_get_key_size($td));

mcrypt_generic_init($td, $key, $iv);


$p_t = trim(mdecrypt_generic($td,$c_t)); //trim to remove padding

/* Clean up */
mcrypt_generic_end($td);
mcrypt_module_close($td);
}

if (strncmp($p_t, $plain_text,strlen($plain_text)) == 0) {
return TRUE;
} else {
return FALSE;
}
}

// function call:
mcrypt_check_sanity();
?>

Encriptando Cookies

<?php

function my_encrypt($string) {
$key = 'supersecret';
$key = md5($key);

/* Open module, trim key to max length */


$td = mcrypt_module_open('twofish', '','ecb', '');
$key = substr($key, 0, mcrypt_enc_get_key_size($td));

/* Initialize encryption handle


* (use blank IV)
*/
if (mcrypt_generic_init($td, $key, `') != -1) {

/* Encrypt data */
$c_t = mcrypt_generic($td, $string);
22

mcrypt_generic_end($td);
mcrypt_module_close($td);
return $c_t;
} //end if
}

function my_decrypt($string) {
$key = 'supersecret';
$key = md5($key);

/* Open module, trim key to max length */


$td = mcrypt_module_open('twofish', '','ecb', '');
$key = substr($key, 0, mcrypt_enc_get_key_size($td));

/* Initialize encryption handle


* (use blank IV)
*/
if (mcrypt_generic_init($td, $key, `') != -1) {

/* Encrypt data */
$c_t = mdecrypt_generic($td, $string);
mcrypt_generic_end($td);
mcrypt_module_close($td);
return trim($c_t); //trim to remove
//padding
} //end if
}

function my_encryptcookie($string) {
return base64_encode(my_encrypt($string));
}

function my_decryptcookie($string) {
return my_decrypt(base64_decode($string));
}
?>

set_twofish_cookie.php
<?php
include("twofish_cookie.php");
if($_COOKIE['test']) print
my_decryptcookie($_COOKIE['test']);
else {
$s = my_encryptcookie("Hello, world.");
if(setcookie('test', $s, time()+3600)) {
print 'Cookie set.';
}
}
?>
23

AES Encryption (aes_sql.php)

<?php
function my_encrypt($string) {
srand((double) microtime() * 1000000);
//for sake of MCRYPT_RAND

$key = 'supersecret';
$key = md5($key);

/* Open module, and create IV */


$td = mcrypt_module_open('rijndael-128', '','cfb', '');
$key = substr($key, 0, mcrypt_enc_get_key_size($td));
$iv_size = mcrypt_enc_get_iv_size($td);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
/* Initialize encryption handle */
if (mcrypt_generic_init($td, $key, $iv) != -1) {

/* Encrypt data */
$c_t = mcrypt_generic($td, $string);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$c_t = $iv.$c_t;
return $c_t;
} //end if
}

function my_decrypt($string,$key) {
$key = md5($key);

/* Open module, and create IV */


$td = mcrypt_module_open('rijndael-128', '','cfb', '');
$key = substr($key, 0, mcrypt_enc_get_key_size($td));
$iv_size = mcrypt_enc_get_iv_size($td);
$iv = substr($string,0,$iv_size);
$string = substr($string,$iv_size);
/* Initialize encryption handle */
if (mcrypt_generic_init($td, $key, $iv) != -1) {

/* Encrypt data */
$c_t = mdecrypt_generic($td, $string);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $c_t;
} //end if
}
?>

<?php
24

include("aes_sql.php");

$string = "Hello, world.";


print my_decrypt(my_encrypt($string), 'supersecret');
?>

Recuperando Senha de forma Segura


Usar senhas fortes, com 8 dígitos ou mais, misturando letras com algarismos e de preferência com
símbolos. Sempre criptografar senha antes de armazenar no banco de dados.

<form action="reset.php" method="GET">


<input type="hidden" name="user" value="chris" />
<p>Please specify the email address where you want your new password sent:</p>
<input type="text" name="email" /><br />
<input type="submit" value="Send Password" />
</form>

<?php
session_start();
$clean = array();
$email_pattern = '/^[^@\s<&>]+@([-a-z0-9]+\.)+[a-z]{2,}$/i';
if (preg_match($email_pattern, $_POST['email']))
{
$clean['email'] = $_POST['email'];
$user = $_SESSION['user'];
$new_password = md5(uniqid(rand(), TRUE));
if ($_SESSION['verified'])
{
/* Update Password */
mail($clean['email'], 'Your New Password', $new_password);
}
}
?>

Envio de Arquivos por Upload

Requer indicação de uma codificação especial no form:


25

<form action="upload.php" method="POST" enctype="multipart/form-data">

Para enviar (upload) basta:


<input type="file" name="attachment" />

O parâmetro MAX_FILE_SIZE controla o tamanho máximo do arquivo enviado (em bytes):


<form action="upload.php" method="POST" enctype="multipart/form-data">
<p>Please choose a file to upload:
<input type="hidden" name="MAX_FILE_SIZE" value="1024" />
<input type="file" name="attachment" /><br />
<input type="submit" value="Upload Attachment" /></p>
</form>
É importante utilizar algumas funções para controlar a segurança do upload (is_uploaded_file,
move_uploaded_file, etc):
<?php
$filename = $_FILES['attachment']['tmp_name'];
if (is_uploaded_file($filename))
{
/* $_FILES['attachment']['tmp_name'] is an uploaded file. */
}
?>

<?php
$old_filename = $_FILES['attachment']['tmp_name'];
$new_filename = '/path/to/attachment.txt';
if (move_uploaded_file($old_filename, $new_filename))
{
/* $old_filename is an uploaded file, and the move was successful. */
}
?>

<?php
$filename = $_FILES['attachment']['tmp_name'];
if (is_uploaded_file($filename))
{
26

$size = filesize($filename);
}
?>

Exemplos de códigos que reforçam a segurança:

<?php
$clean = array();
$html = array();
/* Filter Input ($name, $comment) */
$html['name'] = htmlentities($clean['name'], ENT_QUOTES, 'UTF-8');
$html['comment'] = htmlentities($clean['comment'], ENT_QUOTES, 'UTF-8');
echo "<p>{$html['name']} writes:<br />";
echo "<blockquote>{$html['comment']}</blockquote></p>";
?>

<?php
session_start();
$clean = array();
if (isset($_REQUEST['item'] && isset($_REQUEST['quantity']))
{
/* Filter Input ($_REQUEST['item'], $_REQUEST['quantity']) */
if (buy_item($clean['item'], $clean['quantity']))
{
echo '<p>Thanks for your purchase.</p>';
}
else
{
echo '<p>There was a problem with your order.</p>';
}
}
?>

<?php
session_start();
27

$token = md5(uniqid(rand(), TRUE));


$_SESSION['token'] = $token;
$_SESSION['token_time'] = time();
?>
<form action="buy.php" method="POST">
<input type="hidden" name="token" value="<?php echo $token; ?>" />
<p>
Item:
<select name="item">
<option name="pen">pen</option>
<option name="pencil">pencil</option>
</select><br />
Quantity: <input type="text" name="quantity" /><br />
<input type="submit" value="Buy" />
</p>
</form>

<?php
if (isset($_SESSION['token']) &&
$_POST['token'] == $_SESSION['token'])
{
/* Valid Token */
}
?>

<?php
$token_age = time() - $_SESSION['token_time'];
if ($token_age <= 300)
{
/* Less than five minutes has passed. */
}
?>

Segurança envolvendo Apache


Configurar adequadamente o script:
28

- httpd.conf ou .htaccess

Como esconder a versão do Apache?


- Editar o script httpd.conf e alterar para Off o parâmetro "ServerSignature"

Diz-se que não existe robozinho seguro, portanto para aumentar a segurança rejeitando robôs de
instrumentos de busca use no httpd.conf:
robots.txt
User-agent: *
Disallow: /

Bloqueando o uso de wget (mesmo que seja renomeado):


setfacl -m u:www-data:--- /usr/bin/wget

Usando o script .htaccess

Quando não temos acesso ao servidor Apache temos a alternativa de usar o script .htaccess no
diretório do aplicativo para melhorar a segurança:

No forum do Joomla podemos encontrar um exemplo deste script muito bom para reforçar a
segurança web de sites:
http://forum.joomla.org/index.php/topic,124708.msg613819.html#msg613819

Crypt e Validação do Usuário

Original em - http://www.phpnoise.com/tutorials/3/1

crypt ( string_str [, string_salt]): string

Basic example of using crypt()


<?php

// Get string to encrypt


$string = 'myname';

// Encrypt string
$output = crypt($string);

// Send to browser
print('Input: ' . $string . "\n");
print('Output: ' . $output);
29

?>

Basic example of crypt() with a string salt

<?php

// Get string to encrypt


$string = 'myname';

// Hash string
$hash = 'hash_my_string';

// Encrypt string
$output = crypt($string, $hash);

// Send to browser
print('Input: ' . $string . "\n");
print('Output: ' . $output);

?>

Hash in, hash out using crypt()


<?php

// Get string to encrypt


$string = 'myname';

// Encrypt string twice without salt


$output = crypt($string);
$output2 = crypt($string);

// Send to screen
print('Input: ' . $string . "\n\n");
print('Output: ' . $output . "\n");
print('Output2: ' . $output2 . "\n\n\n");

// Hash the input, using the output string as salt


$new_output = crypt($string, $output);
$new_output2 = crypt($string, $output2);

// Sent to browser
print('Input: ' . $string . "\n\n");
print('Output: ' . $output . "\n");
print('New output: ' . $new_output . "\n\n");
print('Output2: ' . $output2 . "\n");
print('New output2: ' . $new_output2);

?>
30

Stored crypt() output comarison against new inputs

<?php

// Stored hash of 'myname'


$old_output = '$1$d70.iH3.$IonRlvkE.1OfyQGHXNivp/';

// Hash new $input


$output = crypt($_POST['input'], $old_output);
// Compare the results
if ($output == $old_output) {
print('Match found!');
} else {
print('No match.');
}
?>

Add new user form


<html>
<head>
<title>Add User</title>
</head>
<body>
<h1>Add User:</h1>
<form method="post" action="add.php">
<p>
Name:
<input type=text name="user" />
</p>
<p>
Password:
<input type=password name="password" />
</p>
<p>
<input type=submit name="Submit" value="Submit" />
</p>
</form>
</body>
</html>
31

Process user data and store it to a text file


<?php

// File is named "add.php"

// Get user data and strip slashes


$user_name = stripslashes($_POST['user']);
$user_pwd = stripslashes($_POST['password']);

// Crypt() user password


$user_pwd = crypt($user_pwd);

// Store new user in "users.txt"


$file = 'users.txt';
$fp = fopen($file, 'a+');
if (fwrite($user_name . '|' . $user_pwd)) {
print('User: ' . $user_name . ' added.');
} else {
print('Error trying to write to file.');
}
fclose($fp);

?>

Validating user logins

<?php
// Get new login information
$new_user_name = trim($_POST['username']);
$new_user_name = stripslashes($_POST['username']);

$new_user_pwd = trim($_POST['password']);
$new_user_pwd = stripslashes($_POST['password']);

// Get list of valid users


$file = file('users.txt');

// Process list of existing users into name/pwd arrays


foreach ($file as $line) {
$user[] = explode("|",$line);
$user_name[] = trim($user[0]);
$user_pwd[] = trim($user[1]);
}

// Test for a valid user


$found = false;
foreach($user_name as $key => $user) {
if($user_name[$key] == $new_user_name) {
$pwd = crypt($new_user_pwd, $user_pwd[$key]);
if($user_pwd[$key] == $pwd) {
32

$found = true;
break 3;
}
}
}

// Do user specific action


if($found) {
// go to user page
} else {
// output "Invalid Name/Password pair" msg
}

?>

Code segment testing for a valid user

<?php

// Test for a valid user


$found = false;
foreach($user_name as $key => $user) {
if($user_name[$key] == $new_user_name) {
$pwd = crypt($new_user_pwd, $user_pwd[$key]);
if($user_pwd[$key] == $pwd) {
$found = true;
break 3;
}
}
}

?>

Senhas seguras: algumas técnicas e script para teste

Original em: http://www.revistaphp.com.br/artigo.php?id=153

Senhas mais seguras

Seguem algumas dicas para criação de senhas mais seguras:

Não utilizar...

• dados pessoais como: data de nascimento, nome de pessoas da família ou de amigos,


telefone e endereço. Pois esses podem ser descobertos facilmente por outras pessoas.
• palavras contidas em dicionários, de qualquer língua. A buscas por palavras mais comuns é
uma das maneiras utilizas pelos programas para descobrir senhas.
• senhas simples: pequenas, contendo somente números ou letras. Essas são descobertas com
33

muita facilidade.
Agora que sabemos algumas coisas que não devem ser utilizadas, vão algumas dicas de boas
senhas:

Utilizar...
• palavras embaralhadas1, que seja somente de seu conhecimento. <li> no mínimo 3 tipos de
caracteres, tais como letras maiúsculas e minúsculas (az e AZ), números (09) e caracteres
especiais (@!#$%&*+=?|<> e outros).
• no mínimo 8 dígitos.
1Consiste em trocar algumas letras da palavra escolhida utilizando as outras dicas, ou seja, escolher
uma palavra e acrescentar ou trocar as letras por números e caracteres especiais, procurando atender
as dicas para criação de uma senha segura.

Criar um script testasenha.js com o conteúdo:

function verCaracterDaSenha(valor) {

var erespeciais = /[@!#$%&*+=?|-]/;


var ermaiuscula = /[A-Z]/;
var erminuscula = /[a-z]/;
var ernumeros = /[0-9]/;
var cont = 0;

if (erespeciais.test(valor)) cont++;
if (ermaiuscula.test(valor)) cont++;
if (erminuscula.test(valor)) cont++;
if (ernumeros.test(valor)) cont++;
return cont;
}

function segurancaBaixa(d) {
d.innerHTML = '<h4>Seguranca da senha: <font color=\'red\'> BAIXA</font></h4>';
}
function segurancaMedia(d) {
d.innerHTML = '<h4>Seguranca da senha: <font color=\'orange\'> MEDIA</font></h4>';
}
function segurancaAlta(d) {
d.innerHTML = '<h4>Seguranca da senha: <font color=\'green\'> ALTA</font></h4>';
}

function testaSenha(valor) {
var d = document.getElementById('seguranca');
var c = verCaracterDaSenha(valor);
var t = valor.length;

if(t == ''){
d.innerHTML = "<h4>Seguranca da senha: !</h4>";
} else {
if(t > 7 && c >= 3) segurancaAlta(d);
34

else {
if(t > 7 && c >= 2 || t > 4 && c >= 3) segurancaMedia(d);
else segurancaBaixa(d);
}
}
}

Criar um arquivo para testar:


<html>
<title>Sua senha é segura?</title>
<head>
<script type="text/javascript" src="testasenha.js"></script>
</head>
<body>
Senha: <br>
<input type="password" name="novasenha" id="novasenha" onKeyUp="testaSenha(this.value);">
<div id='seguranca'><h4>Seguranca da senha: !</h4></div>
</body>
</html>

Using the HTTP_REFERER


<?PHP

// Check form data coming from right place

// Valid URL
my_form_URL = "http://domain.net/my_form.html";

// Set to lowercase
my_form_URL = strtolower(my_form_URL);
incoming_URL = strtolower($_SERVER['HTTP_REFERER']);

if (my_form_URL == incoming_URL) {
// All ok - continue
} else {
// Someone is cheating!!
echo "Access Denied.";
exit; // stop executing script
}

// Rest of the script

?>

GET/POST Form trick form


<html>
<head>
<title>Login</title>
</head>
35

<body>
<form name="login" method="post"
action="log.php?<?PHP
// generate dynamic_info var
print('data=' . dynamic_info);
?>">
<input type=text name="uName" />
<input type=password name="uPwd" />
<input type=submit value="Submit" />
</form>
</body>
</html>
GET/POST Form trick script

<?PHP

// Generate dynamic_info here

// Check for data


if (isset($_GET['data'])) {
if ($_GET['data'] == dynamic_info) {
// all is fine so far
} else {
// Not a valid form!
}
} else {
// Not a valid form!
}

// Check for HTTP_REFERER


// Use code in figure 3.1

// Check for correct method


if (isset($_GET['uName'])) {
// Not a valid form!
}
// Continue dealing with form as normal

?>

Cleaning form inputs

<?php
function clean_it($vName) {
$vName = stripslashes(trim(vName));
$vName = nl2br(vName);
$vName = htmlentities(vName);
return $vName;
}
36

// Usage
$uName = clean_it($_POST['uName']);
?>

Secure Login Using Cookies

<?php
// valid login credentials
$username = 'admin';
$password = 'admin_pass';
// grab current time
$time=time();

// handle the logout event


if ($logout == true) {
setcookie ("user", md5($_POST[user]), $time-3200);
setcookie ("pass", md5($_POST[pass]), $time-3200);
header("Location: index.php");
}

// handle validation event


if ($_POST[user] && $_POST[pass]) {
if ($_POST[user]==$username && $_POST[pass]==$password) {
setcookie ("user", md5($_POST[user]), $time+3200);
setcookie ("pass", md5($_POST[pass]), $time+3200);
header("Location: index.php");
} else { $login_error= true; }
}

// handle login event, both successful and erroneous, or show login screen
if ($login_error == true) { ?>
<table align=center style="font-family:arial; font-size:12; border:1 solid #000000;">
<tr><td align=center bgcolor=#123dd4>LOGIN ERROR</td></tr>
<tr><td align=center><b>Invalid Username and/or Password</b><br><br><a
href=index.php>Back</a></td></tr>
</table>
<?
} elseif ($_COOKIE[user] == md5($username) && $_COOKIE[pass] == md5($password)) { ?>
<table align=center style="font-family:arial; font-size:12; border:1 solid #000000;">
<tr><td align=center bgcolor=#123dd4>SECURE AREA</td></tr>
<tr><td align=right><a href=index.php?logout=true>Logout</a></td></tr>
<tr><td>You have successfully logged in.<br><br>
Encrypted Username: <b><?= $_COOKIE[user] ?></b><br>
Encrypted Password: <b><?= $_COOKIE[pass] ?></b><br>
</td></tr>
</table>
<?
} else {
?>
<form action=index.php method=post>
37

<table align=center style="font-family:arial; font-size:12; border:1 solid #000000;">


<tr><td colspan=2 align=center bgcolor=#123dd4>LOGIN</td></tr>
<tr><td align=right>Username: </td><td><input type=text name=user size=15></td></tr>
<tr><td align=right>Password: </td><td><input type=password name=pass size=15></td></tr>
<tr><td align=center colspan=2><input type=submit value=Login></td></tr>
</table>
</form>
<?
}
?>

<?php
// Form Autenticação

/* mysql_connect() */
/* mysql_select_db() */

$clean = array();
$mysql = array();

$now = time();
$max = $now - 15;

$salt = 'SHIFLETT';

if (ctype_alnum($_POST['login']))
{
$clean['login'] = $_POST['login'];
}
else
{
/* ... */
}

$clean['senha'] = md5($salt . md5($_POST['senha'] . $salt));


$mysql['login'] = mysql_real_escape_string($clean['login']);

$sql = "SELECT ultima_falha, senha


FROM usuarios
WHERE login = '{$mysql['login']}'";

if ($result = mysql_query($sql))
{
if (mysql_num_rows($result))
{
$record = mysql_fetch_assoc($result);

if ($record['ultima_falha'] > $max)


{
38

/* Less than 15 seconds since last failure */


}
elseif ($record['senha'] == $clean['senha'])
{
/* Successful Login */
}
else
{
/* Failed Login */

$sql = "UPDATE usuarios


SET ultima_falha = '$now'
WHERE login = '{$mysql['login']}'";

mysql_query($sql);
}
}
else
{
/* Invalid login */
}
}
else
{
/* Error */
}
</body>
</html>

<?php
//Check a Persistent Login Cookie

/* mysql_connect() */
/* mysql_select_db() */

$clean = array();
$mysql = array();

$now = time();
$salt = 'SHIFLETT';

list($identifier, $token) = explode(':', $_COOKIE['auth']);

if (ctype_alnum($identifier) && ctype_alnum($token))


{
$clean['identifier'] = $identifier;
$clean['token'] = $token;
}
else
39

{
/* ... */
}

$mysql['identifier'] = mysql_real_escape_string($clean['identifier']);

$sql = "SELECT login, token, timeout


FROM usuarios
WHERE identifier = '{$mysql['identifier']}'";

if ($result = mysql_query($sql))
{
if (mysql_num_rows($result))
{
$record = mysql_fetch_assoc($result);

if ($clean['token'] != $record['token'])
{
/* Failed Login (wrong token) */
}
elseif ($now > $record['timeout'])
{
/* Failed Login (timeout) */
}
elseif ($clean['identifier'] !=
md5($salt . md5($record['login'] . $salt)))
{
/* Failed Login (invalid identifier) */
}
else
{
/* Successful Login */
}
}
else
{
/* Failed Login (invalid identifier) */
}
}
else
{
/* Error */
}

?>
</body>
</html>

<?php
40

session_start();

if (isset($_POST['message']))
{
if (isset($_SESSION['token']) && $_POST['token'] == $_SESSION['token'])
{
$message = htmlentities($_POST['message']);

$fp = fopen('./messages.txt', 'a');


fwrite($fp, "$message<br />");
fclose($fp);
}
}
$token = md5(uniqid(rand(), true));
$_SESSION['token'] = $token;

?>

<form method="POST">
<input type="hidden" name="token" value="<?php echo $token; ?>" />
<input type="text" name="message"><br />
<input type="submit">
</form>

<?php

readfile('./messages.txt');

?>

This is good when we write HTML code or bad javascript on a textarea.

function parsearHTMLInjectado($texto)
{
return nl2br( htmlentities($texto) );
}

Dicas de Segurança

- Nunca enviar valores sigilosos através de inputs tipo hidden, pois podem ser vistos.
- Valide todos os valores antes de usar
- Se não puder validar os valores valide pelo menos os tipos de dados
- Use sempre valores default
- Usar segurança contra spam nos forms, como Captcha.
- Tratar todos os valores de entrada como potencial dano
- Ter sempre um plano para processamento das informações

* O que é validado e em que ordem?


41

o O usuário precisa estar logado?


o O usuário precisa pertencer a um específico grupo para agir?
o Checar dupliciade de dados?
* Quais checagens de segurança serão efetuadas?
o Segurança por hashes
o CAPTCHA
o Blacklist Banning / Whitelist Overrides
+ $IN->blacklisted == 'y' (blacklisted)
+ $IN->whitelisted == 'y' (whitelist override)
o Preferences and settings checked against
* Filtros e conversão de Dados
o limpar XSS
o Formatação de números: number_format(), ceil(), etc.
o Conversão de Character set (codificação)
o XML convert
* Inserir e Atualizar Dados
o Todos os dados devem ser adequadamente "escapados"

Usuário super administrador deve ser utilizado somente na instalação e configurações iniciais.
Logo após devemos criar um usuário com menor poder para maior segurança na administração.

Fontes:

1 - PHP Manual - Segurança - http://docs.php.net/manual/pt_BR/security.php


2 - http://expressionengine.com/docs/development/guidelines/general.html
3 – Diversos outros artigos encontrados via internet

Ribamar FS – http://ribafs.net

Das könnte Ihnen auch gefallen