Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

laiorc

ESSE CÓDIGO É SEGURO??

Recommended Posts

Galera tenho um site e preciso liberar videos somente para pessoas autorizadas. Meu código está funcionando, porém não sei se essa maneira de fazer é segura. Eu armazeno os vídeos no meu servidor dentro da pasta www/videos/ . Dentro do cadastro do usuario tem um campo chamado "liberacao"  ( = 0 não liberado, = 1 liberado )

 

Depois que o usuário faz login no site o PHP é assim:

<?php
    $sqlInfosUser   = ("SELECT * FROM usuarios WHERE user_id = '". $idUsuario ."'");
    $queryInfosUser = mysql_query($sqlInfosUser,$conexao);
    $infosUser      = mysql_fetch_array($queryInfosUser);
    $liberacao      = $infosUser['liberacao'];

    if($liberacao == 0){
        //exibe imagem com 1 frame do vídeo
		<img src='caminhodaimagem'/>
    }elseif($liberacao >= 1){
        //exibe o video
		<video width='1024px' height='468px' controls>
  			<source src='videos/video1.mp4' type='video/mp4'>
		</video>
    }
?>

Gostaria de saber se isso é seguro. E se alguém de fora sem cadastro no site possa conseguir ter acesso direto pela URL a esse vídeo de alguma maneira.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Vejas isso e tire suas próprias conclusões:

 

 

 

E sim, esse código aí não tem segurança alguma.

Compartilhar este post


Link para o post
Compartilhar em outros sites
3 horas atrás, Alaerte Gabriel disse:

Não é seguro. Qualquer um pode acessar seu vídeo.

Suspeitei desde o principio rsrs. Obrigado!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Desculpem pessoal, mas não entendi onde exatamente podemos verificar e afirmar que "o código não tem segurança nenhuma".

 

Entendo que há melhorias a ser feitas no código, como o uso das funções mysql_* (que deveriam ser substituídas por PDO ou mysqli), mas supondo que $idUsuario seja um valor inteiro, o que automaticamente inviabilizaria uma tentativa de SQL Injection, onde estaria a brecha de segurança que permite que qualquer um tenha acesso ao vídeo?

 

O uso de prepared statements seria um bom argumento e eu concordaria 100% com isso, mas não é o caso no trecho de código acima, apenas nas outras *possíveis* consultas desse sistema.

 

Na linha 2 podemos alterar a linha para ficar assim (estou assumindo o uso do MySQL/MariaDB como base de dados):

$sqlInfosUser = 'SELECT * FROM `usuarios` WHERE `user_id` = ' . (int)$idUsuario;

Isso iria forçar o tipo inteiro, removendo quaisquer outros caracteres e frustrando uma eventual tentativa de ataque.

 

Por favor, não me entendam mal, apenas gostaria de elaborar melhor a resposta para ajudar o autor do tópico. Talvez também eu não esteja vendo ou desconheça de alguma falha presente no código.

Compartilhar este post


Link para o post
Compartilhar em outros sites
11 horas atrás, Matheus Tavares disse:

Desculpem pessoal, mas não entendi onde exatamente podemos verificar e afirmar que "o código não tem segurança nenhuma".

 

Entendo que há melhorias a ser feitas no código, como o uso das funções mysql_* (que deveriam ser substituídas por PDO ou mysqli), mas supondo que $idUsuario seja um valor inteiro, o que automaticamente inviabilizaria uma tentativa de SQL Injection, onde estaria a brecha de segurança que permite que qualquer um tenha acesso ao vídeo?

 

O uso de prepared statements seria um bom argumento e eu concordaria 100% com isso, mas não é o caso no trecho de código acima, apenas nas outras *possíveis* consultas desse sistema.

 

Na linha 2 podemos alterar a linha para ficar assim (estou assumindo o uso do MySQL/MariaDB como base de dados):


$sqlInfosUser = 'SELECT * FROM `usuarios` WHERE `user_id` = ' . (int)$idUsuario;

Isso iria forçar o tipo inteiro, removendo quaisquer outros caracteres e frustrando uma eventual tentativa de ataque.

 

Por favor, não me entendam mal, apenas gostaria de elaborar melhor a resposta para ajudar o autor do tópico. Talvez também eu não esteja vendo ou desconheça de alguma falha presente no código.

 

Concordo, mas o que quis dizer e você mesmo já disse, é que não há tratamento algum de dados.

