Ir para conteúdo

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 landerbadi
      Boa tarde pessoal. Estou tentado fazer uma consulta no banco de dados porém estou tendo dificuldades. Tenho uma tabela chamada "itens" com os seguintes campos: id, item, ativo. Nela tem cadastrado vários itens. No campo ativo eu coloco a letra "S" para informar que este item está ativo no sistema. Por exemplo: 1, casa, S 2, mesa, S 3, cama, S 4, moto S 5, rádio O quinto registro "radio" não está ativo no sistema pois não tem um "S" no campo ativo. E outra tabela chamada "produtos" com os seguintes campos (id, item1, item2, item3) com os seguintes registros: 1, casa, mesa, moto 2, mesa, casa, cama 3, rádio, cama, mesa Eu preciso fazer uma busca na tabela produtos da seguinte maneira: Eu escolho um registro na tabela "itens", por exemplo "mesa". Preciso fazer com que o php me liste todos os registros da tabela "produtos" que contenham a palavra "mesa". Até aqui tudo bem eu consigo listar. Estou fazendo assim: <?php $item = "mesa" $sql = mysqli_query($conn, "SELECT * FROM produtos WHERE item1 LIKE '$item' OR item2 LIKE '$item' OR item3 LIKE '$item' LIMIT 10"); while($aux = mysqli_fetch_assoc($sql)) { $id = $aux["id"]; $item1 = $aux["item1"]; $item2 = $aux["item2"]; $item3 = $aux["item3"]; echo $id . " - " . $item1 . ", " . $item2 . ", " $item3 . "<br>"; } ?> O problema é que está listando todos os registros que contém o item mesa. Eu preciso que o php verifique os demais item e me liste somente os registro em que todos os registros estejam ativos no sistema. No exemplo acima ele não deveria listar o registro 3. pois nesse registro contém o item "radio" e este item não está ativo no sistema. Ou seja, o registro "radio" na tabela itens não possui um "S" na coluna "ativo". Alguém sabe como resolver isso?
    • Por ILR master
      Fala galera.
      Espero que todos estejam bem.
      Seguinte: Tenho um arquivo xml onde alguns campos estão com : (dois pontos), como o exemplo abaixo:
       
      <item>
      <title>
      d sa dsad sad sadasdas
      </title>
      <link>
      dsadas dsa sad asd as dsada
      </link>
      <pubDate>sadasdasdsa as</pubDate>
      <dc:creator>
      d sad sad sa ad as das
      </dc:creator>
      </item>
       
      Meu código:
       
      $link = "noticias.xml"; 
      $xml = simplexml_load_file($link); 
      foreach($xml -> channel as $ite) {     
           $titulo = $ite -> item->title;
           $urltitulo = $ite -> item->link;
           print $urltitulo = $ite -> item->dc:creator;
      } //fim do foreach
      ?>
       
      Esse campo dc:creator eu não consigo ler. Como faço?
       
      Agradeço quem puder me ajudar.
       
      Abs
       
       
    • Por First
      Olá a todos!
       
      Eu estou criando um sistema do zero mas estou encontnrando algumas dificuldades e não estou sabendo resolver, então vim recorrer ajuda de vocês.
      Aqui está todo o meu código: https://github.com/PauloJagata/aprendizado/
       
      Eu fiz um sistema de rotas mas só mostra o conteúdo da '/' não sei porque, quando eu tento acessar o register nada muda.
      E eu também quero que se não estiver liberado na rota mostra o erro de 404, mas quando eu tento acessar um link inválido, nada acontece.
      Alguém pode me ajudar com isso? E se tiver algumas sugestão para melhoria do código também estou aceitando.
       
       
      Desde já, obrigado.
    • Por landerbadi
      Olá pessoal, boa tarde
       
      Tenho uma tabela chamada "produtos" com os seguintes campos (id, produto) e outra tabela chamada "itens" com os seguintes campos (id, prod_01, prod_02, prod_03, prod_04).
       
      Na tabela produtos eu tenho cadastrado os seguintes produtos: laranja, maçã, uva, goiaba, arroz, feijão, macarrão, etc.
       
      Na tabela itens eu tenho cadastrado os itens da seguinte maneira:
       
      1, laranja, uva, arroz, feijão;
      2, maçã, macarrão, goiaba, uva;
      3, arroz, feijão, maçã, azeite
       
      Meu problema é o seguinte: 
      Eu escolho um produto da tabela "produtos", por exemplo "uva".  Preciso fazer uma consulta na tabela "itens" para ser listado todos os registros que contenham o produto "uva" e que todos os demais produtos estejam cadastrados na tabela "produtos".
       
      No exemplo acima seria listado apenas dois registros, pois o terceiro registro não contém o produto "uva". 
       
      Alguém pode me ajudar? Pois estou quebrando a cabeça a vários dias e não consigo achar uma solução.
    • Por landerbadi
      Boa tarde pessoal. Estou tentado fazer uma consulta no banco de dados porém estou tendo dificuldades. Tenho uma tabela chamada "itens" com os seguintes campos: id, item, plural, ativo. Nela tem cadastrado vários itens e seu respectivo plural. No campo ativo eu coloco a letra "S" para informar que esta palavra está ativa no sistema. Por exemplo: 1, casa, casas, S 2, mesa, mesas, S 3, cama, camas, S 4, moto, motos, S 5, rádio, rádios O quinto registro "radio" não está ativo no sistema pois não tem um "S" no campo ativo. E outra tabela chamada "variações" com os seguintes campos (id, item1, item2, item3) com os seguintes registros: 1, casa, camas, moto 2, mesas, casas, radio 3, rádio, cama, mesa Eu preciso fazer uma busca na tabela variações da seguinte maneira: Eu escolho um registro na tabela "itens", por exemplo "casa". Preciso fazer com que o php me liste todos os registros da tabela "variações" que contenham a palavra "casa". Porém se tiver algum registro com a palavra "casas" também tem que ser listado. Neste caso ele irá encontrar dois registros. Agora eu preciso que o php verifique os demais itens e faça a listagem apenas dos item que estão ativos (que contenham um "S" no campo ativo. Neste caso ele irá encontrar apenas um registro, pois o segundo registro contém a palavra "rádio". E "rádio" não está ativo na tabela itens. Como faço isso?
×

Informação importante

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