Impedir ataques

<< Click to Display Table of Contents >>

Navigation:  Segurança e Otimização >

Impedir ataques

Previous pageReturn to chapter overviewNext page

Estrutura

 

/classes/funcoes/Helpers.php
/classes/Config.php
/classes/Sessao.php
/classes/autoload.php
/Principal.php

 

form.php

 
<form name="post" action="./aula2.php" method="post" enctype="multipart/form-data" autocomplete="off" novalidate>
    <?= csrf_input(); ?>
    <input type="text" name="nome" value="<?= ($dados->nome ?? ""); ?>" placeholder="Nome"/><br/>
    <input type="password" name="pass" value="<?= ($dados->senha ?? ""); ?>" placeholder="Senha"/><br/>
    <button>Enviar</button>
</form>

 

Principal.php

 
require __DIR__ . '/classes/autoload.php';
 
// Ataque XSS = Cross-Site Scripting
 
// isso já remove todos os scripts inseridos no POST (colocar um <script>dentro de um campo </script>)
$post = filter_input_array(INPUT_POSTFILTER_SANITIZE_STRIPPED);
if ($post) {
    $dados = (object$post;
    echo $dados->nome// se tinha script, já era
    var_dump($post$dados);
}
 
// Ataque CSRF = Cross-Site Request Forgery
// Impede ataques de outros sites e também usar F5 após enviar um formulário também é bloqueado
 
if ($_REQUEST && !csrf_verificar($_REQUEST)) {
    echo "<h2>BLOQUEADO, você deu F5?</h2>";
}
 
include __DIR__ . "/form.php";

 

Config.php

 

/**
 * Base
 */
define("DB_HOST""localhost");
define("DB_USER""root");
define("DB_PASS""123456");
define("DB_BASE""aula");
 
/**
 * URL
 */
define("URL_BASE""http://www.localhost");
define("URL_ADMIN"URL_BASE."/admin");
define("URL_ERRO"URL_BASE."/404");
 
/**
 * Datas
 */
define("DATA_BR""d/m/Y H:i:s");
define("DATA_APP""Y-m-d H:i:s");
 
/**
 * SESSAO
 */
// pasta sessoes no raiz do site
define("SESSAO_CAMINHO"__DIR__."/../sessoes/");

 

autload.php

 

require __DIR__."/funcoes/Helpers.php";
 
spl_autoload_register(function ($class) {
 
    $prefixo = "classes\\";
    $baseDir = __DIR__ . "/";
    $len = strlen($prefixo);
 
    if (strncmp($prefixo$class$len) !== 0) {
        return;
    }
 
    $classeRelativa = substr($class$len);
    $arq = $baseDir . str_replace("\\""/"$classeRelativa) . ".php";
 
    if (file_exists($arq)) {
        require $arq;
    }
});

 

Helpers.php

 

// não tem namespace
 
require __DIR__ . "/../Config.php";
 
/**
 * ######################
 * ###   VALIDADOES   ###
 * ######################
 */
 
/**
 * Retorna true quando email valido
 * @param string $email
 * @return bool
 */
function is_email(string $email): bool {
    return filter_var($emailFILTER_VALIDATE_EMAIL);
}
 
/**
 * Retorna true quando password é valido
 * @param string $pass
 * @return bool
 */
function is_password(string $pass): bool {
// no minimo 8 letras maximo 30
    $tam = mb_strlen($pass);
    return ($tam >= 8 && $tam <= 30 ? true : false);
}
 
/**
 * Retorna um password já criptografado (hash)
 * @param string $pass
 * @return string
 */
function get_senha(string $pass): string {
    return password_hash($passPASSWORD_DEFAULT);
}
 
/**
 * Testa se um password bate com uma hash
 * @param string $pass
 * @param string $hash
 * @return bool
 */
function is_senha(string $passstring $hash): bool {
    return password_verify($pass$hash);
}
 
/**
 * Monta um input oculto com o csrf_token
 * @return string
 */
function csrf_input(): string {
    sessao()->csrf();
    return "<input type='hidden' name='csrf' value='" . (sessao()->csrf_token ?? "") . "'/>";
}
 
/**
 * Confere se a requisicao tem o token de validação de CSRF
 * @param type $request
 * @return bool
 */
function csrf_verificar($request): bool {
    // nao tem o token ou request for vazio
    if (empty(sessao()->csrf_token) || empty($request['csrf']) ||
            //, ou se forem diferentes
            sessao()->csrf_token != $request['csrf']) {
        return false;
    }
    
    return true;
}
 
/**
 * ##################
 * ###   STRING   ###
 * ##################
 */
 
/**
 * Transforma string numa url valida (remove caracteres especiais, espacos)
 * @param string $string
 * @return string
 */
function str_normalize($string) {
    return trim(html_entity_decode(preg_replace('/[ ]{2,}/'' 'preg_replace("/(\r\n|\n|\r|\t|&nbsp;)/"' '$string))));
}
 
function str_normalize_versao2(string $string): string {
 
    $ajustada = filter_var(mb_strtolower($string), FILTER_SANITIZE_STRIPPED);
    $orig = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜüÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûýýþÿRr"!@#$%&*()_-+={[}]/?;:.,\\\'<>°ºª';
    $novo = 'aaaaaaaceeeeiiiidnoooooouuuuuybsaaaaaaaceeeeiiiidnoooooouuuyybyRr                                 ';
 
    $retorno = str_replace(" ""-",
            trim(strtr(utf8_decode($ajustada), utf8_decode($orig), $novo))
    );
 
    return $retorno;
}
 
/**
 * Primeira letra de cada palavra maiuscula - sem espaços
 * @param string $string
 * @return string
 */
function str_capitalizado(string $string): string {
    $ajustada = str_normalize($string);
    $retorno = str_replace(" """,
            mb_convert_case(str_replace("-"" "$ajustada), MB_CASE_TITLE)
    );
 
    return $retorno;
}
 
/**
 * Primeira letra de cada palavra maiuscula - sem espaços, porém primeira de todos minuscula
 * @param string $string
 * @return string
 */
function str_camel_case(string $string): string {
    return lcfirst(str_capitalizado($string));
}
 
/**
 * Gera um titulo removendo scripts
 * @param string $string
 * @return string
 */
function str_titulo(string $string): string {
    return mb_convert_case(filter_var($stringFILTER_SANITIZE_SPECIAL_CHARS), MB_CASE_TITLE);
}
 
/**
 * Resume um texto em uma quantidade de palavras
 * @param string $string
 * @param int $limite
 * @param string $ponteiro
 * @return string
 */
function str_limite_palavras(string $stringint $limitestring $ponteiro = "..."): string {
// primeiro limpa
    $texto = trim(filter_var($stringFILTER_SANITIZE_SPECIAL_CHARS));
// separa palavras num array
    $arrWords = explode(" "$texto);
// conta o array
    $numWords = count($arrWords);
// se não estourou limite só retorna o original
    if ($numWords < $limite) {
        return $texto;
    }
 
// junta em string o array, mas do item 0 até quantidade de palavras
    $palavras = implode(" "array_slice($arrWords0$limite));
    return $palavras . $ponteiro;
}
 
/**
 * Limita tamanho de uma string, mas nao corta no meio da palavra
 * @param string $string
 * @param int $limite
 * @param string $ponteiro
 * @return string
 */
function str_limite_letras(string $stringint $limitestring $ponteiro = "..."): string {
// primeiro limpa
    $texto = trim(filter_var($stringFILTER_SANITIZE_SPECIAL_CHARS));
    if (mb_strlen($texto) <= $limite) {
        return $texto;
    }
 
// vamos descobrir o ultimo espaço antes do limite
    $ultimo_espaco = mb_strrpos(mb_substr($texto0$limite), " ");
    $letras = mb_substr($texto0$ultimo_espaco);
    return $letras . $ponteiro;
}
 
/**
 * #####################
 * ###   NAVEGACAO   ###
 * #####################
 */
 
/**
 * monta URL com base na constante em Config.php
 * @param string $path
 * @return string
 */
function url(string $path): string {
    return URL_BASE . "/" . ($path[0] == "/" ? mb_substr($path1) : $path);
}
 
/**
 * Redireciona pagina
 * @param string $url
 * @return void
 */
function redirect(string $url): void {
    header("HTTP/1.1 302 Redirect");
    if (filter_var($urlFILTER_VALIDATE_URL)) {
        header("Location: {$url}");
        exit;
    }
 
    $local = url($url);
    header("Location: {$local}");
    exit;
}
 
/**
 * ##############################
 * ###   INSTANCIAR CLASSES   ###
 * ##############################
 */
function db(): PDO {
    return \classes\base\Conexao::getConexao();
}
 
function sessao(): classes\Sessao {
    return new classes\Sessao();
}

 

 

Sessao.php

 

namespace classes;
 
class Sessao {
 
    public function __construct() {
        // ainda não foi startada sessões?
        if (!session_id()) {
            session_save_path(SESSAO_CAMINHO);
            session_start();
        }
    }
 
    public function __get($chave) {
 
        // vazio returna null
        if (!empty($_SESSION[$chave])) {
            return $_SESSION[$chave];
        }
 
        return null;
    }
 
    public function __isset($chave): bool {
        return $this->existe($chave);
    }
 
    /**
     * retorna a sessão como objeto
     * @return object|null
     */
    public function asObj(): ?object {
        return (object$_SESSION;
    }
 
    /**
     * seta um valor na sessao e retorna ela mesma
     * @param string $chave
     * @param type $valor
     * @return \classes\Sessao
     */
    public function set(string $chave$valor): Sessao {
 
        // se o $valor for array, converte para objeto
        $_SESSION[$chave] = (is_array($valor) ? (object$valor : $valor);
 
        return $this;
    }
 
    /**
     * Limpa um valor da sessao
     * @param type $chave
     * @return \classes\Sessao
     */
    public function unset($chave): Sessao {
        unset($_SESSION[$chave]);
        return $this;
    }
 
    /**
     * Retorna verdadeiro se o variavel existe na sessao atual
     * @param type $chave
     * @return bool
     */
    public function existe($chave): bool {
        return isset($_SESSION[$chave]);
    }
 
    /**
     * Apaga o arquivo velho e cria uma nova (segurança)
     * @return \classes\Sessao
     */
    public function regenerar(): Sessao {
        session_regenerate_id(true);
        return $this;
    }
 
    /**
     * Finaliza todas as variaveis da sessao
     * @return \classes\Sessao
     */
    public function finalizar(): Sessao {
        session_destroy();
        return $this;
    }
 
    /**
     * Obtem um valor e mata ele. Uso unico.
     * Utilitario: coloque dentro dele uma informação que seja necessário no redirect
     * da pagina, depois de pegar ele, limpa a mensagem para não usar novamente.
     * Tipo uma mensagem de erro
     */
    public function getOnce($chave) {
        // se existe a chave
        if ($this->existe($chave)) {
            // pega o valor dela
            $local = $_SESSION[$chave];
            // limpa o conteudo
            $this->unset($chave);
            // retorna o valor
            return $local;
        }
        return null;
    }
    
    /**
     * Para evitar invasão por CSRF = Cross-Site Request Forgery
     * @return void
     */
    public function csrf(): void 
    {
        // passa um código 20 criptografado
        $_SESSION['csrf_token'] = base64_encode(random_bytes(20));
    }
}