Hora em outro tópico que vi nesse fórum, e torno a repetir o que disse, "usar PDO é mais seguro que mysqli?" a resposta é Não! Mesmo que eu só uso a PDO por que a segurança é mais confiável então....

As pessoas acham que se usarem um prepare aqui, um bind alí vão proteger seus códigos. Segurança no PHP vai além de um arquivo escrito com o código impecável. Se tudo é tratado antes de ser entregue ao arquivo que fará o uso do banco, não há problema algum se for até mesmo passar uma query direta na execução, mesmo que não aconselho fazer assim.

Também vale lembrar que ficar pesando o sistema para por segurança pesada por cima de segurança não compensa porque perdemos muito em velocidade.

Uma coisa é certa que deve se pensar (Até onde preciso por segurança em minha aplicação? Qual vai ser a demanda? etc..)

No seu exemplo mesmo você altera a forma que o dado será passado na query, de forma a que se fosse um código que esteja sendo injetado o PHP ignorar o código e usará um INTEGER, no caso se for mesmo um código o retorno será null, assim sendo não executando nada dentro da query trazendo a segurança contra um inject.

Se entende, saberá o ponto que quis chegar,  o simples fato da pergunta que deve ser feita: De quanto de segurança preciso mesmo?

 

Em resumo final o que torna o código "inseguro" é o não tratamento de dados que ele vai receber antes de executar-lo.

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Legal Omar... minha intenção era exatamente essa: abrir a discussão para elaborarmos melhor a resposta.

 

Concordo 100% com você, apenas quis dizer que não sabemos a origem da variável $idUsuario, pois o laiorc não postou o código completo (ou está com a register_globals ligada, o que seria um erro enorme), e assim não sabíamos se a variável já não estava tratada anteriormente.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Em primeiro lugar está usando a conexão em MYSQL que a mesma encontra-se obsoleta, agora utiliza-se o mysqli ou o PDO. Recomendo que utilize o PDO para usar o parâmetro bindParam para efetuar a correção e obrigação de parametrização de dados por tipo de dados tais como: INT,FLOAT,STRING,BOOLEAN.

 

também poderá utilizar os comandos tais como: is_int,is_float, is_string, is_bool.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Esse é o PHP para cadastro:

<?php

class Registration
{
   
    private $db_connection = null;
    
    public $errors = array();
    
    public $messages = array();

    
    public function __construct()
    {
        if (isset($_POST["register"])) {
            $this->registerNewUser();
        }
    }

   
    private function registerNewUser()
    {
        if (empty($_POST['user_name'])) {
            $this->errors[] = "Empty Username";
        } elseif (empty($_POST['user_password_new']) || empty($_POST['user_password_repeat'])) {
            $this->errors[] = "Empty Password";
        } elseif ($_POST['user_password_new'] !== $_POST['user_password_repeat']) {
            $this->errors[] = "Password and password repeat are not the same";
        } elseif (strlen($_POST['user_password_new']) < 6) {
            $this->errors[] = "Password has a minimum length of 6 characters";
        } elseif (strlen($_POST['user_name']) > 64 || strlen($_POST['user_name']) < 2) {
            $this->errors[] = "Username cannot be shorter than 2 or longer than 64 characters";
        } elseif (!preg_match('/^[a-z\d]{2,64}$/i', $_POST['user_name'])) {
            $this->errors[] = "Username does not fit the name scheme: only a-Z and numbers are allowed, 2 to 64 characters";
        } elseif (empty($_POST['user_email'])) {
            $this->errors[] = "Email cannot be empty";
        } elseif (strlen($_POST['user_email']) > 64) {
            $this->errors[] = "Email cannot be longer than 64 characters";
        } elseif (!filter_var($_POST['user_email'], FILTER_VALIDATE_EMAIL)) {
            $this->errors[] = "Your email address is not in a valid email format";
        } elseif (!empty($_POST['user_name'])
            && strlen($_POST['user_name']) <= 64
            && strlen($_POST['user_name']) >= 4
            && preg_match('/^[a-z\d]{2,64}$/i', $_POST['user_name'])
            && !empty($_POST['user_email'])
            && strlen($_POST['user_email']) <= 64
            && filter_var($_POST['user_email'], FILTER_VALIDATE_EMAIL)
            && !empty($_POST['user_password_new'])
            && !empty($_POST['user_password_repeat'])
            && ($_POST['user_password_new'] === $_POST['user_password_repeat'])
        ) {
            
            $this->db_connection = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);

            
            if (!$this->db_connection->set_charset("utf8")) {
                $this->errors[] = $this->db_connection->error;
            }

            
            if (!$this->db_connection->connect_errno) {

                
                $user_name = $this->db_connection->real_escape_string(strip_tags($_POST['user_name'], ENT_QUOTES));
                $user_email = $this->db_connection->real_escape_string(strip_tags($_POST['user_email'], ENT_QUOTES));

                $user_password = $_POST['user_password_new'];

               
                $user_password_hash = password_hash($user_password, PASSWORD_DEFAULT);

                
                $sql = "SELECT * FROM users WHERE user_name = '" . $user_name . "' OR user_email = '" . $user_email . "';";
                $query_check_user_name = $this->db_connection->query($sql);

                if ($query_check_user_name->num_rows == 1) {
                    $this->errors[] = "Login e/ou Email já cadastrados. Tente novamente.";
                } else {
                    
                    $sql = "INSERT INTO users (user_name, user_password_hash, user_email)
                            VALUES('" . $user_name . "', '" . $user_password_hash . "', '" . $user_email . "');";
                    $query_new_user_insert = $this->db_connection->query($sql);

                    
                    if ($query_new_user_insert) {
                        $this->messages[] = "Sua conta foi criada com sucesso! Você já pode fazer login.";
                    } else {
                        $this->errors[] = "Erro ao tentar se cadastrar. Tente novamente.";
                    }
                }
            } else {
                $this->errors[] = "Erro ao se conectar com o banco de dados, entre em contato com a administração ou tente novamente mais tarde.";
            }
        } else {
            $this->errors[] = "Erro desconhecido, entre em contato com a administração ou tente novamente mais tarde.";
        }
    }
}

