New question

Question:

Date: 05-03-2016 17:41:33 (In Spanish)

Evitar ataques CSRF en Formularios (Debate)[Resolved]

Hola a todos

En esta ocasión propongo un debate abierto sobre las mejores practicas para evitar un ataque CSRF (Cross Site Request Forgery) en los formularios.

Como punto de partida dejare un script de ejemplo básico con el cual podremos ir mejorandolo a lo largo del debate.

Archivo: token.class.php

<?php
/**
* Generador y chequeo de Token 
*/
class Token 
{
	
	public static function generate()	{
		return $_SESSION['token']	= base64_encode(openssl_random_pseudo_bytes(32));
	}

	public static function check($token){
		if(isset($_SESSION['token']) && $_SESSION['token'] === $_SESSION['token']){
			unset($_SESSION['token']);
			session_destroy();
			return true;
		}
		return false;
	}

}


Archivo: form.php

<?php
session_start();
require_once 'token.class.php';
if(isset($_POST['nombre'], $_POST['apellido'], $_POST['token'])) {
	$nombre = $_POST['nombre'];
	$apellido = $_POST['apellido'];

	if(!empty($_POST['nombre']) && !empty($_POST['apellido'])){
		if(Token::check($_POST['token'])){
			echo '<font color="green">Ingreso del formulario valido.</font>';
		}else{
			echo '<font color="red">Ingreso del formulario invalido, ataque CSRF.</font>';
		}
	}else{
			echo '<font color="red">Ha enviado el formulario con los campos vacios.</font>';
	}
}

?>

<!DOCTYPE html>
<html lang="es">
<head>
	<meta charset="UTF-8">
	<title>Formulario con CSRF -Token</title>
</head>
<body>
<h2>Formulario con CSRF Token</h2>
<form method="POST" action="">
	<p>Nombre: <input type="text" name="nombre" size="20"></p>
	<p>Apellido: <input type="text" name="apellido" size="20"></p>
	<p><input type="hidden" name="token" value="<?php echo Token::generate(); ?>"></p>
	<p><input type="submit" value="Enviar" name="enviar"></p>
</form>
</body>
</html>


Con la participación de todos los ciudadanos de esta comunidad muchos aprenderemos de su experiencia volcada en las respuestas, las cuales son agradecidas.

Espero que muchos de ustedes puedan aportar sugerencias y el codigo mejorado.

Que tengan un buen dia.

Saludos.
Tags: CSRF - Debate - Form - PHP - Question - Security - Suggestion Votes: 3 - Answers: 6 - Views: 34 Share on: Google Facebook Twitter LinkedIn Link
 

