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 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
    • Por Carcleo
      Tenho uma abela de usuarios e uma tabela de administradores e clientes.
      Gostaria de uma ajuda para implementar um cadastro
       
      users -> name, login, passord (pronta) admins -> user_id, registratiom, etc.. client -> user_id, registratiom, etc...
      Queria ajuda para extender de user as classes Admin e Client
      Olhem como estáAdmin
      <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Admin extends User {     use HasFactory;            protected $fillable = [         'name',         'email',         'password',         'registration'     ];      private string $registration;     public function create(         string $name,          string $email,          string $password,         string $registration     )     {         //parent::create(['name'=>$name, 'email'=>$email, 'password'=>$password]);         parent::$name = $name;         parent::$email = $email;         parent::$password = $password;         $this->registration = $registration;     } } User
      <?php namespace App\Models; // use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class User extends Authenticatable {     /** @use HasFactory<\Database\Factories\UserFactory> */     use HasFactory, Notifiable;     static string $name;     static string $email;     static string $password;     /**      * The attributes that are mass assignable.      *      * @var list<string>      */     protected $fillable = [         'name',         'email',         'password',     ];          /**      * The attributes that should be hidden for serialization.      *      * @var list<string>      */     protected $hidden = [         'remember_token',     ];     /**      * Get the attributes that should be cast.      *      * @return array<string, string>      */     protected function casts(): array     {         return [             'email_verified_at' => 'datetime',             'password' => 'hashed',         ];     }          public function roles() : BelongsToMany {         return $this->belongsToMany(Role::class);     }       public function hasHole(Array $roleName): bool     {                 foreach ($this->roles as $role) {             if ($role->name === $roleName) {                 return true;             }         }         return false;     }         public function hasHoles(Array $rolesName): bool     {                 foreach ($this->roles as $role) {             foreach ($rolesName as $rolee) {             if ($role->name === $rolee) {                 return true;             }          }         }         return false;     }         public function hasAbility(string $ability): bool     {         foreach ($this->roles as $role) {             if ($role->abilities->contains('name', $ability)) {                 return true;             }         }         return false;     }     } Como gravar um Admin na tabela admins sendo que ele é um User por extensão?
      Tentei assim mas é claro que está errado...
      public function store(Request $request, Admin $adminModel) {         $dados = $request->validate([             "name" => "required",             "email" => "required|email",             "password" => "required",             "registration" => "required"         ]);         $dados["password"] =  Hash::make($dados["password"]);                  $admin = Admin::where("registration",  $dados["registration"])->first();                  if ($admin)              return                    redirect()->route("admin.new")                             ->withErrors([                                 'fail' => 'Administrador já cadastrados<br>, favor verificar!'                   ]);                            $newAdmin = $adminModel->create(                                    $dados['name'],                                    $dados['email'],                                    $dados['password'],                                    $dados['registration']                                 );         dd($newAdmin);         $adminModel->save();         //$adminModel::create($admin);                  return redirect()->route("admin.new")->with("success",'Cadastrado com sucesso');     }  
    • Por violin101
      Caros amigos, saudações.
       
      Gostaria de tirar uma dúvida com os amigos, referente a PDV.
       
      Estou escrevendo um Sistema com Ponto de Vendas, a minha dúvida é o seguinte, referente ao procedimento mais correto.

      Conforme o caixa vai efetuando a venda, o Sistema de PDV já realiza:
      a baixa direto dos produtos no estoque
      ou
      somente após concretizar a venda o sistema baixa os produtos do estoque ?
       
      Grato,
       
      Cesar
       
    • Por violin101
      Caros amigos do grupo, saudações e um feliz 2025.
       
      Estou com uma pequena dúvida referente a Teclas de Atalho.

      Quando o Caps Lock está ativado o Comando da Tecla de Atalho não funciona.
      ou seja:
      se estiver para letra minúscula ====> funciona
      se estiver para letra maiúscula ====> não funciona
       
      Como consigo evitar essa falha, tanto para Letra Maiúscula quanto Minúscula ?

      o Código está assim:
      document.addEventListener( 'keydown', evt => { if (!evt.ctrlKey || evt.key !== 'r' ) return;// Não é Ctrl+r, portanto interrompemos o script evt.preventDefault(); });  
      Grato,
       
      Cesar
    • Por violin101
      Caros amigos, saudações.
       
      Por favor, poderiam me ajudar.

      Estou com a seguinte dúvida:
      --> como faço para para implementar o input código do produto, para quando o usuário digitar o ID o sistema espera de 1s a 2s, sem ter que pressionar a tecla ENTER.

      exemplo:
      código   ----   descrição
           1       -----   produto_A
       
      Grato,
       
      Cesar
×

Informação importante

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