Depois que a pessoa faz login é aberta a session.

 

<?php
	<?php


class Login
{
    
    private $db_connection = null;
    
    public $errors = array();
    
    public $messages = array();

    
    public function __construct()
    {
        
        session_start();

        if (isset($_GET["logout"])) {
            $this->doLogout();
        }
        
        elseif (isset($_POST["login"])) {
            $this->dologinWithPostData();
			header("location:index.php");
        }
		elseif (isset($_POST["login2"])) {
            $this->dologinWithPostData();
			header("location:register.php");
        }
    }

    
    private function dologinWithPostData()
    {
        
        if (empty($_POST['user_name'])) {
            $this->errors[] = "Username field was empty.";
        } elseif (empty($_POST['user_password'])) {
            $this->errors[] = "Password field was empty.";
        } elseif (!empty($_POST['user_name']) && !empty($_POST['user_password'])) {

           
            $this->db_connection = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);

            
            if (!$this->db_connection->set_charset("utf8")) {
                $this->errors[] = $this->db_connection->error;
            }

            
            if (!$this->db_connection->connect_errno) {

                
                $user_name = $this->db_connection->real_escape_string($_POST['user_name']);

               
                $sql = "SELECT user_name, user_email, user_password_hash
                        FROM users
                        WHERE user_name = '" . $user_name . "' OR user_email = '" . $user_name . "';";
                $result_of_login_check = $this->db_connection->query($sql);

               
                if ($result_of_login_check->num_rows == 1) {

                    
                    $result_row = $result_of_login_check->fetch_object();

                    
                    if (password_verify($_POST['user_password'], $result_row->user_password_hash)) {

                        
                        $_SESSION['user_name'] = $result_row->user_name;
                        $_SESSION['user_login_status'] = 1;

                    } else {
                        $this->errors[] = "Wrong password. Try again.";
                    }
                } else {
                    $this->errors[] = "ESSE USUÁRIO NÃO EXISTE!";
                }
            } else {
                $this->errors[] = "Database connection problem.";
            }
        }
    }

    
    public function doLogout()
    {
        
        $_SESSION = array();
        session_destroy();
        
        $this->messages[] = "DESLOGADO COM SECUSSO!";
		header("Location: index.php");
    }

    
    public function isUserLoggedIn()
    {
        if (isset($_SESSION['user_login_status']) AND $_SESSION['user_login_status'] == 1) {
            return true;
        }
        return false;
    }
}
?>

Eu dei uma alterada no código quando criei o tópico, pois não estava com o código no pc. Mas depois que é feito o login eu pego os dados assim:

<?php	
 	$sqlDados    = "SELECT * FROM users WHERE user_name = '" . $_SESSION['user_name'] . "'";
	$queryDados  = mysql_query($sqlDados,$conexao);
	$dados       = mysql_fetch_array($queryDados);
	$nome        = $dados ['user_name'];
	$email       = $dados ['user_email'];
	$user_id     = $dados ['user_id'];
	$liberacao   = $dados ['liberacao'];