Answers:

  • Date: 06-03-2016 19:08:08 adicional a eso creo que se debería limpiar las variables que llegan del formulario por ejemplo
    $nombre = filter_input(INPUT_POST,'nombre',FILTER_SANITIZE_STRING);
    $nombre = filter_input(INPUT_POST,'apellido',FILTER_SANITIZE_STRING);
    
      Votes: 4 - Link answer
     
  • Date: 07-03-2016 06:49:15 Que buen tema chicos, el filter_input es una buena opcion y es compatible con todos los PHP desde la 5.2. Y para manejarlo bien estará bueno que aprendan sus posibilidades:
    http://php.net/manual/es/function.filter-input.php
    http://php.net/manual/es/filter.filters.validate.php
    http://php.net/manual/es/filter.filters.sanitize.php
    http://php.net/manual/es/filter.filters.misc.php (ideal si quieren elaborar sus propios filtros)
    y las flags soportadas: http://php.net/manual/es/filter.filters.flags.php

    El problema es cuando tenés un formulario complejo con muchos campos. En el primer ejemplo que publicaron tendrías que agregar una evaluación de !empty por cada uno, pero yo primero recorrería la lista de parámetros y los sanitizaría sin usar filtros, lo cual es más rápido:

    $charset_de_origen=="UTF-8";
    $charset_a_usar=="ISO-8859-1";
    $Parametros=array();
    
    foreach($_POST as $key => $value){
    	$value=htmlspecialchars($value, ENT_QUOTES, $charset_de_origen);
    	$value=mb_convert_encoding($value, $charset_de_origen, $charset_a_usar);
    	$value=htmlentities($value, ENT_QUOTES, $charset_a_usar);
    	array_push($Parametros,$key,$value);
    }


    Esto nos devolvería una matriz llamada $Parametros con todo el contenido del $_POST ya sanitizado y listo para que procesemos. Yo en ese caso tengo un form con charset UTF-8 pero como el cliente usa todavía un Outlook viejo, necesita que le llegue convertido a ISO-8859-1, pero la conversión es opcional.

    Y si hacen esto por supuesto recordar que si se debe enviar por mail, hay que insertarle una cabecera con el charset correcto:

    "Content-type: text/html; charset=".$charset_a_usar."\n";
    


    Otra cosa que hago para evitar bots, es agregar al formulario un par de campos de texto, ocultos, y al recibir el POST verifico que esos campos sigan estando vacíos. Si están !empty entonces lo envía un bot y por ende se descarta con un mero exit() (no vale la pena dar explicaciones ni mensajes de error en esos casos, a mi modo de ver).

    Buen post para intercambiar ideas. Saludos!
      Votes: 4 - Link answer
     
  • Date: 07-03-2016 10:11:58 Entrando en el debate propuesto por Walter (reinteresante).

    Actualmente estoy recomponiendo las contraseñas de usuarios sobre el script PHP password_hash(). Es (así lo dicen) la mejor versión después de crypt() para versiones de PHP 5.5 y posteriores, inluido PHP 7. Ver function.password-hash.php#example-970
    Cualquier observación será bien recibida
    <?php
    /**
     * Queremos crear un hash de nuestra contraseña usando el algoritmo DEFAULT actual.
     * Actualmente es BCRYPT, y producirá un resultado de 60 caracteres.
     *
     * Hay que tener en cuenta que DEFAULT puede cambiar con el tiempo, por lo que debería prepararse
     * para permitir que el almacenamento se amplíe a más de 60 caracteres (255 estaría bien)
     */
    echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT)."\n";
    ?>


    Simplificando, una contraseña de (ejemplo) rasmuslerdorf crearía una contraseña similar a $2y$10$RqQczgToJZVnQpfr9wjWeOIEtS.5wRl5bL3v3/8sor85Mg9pmaFW.

    Su verificación viene de la mano de password_verify (ver function.password-verify.php#example-975
    $password =  'rasmuslerdorf';
    $hash = '$2y$10$o2JfTPUHMMsT7g7drDaxT.jLEktnnVZTz9f4mV4Sb3WN3AfzpBE6O ;
    if (password_verify($password, $hash)) {
        echo 'La contraseña es válida';
    } else {
        echo 'La contraseña no es válida.';
    }


    Si la experiencia es importante, me lo comentan.

    Si puede generar fallos de seguridad, ... ?!?!? kaputt a todo mi trabajo ¡¡Diganmelo!!
      Votes: 5 - Link answer
     
  • Date: 09-03-2016 10:04:27 comparto una forma de testiar si nuestros login seles puede hacer ataques de fuerza dura o brutal
    Ataque de Fuerza Brutal
    esube probanco enviando los datos de esta forma y pues al parecer o no lo reconose o no lo supe implementar

    
      <body>
        <?php
            require_once "controller.php";
            $form = new Controller;
            session_start();
    
           ?>
        <form class="" action="" method="post">
          <input type="text" name="Nombre" value="">
          <input type="password" name="Pass" value="">
          <input type="text" name="token" value="<?=$form->Token()?>" style="display:none">
          <input type="submit" name="login"   value="Entrar" >
    
        </form>
        <?php
          if(isset($_POST['login']))
          {
            $form->Login();
          }
    
         ?>
    
      </body>

    Contolador login

      public function Login()
      {
        $this->nombre = filter_input(INPUT_POST,'Nombre',FILTER_SANITIZE_STRING);
        $this->contrasena = filter_input(INPUT_POST,'Pass',FILTER_SANITIZE_STRING);
        $this->token = filter_input(INPUT_POST,'token',FILTER_SANITIZE_STRING);
        $pass = (string)"LoginRoot";
        if(session_status() === PHP_SESSION_ACTIVE ? TRUE : FALSE){
          if(password_verify($this->contrasena,password_hash($pass,PASSWORD_DEFAULT))){
              $_SESSION['User'] = array(
                'UserName' =>(string) $this->nombre,
                'UserTokenUnik'=> $_SESSION['token'],
              );
              unset($_SESSION['token']);
              echo 'Log aCtivo';
          }else {
            echo json_encode(array('error' => 'Algo a pasado intentalo de nuevo '));
          }
        }else{
          echo "Ya Hay una Session Activa ";
    
          session_destroy();
          unset($_SESSION['User']);
    
        }
    
      }
    
      Votes: 1 - Link answer
     
  • Date: 18-03-2016 18:57:07 una forma efectiva es como dijo Walter es poner un token que se guarde en una sesion, este token debe que ser diferente cada vez que se inicie sesion pero no basta con eso tambien hay que considerar llamar de diferente forma el token cada vez que se inicie sesion asi la persona aunque vea el codigo fuente pensara que es siempre esto : $_SESSION['token'] pero no sera siempre asi, para la siguiente vez podia llamarse $_SESSION['token2'] y asi continuamente haciendo imposible adivinar incluso como se llamara el value del la sesion de cada usuario ya que seran diferentes una y otra vez.

    pd: disculpen por mi mala ortografia xD.

    Saludos :)
      Votes: 1 - Link answer
     
  • Date: 07-02-2018 20:53:07 Te conviene tomar los datos del formulario y enviarlos a través de ajax.
    En el archivo php de control haces la consulta a través de una función de saneamiento y compones la consulta para llamar a una clase que maneje las queries sql:
    $user_email = $_POST['email'];
    $user_pass = md5($_POST['pass']);
    //
    $cols = array('id', 'name', 'img', 'status');
    $query = array('col_' => $cols, 'email' => $user_email, 'pass' => $user_pass);
    $sql = Query('users', 'accounts', 'select', $query);
    isset( $sql->result ) ? $log = true : $log = false;
    

    De esta manera jamás te puede devolver algo que no esté previamente asignado.
      Votes: 0 - Link answer
     
To actively participate in the community first must authenticate, enter the system.Sign In
 
frjcbbae garagebible.com