?>

 

No caso, eu vou alterar essa consulta para PDO.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Gostaria de deixar os links dos tutoriais mais completos que já vi sobre o assunto:

https://phpdelusions.net/sql_injection

https://phpdelusions.net/mysqli

 

Sobre a questão do integer, existe uma falha que pode ocorrer. Não é sempre e é específico de alguns casos/bancos:

Citar

First of all, there is a potential problem with integers. There is a chance that some numerical string for some reason can be casted to the numeric type and then bound using such a deduction as int, and then, when compared to a string field to database, will return unwanted results. SELECT * FROM users WHERE password=0 query will match any user whose password starts from a letter. That's why I prefer to either bind a variable as a string by default, or set the type explicitly. I had a disastrous experience with such a deduction once, and so once bitten I am twice shy.

4

Esse comentário é do autor do tutorial acima no link sobre SQL Injection. 

 

Sobre a questão geral de SQL Injection apenas, o que ele quer demonstrar (além deste comentários e entre outros tutorias e comentários no stack overflow) é que algumas soluções são viáveis em alguns casos e em outros é necessário algo além. Entretanto, é importante se focar em uma detertminada solução.

 

Acho que este comentário, de outro usuário, resume bem a situação:

Citar

 why not just use Prepared statements and forget about parameter checking

 

Outro detalhe, e esse é um pouco complicado de se colocar em palavras, é que prepared statements são suficientes para o caso de SQL Injection, mas somente usá-las não é o suficiente. É preciso ter cuidados, seja de configuração ou de uso (isso tudo é explicado nos links acima e nos demais links abaixo). 

https://stackoverflow.com/questions/8263371/how-can-prepared-statements-protect-from-sql-injection-attacks/8265319#8265319

https://stackoverflow.com/a/8255054/1628790

 

No mais, sobre o código, acredito que o principal já tenha sido explicado.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Interessantíssimas suas contribuições, Gabriel Heming.

 

O único ponto que me causou estranheza é que o que foi comentado sobre valores inteiros, apesar de verdadeiro, não tem absolutamente nenhum valor prático, uma vez que nunca iríamos fazer um typecast int em um campo password/string, já que este campo evidentemente não se trata de um valor inteiro.

Além disso campos de senha geralmente sofrem uma função hashing antes de seu uso.

 

De todo modo, vou ler todos os links depois com calma, pois de fato estão bem completos e é um tema interessante.

Obrigado! :smile:

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Matheus Tavares sim, nesse caso, foi apenas por questões informativas e, também. para fazer uma ponte à justificativa de usar sempre o mesmo método (prepared statements)

Compartilhar este post


Link para o post
Compartilhar em outros sites

No caso usando PDO, o código ficaria assim:

 

<?php
	$pegaDadosUsuario = $pdo->prepare("SELECT * FROM users WHERE user_name=:userName");
	$pegaDadosUsuario->bindValue(":userName",$_SESSION['user_name']);
	$pegaDadosUsuario->execute();
	$liberacao = $pegaDadosUsuario->fetch(PDO::FETCH_ASSOC);
		if((int)$liberacao['liberacao'] == 0):
			echo "<img src='imagens/video1.png'>";
		elseif((int)$liberacao['liberacao'] == 1):
			echo "<video width='1024px' height='768px' controls>
  				      <source src='videos/video1.mp4' type='video/mp4'>
				  </video>";
		endif;
?>

Já estaria mais seguro??

Compartilhar este post


Link para o post
Compartilhar em outros sites

A palestra que o @Omar~ postou vale ouro!

 

Achei que seria mais uma palestra cheia de termos que jamais vi na vida, mas foi super simples, de fácil entendimento e com recursos que salvam vidas!

 

Caí de paraquedas no post, mas gostaria de agradece-lo por me mostrar esse vídeo! :D

Compartilhar este post


Link para o post
Compartilhar em outros sites

  • Conteúdo Similar

    • Por Rafael_Ferreira
      Não consigo carregar a imagem do captcha do meu formulário. Foi testado com o xampp e easyphp. Também não carregou a imagem de outros captcha. 
       
       
    • Por luiz monteiro
      Olá, tudo bem?
       
      Estou melhorando meu conhecimento em php e mysql e, me deparei com o seguinte. A tabela da base de dados tem um campo do tipo varchar(8) o qual armazena números. Eu não posso alterar o tipo desse campo. O que preciso é fazer um select para retornar o números que contenham zeros a direita ou a esquerda.
      O que tentei até agora
       
      Ex1
      $busca = $conexao->prepare("select campo form tabela where (campo = :campo) ");
      $busca->bindParam('campo', $_REQUEST['campo_form']);
       
      Se a direita da string $_REQUEST['campo_form'] termina ou inicia com zero ou zeros, a busca retorna vazio.
      Inseri dados numéricos, da seguinte maneira para testar: 01234567;  12345670: 12345678: 12340000... entre outros nessa coluna. Todos os valores que não terminam ou não iniciam com zero ou zeros, o select funciona.
       
       
      Ex2
      $busca = $conexao->prepare("select campo form tabela where (campo = 0340000) ");
      Esse número está cadastrado, mas não retorna.
       
      Ex3
      $busca = $conexao->prepare("select campo form tabela where (campo = '02340001' ) ");
      Esse número está cadastrado, mas não retorna.
       
       
      Ex4
      $busca = $conexao->prepare("select campo form tabela where (campo like 2340000) ");
      Esse número está cadastrado, mas não retorna.
       
      Ex5
      $busca = $conexao->prepare("select campo form tabela where (campo like '12340000') ");
      Esse número está cadastrado, mas não retorna.
       
      Ex6
      $busca = $conexao->prepare("select campo form tabela where (campo like '"12340000"' ) ");
      Esse número está cadastrado, mas não retorna.
       
       
      Ex7
      $busca = $conexao->prepare("select campo form tabela where (campo like :campo) ");
      $busca->bindParam('campo', $_REQUEST['campo_form'])
      Não retorna dados.
       
      O  $_REQUEST['campo_form'] é envio via AJAX de um formulário. 
      Usei o gettype para verificar o post, e ele retorna string.
      Fiz uma busca com número 12345678 para verificar o que o select retorna, e também retrona como string.
       
      Esse tipo de varchar foi usado porque os números que serão gravados nesse campo,  terão zeros a direita ou na esquerda. Os tipos number do mysql não gravam zeros, então estou usando esse. O problema é a busca.
      Agradeço desde já.
       
       
    • Por daemon
      Tenho 3 selects assim com varias categorias...
       
              $sql = "SELECT * FROM topicos          WHERE idcategoria = $idcategoria          AND pubdate BETWEEN NOW() - INTERVAL $intervalo1_horas_ou_minutos $tipo_intervalo1_horas_mnutos AND          NOW() - $intervalo2_horas_ou_minutos ORDER BY pubdate DESC LIMIT 1"; Preciso que na minha pagina principal (index) mostre este registro por 10minutos.. passando de 10 minutos mostra o resultado do proximo select (categoria)..
    • Por daemon
      Boa tarde,
       
      Eu tenho uma rotina que faz uma leitura do arquivo .xml de vários sites.

      Eu consigo pegar o tópico e a descrição, e mostrar a imagem que esta na pagina do link.
      Para isso utilizo esta função:
      function getPreviewImage($url) { // Obter o conteúdo da página $html = file_get_contents($url); // Criar um novo objeto DOMDocument $doc = new DOMDocument(); @$doc->loadHTML($html); // Procurar pela tag meta og:image $tags = $doc->getElementsByTagName('meta'); foreach ($tags as $tag) { if ($tag->getAttribute('property') == 'og:image') { return $tag->getAttribute('content'); } } // Se não encontrar og:image, procurar pela primeira imagem na página $tags = $doc->getElementsByTagName('img'); if ($tags->length > 0) { return $tags->item(0)->getAttribute('src'); } // Se não encontrar nenhuma imagem, retornar null return null; } // Uso: $url = "https://example.com/article"; $imageUrl = getPreviewImage($url); if ($imageUrl) { echo "<img src='$imageUrl' alt='Preview'>"; } else { echo "Nenhuma imagem encontrada"; }  
      Mas estou com um problema, esta funcão funciona quando coloco em uma pagina de teste.php. Preciso mostrar em uma página inicial diversas fotos de todos os links. (No caso acima só funciona 1).
    • Por violin101
      Caros amigos, saudações.
       
      Por favor, me permita tirar uma dúvida com os amigos.

      Tenho um Formulário onde o Usuário digita todos os Dados necessários.

      Minha dúvida:
      --> como faço após o usuário digitar os dados e salvar, o Sistema chamar uma Modal ou mensagem perguntando se deseja imprimir agora ?

      Grato,
       
      Cesar
×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.