Nueva pregunta

Pregunta:

Fecha: 23-11-2015 16:40:57 (En Español)

Clase Password PHP[Resuelta]

Buenas comunidad yo de nuevo por aquí quemando pestañas, estoy realizando de modo de practica una clase para generar contraseñas aletorias.

A la hora de ver si la contraseña es fuerte tiene que tener 3 o mas numeros, 2 o mas minuscula, 2 o mas mayuscula y 2 o mas caracteres.

Esta seria la clase.


<?php

class Password {
   private $tipo;
   private $longitud;
   private $password;

   public function __construct($ti, $long) {
      $this->tipo = $ti;
      $this->longitud = $long;

   //Dependiendo de lo que pase el constructor ejecuto al metodo.
      switch ($this->tipo) {
         case 'numero':
            $this->numero();
            break;
         case 'letra':
            $this->letra();
            break;
         case 'alfanumerica':
            $this->alfanumerica();
            break;
         case 'todoCaracteres':
            $this->todoCaracteres();
            break;
      }
   }

   private function numero() {
      for($i = 0; $i < $this->longitud; $i++) {
         //Numero 0, 9 ASCII
         $caracteres[0] = chr(rand(48, 57));

         $this->password .= $caracteres[0];
      }
   }

   private function letra() {
      for($i = 0; $i < $this->longitud; $i++) {
         //a - z minuscula ASCII
         $caracteres[0] = chr(rand(65, 90));
         //A - Z Mayuscula ASCII
         $caracteres[1] = chr(rand(97, 122));

         $this->password .= $caracteres[rand(0,1)];
      }
   }

   private function alfanumerica() {
      for($i = 0; $i < $this->longitud; $i++) {
         //Numero 0, 9 ASCII
         $caracteres[0] = chr(rand(48,57));
         //a - z minuscula ASCII
         $caracteres[1] = chr(rand(65, 90));
         //A - Z Mayuscula ASCII
         $caracteres[2] = chr(rand(97, 122));

         $this->password .= $caracteres[rand(0, 2)];
      }
   }

   private function todoCaracteres() {
      for($i = 0; $i < $this->longitud; $i++) {

         //Dependiendo de lo que salga aqui me agrega algo a la propiedad password.
         $aleatorio = rand(0, 3);

         //Numero 0, 9 ASCII
         $caracteres[0] = chr(rand(48,57));
         //a - z minuscula ASCII
         $caracteres[1] = chr(rand(65, 90));
         //A - Z Mayuscula ASCII
         $caracteres[2] = chr(rand(97, 122));
         //Cadena con signos.
         $caracteres[3] = '!"#$%&\'()*+,-/[]^_{|}~.;:';

            switch($aleatorio) {
               case 0:
                  $this->password .= $caracteres[0];
                  break;
               case 1:
                  $this->password .= $caracteres[1];
                  break;
               case 2:
                  $this->password .= $caracteres[2];
                  break;
               case 3:
                  //Me toma un caracter alzar de la cadena caracteres[3]
                  $this->password .= substr($caracteres[3], rand(0, strlen($caracteres[3])-1), 1);
                  break;
            }
      }
   }

   public function get() {
      echo $this->password;
   }

   public function esFuerte() {

      //numeros
      preg_match_all('/[[:digit:]]/', $this->password, $numero);
      //minuscula
      preg_match_all('/[[:lower:]]/', $this->password, $minuscula);
      //mayuscula
      preg_match_all('/[[:upper:]]/', $this->password, $mayuscula);
      //Marca de puntuacion .''?;
      preg_match_all('/[[:punct:]]/', $this->password, $caracter);

      if(count($numero[0]) >=3 && count($minuscula[0]) >= 2 && count($mayuscula[0]) >= 2 && count($caracter[0])  >= 2) {
         echo 'Es Fuerte...<br/>';
      }
   }
}

$p = new Password('todoCaracteres', 12);
$p->esFuerte();
$p->get();
 ?>


El tema seria si en el constructor me pasa 'numero' voy a tener una constraseña con 12 numeros y para que sea fuera fuerte me gustaria que tenga mas de 14 caracteres.

Que seria lo ideal hacer esto?

   private function numero() {
      //Me genera la contraseña.

   public function esFuerte() {

    //Compruebo si tiene mas de 14 caracteres.
      }
   }
   


Es decir cuando en el constructor llama al metodo numero() que me genera la contraseña y de paso me calcule si es fuerte o lo mejor es tener un metodo esFuerte() separado?


Disculpen si no me pude expresar de la mejor manera, cualquier duda que tengan estoy aquí para responder al igual si me quieren decir algo por el diseño de la clase estoy para aprender con ustedes si es posible.

¡Saludos y Gracias!
Etiquetas: Clase PHP - Contraseña - PHP - POO - Pregunta - Seguridad Votos: 2 - Respuestas: 10 - Vistas: 30 Compartir en: Google Facebook Twitter LinkedIn Link
 

Respuestas:

  • Fecha: 24-11-2015 05:17:16 Hola Gonzalo, aquí te dejo una clase que hice yo para generar password's de forma aleatoria y fuertes, configurable para aumentar el nivel de fortaleza de la password por medio de las constantes de la clase, con una lógica de recursividad que informa el esfuerzo de ejecución realizado para llegar al requerimiento de fortaleza (lo que nos permite afinar las restricciones).

    Por otro lado, también trate de utilizar las mejores prácticas de programación, uso de getters y setters, encapsulamiento (definición de alcances public private), utilización del método toString() para poder utilizar el objeto como un string en una concatenación, evitando el error "Catchable fatal error: Object of class Password could not be converted to string in...", uso de constantes, etc.

    La función clave de la clase es permitir tanto generar una nueva password como también instanciar una password existente, con la restricciones de fortaleza de password configurables.

    <?php
    class Password {
    
        const LONGITUD = 15; //longitud de la password autogenerada
        const LONGITUD_MINIMA = 5; //longitud mínima para una password instanciada
        const LONGITUD_MAXIMA = 30; //longitud máxima para una password instanciada
        const CADENA_SIGNOS = '!"#$%&\'()*+,-/[]^_{|}~.;:'; //Cadena de signos permitidos en la password
        const NUMEROS_MINIMO = 3; //minima cantidad de números que puede tener la password para ser considerada fuerte
        const MINUSCULAS_MINIMO = 2; //minima cantidad de minúsculas que puede tener la password para ser considerada fuerte
        const MAYUSCULAS_MINIMO = 2; //minima cantidad de mayúsculas que puede tener la password para ser considerada fuerte
        const SIGNOS_MINIMO = 1; //minima cantidad de signos que puede tener la password para ser considerada fuerte
    
        private $cantidadIntentos = 1; //contabiliza la cantidad de intentos para generar la password fuerte, el valor por defecto es 1, indicando que se realizo al primer intento
        private $password = '';
    
        public function getPassword() {
            return $this->password;
        }
    
        private function setPassword($password) {
            //solo se puede setear una password segura. 
            //si la password no es fuerte, se lanza una excepción que deberá ser controlada
            if (!self::esFuerte($password)) {
                throw new Exception('Error: se intento setear una password que no supero la validación de fortaleza.');
            }
            $this->password = $password;
        }
    
        public function getCantidadIntentos() {
            return $this->cantidadIntentos;
        }
    
        private function incrementarCantidadIntentos() {
            return $this->cantidadIntentos++;
        }
    
        private function generarPassword() {
            for ($i = 0; $i < self::LONGITUD; $i++) {
                //flag de entropia de password, dependiendo su aleatoriedad se genera la password
                $aleatorio = rand(0, 3);
                switch ($aleatorio) {
                    case 0:
                        //Numero 0, 9 ASCII
                        $numero = chr(rand(48, 57));
                        $this->password .= $numero;
                        break;
                    case 1:
                        //a - z minuscula ASCII
                        $minuscula = chr(rand(65, 90));
                        $this->password .= $minuscula;
                        break;
                    case 2:
                        //A - Z Mayuscula ASCII
                        $mayuscula = chr(rand(97, 122));
                        $this->password .= $mayuscula;
                        break;
                    case 3:
                        //tomar un caracter al azar de la cadena de signos
                        $signos = '!"#$%&\'()*+,-/[]^_{|}~.;:';
                        $signo = substr($signos, rand(0, strlen($signos) - 1), 1);
                        $this->password .= $signo;
                        break;
                }
            }
    
            //si no es fuerte sigo con el proceso hasta que lo sea (proceso recursivo)
            if (!self::esFuerte($this->password)) {
                set_time_limit(15);
                $this->password = ''; //se reinicializa la password
                $this->incrementarCantidadIntentos();
                return $this->generarPassword(); //si no es fuerte se contabiliza un intento mas y se intenta volver a generar
            } else {
                return $this->getCantidadIntentos(); //es fuerte!, se devuelve la cantidad de intentos
            }
        }
    
        public function __construct($password = false) {
            //si la password no fue especificada, se genera una
            if ($password === false) {
                $this->generarPassword();
            } else {
                //si la password no es fuerte, se lanza una excepción que deberá ser controlada
                $this->setPassword($password);
            }
        }
    
        public function __toString() {
            return $this->getPassword();
        }
    
        /**
         * Segun el algoritmo de esta función, para ser fuerte la password debe tener:
         * por lo menos X mumero
         * por lo menos X minuscula
         * por lo menos X mayuscula
         * por lo menos X signo
         * y debe cumplir con la longitud minima y maxima, todos estos valores configurados en las constantes de la clase.
         * @param type $password password a ser verificada
         * @return boolean es fuerte =true; no es fuerte = false
         */
        public static function esFuerte($password) {
            //arrays usados para comprobar la cantidad de ocurrencias.
            $numeros = array();
            $minusculas = array();
            $mayusculas = array();
            $signos = array();
    
            //numeros
            preg_match_all('/[[:digit:]]/', $password, $numeros);
            //minuscula
            preg_match_all('/[[:lower:]]/', $password, $minusculas);
            //mayuscula
            preg_match_all('/[[:upper:]]/', $password, $mayusculas);
            //Marca de puntuacion .''?;
            preg_match_all('/[[:punct:]]/', $password, $signos);
    
            if (strlen($password) >= self::LONGITUD_MINIMA && strlen($password) <= self::LONGITUD_MAXIMA && count($numeros[0]) >= self::NUMEROS_MINIMO && count($minusculas[0]) >= self::MINUSCULAS_MINIMO && count($mayusculas[0]) >= self::MAYUSCULAS_MINIMO && count($signos[0]) >= self::SIGNOS_MINIMO) {
                //Es Fuerte
                return true;
            } else {
                return false;
            }
        }
    
    }
    ?>
    


    Ejemplos de instanciación:
    <?php
    //Generar una nueva password:
    $objPassword = new Password();
    $respuesta = 'La password generada es: ' . $objPassword;
    echo $respuesta;
    echo '<br/>';
    echo 'Cantidad de intentos para generar la password fuerte: ' . $objPassword->getCantidadIntentos();
    echo '<br/>';
    echo '<br/>';
    
    //Instanciar una password existente (por ejemplo la password que inventa el usuario o que recuperamos de algun repositorio)
    $objPassword2 = new Password('FernAndo471.');
    $respuesta = 'Password fuerte ingresada por el usuario: ' . $objPassword2;
    echo $respuesta;
    echo '<br/>';
    ?>
    


    Espero que esta clase de respuesta a tu pregunta.

    Saludos,
    Fernando
      Votos: 6 - Link respuesta
     
  • Fecha: 24-11-2015 07:01:18 Hola Fernando gracias por responder, esta noche analizare la clase que dejaste para mejorar mi práctica y vuelvo a comentar.

    ¡Saludos!
      Votos: 1 - Link respuesta
     
  • Fecha: 24-11-2015 17:59:21 Muy buenos ejemplos pero aguas con el uso de magic numbers.
    No es aconsejable utilizarlos porque, en implementaciones más complicadas puede volverse difícil seguir el rastro.

    Otro punto a tomar en cuenta son los constructores.
    Establecer funcionalidades al constructor no es recomendable y debe evitarse lo más posible pues ocultas detalles de la implementación y dificulta hacer pruebas unitarias a los objetos.

    Respecto a los setters se prefiere que al declararlos se regrese el objeto pues habilitas el "method chaining" que puede facilitarte el uso del objeto:

    Saludos!
      Votos: 1 - Link respuesta
     
  • Fecha: 25-11-2015 03:42:02 Hola Ernesto, trataré de dar respuesta a cada punto.

    "magic numbers": este punto esta contemplado en el uso de constantes. "magic numbers" es el uso directo de un número en el código, y es por eso que están definidas las constantes, no es necesario hacer mucho más para salvar el punto, también hay opiniones divididas respecto a esto (por lo que no es tan así). Algo más, aumentemos el nivel "aguas con el uso de magic numbers" no esta bueno...

    "constructores": no quería complejisar la clase con un patrón factory, hasta acá estamos bien para los objetivos de la clase.

    "method chaining": interesante punto que es poco difundido debido al nivel de profundización escasa de los desarrolladores php en la teoría de objetos, nuevamente no quise complejizar, pero podríamos hacer que todos los getters y setters devuelvan y reciban objetos, y en este punto por que no también utilizar "type hinting".

    Ernesto, has propuesto muy buenas mejoras, si gustas puedes tomar la clase Password, hacer las modificaciones que consideres y presentarla.

    Saludos,
      Votos: 1 - Link respuesta
     
  • Fecha: 25-11-2015 06:45:20 Uff type hinting, ese tema es siempre olvidado pero es de las mejores prácticas y de lo mejor que se viene en PHP7 :D   Votos: 0 - Link respuesta
     
  • Fecha: 25-11-2015 07:11:51 Para que no se mal entienda, type hinting esta disponible en PHP5 desde hace unos cuantos años, no es algo que se viene con PHP7, yo lo uso siempre que puedo...
    Saludos!
      Votos: 0 - Link respuesta
     
  • Fecha: 25-11-2015 09:01:30 Si pero no esta disponible para valores escalares :(   Votos: 1 - Link respuesta
     
  • Fecha: 25-11-2015 09:02:03 A menos que uses una librería específica (que la hay). Pero nativamente no los soporta.   Votos: 1 - Link respuesta
     
  • Fecha: 25-11-2015 10:14:49 Aquí comparto mi aproximación al problema basada en lo que ustedes hicieron.
    Personalmente no me gustan los modificadores privados porque limitan la herencia.
    Son dos partes, una interfaz y una clase abstracta. Lo separo así porque me gusta mantener el órden.

    Primero la interfaz
    /**
     * Interface IContrasena
     */
    interface IContrasena
    {
        /**
         * Longitud mínima para una contraseña generada
         */
        const LONGITUD_MINIMA = 8;
        /**
         * Longitud máxima para una contraseña generada
         */
        const LONGITUD_MAXIMA = 30;
        /**
         * Cadena de números permitidos en la contraseña
         * Se omiten el 0 y 1 para no confundir con las letras L minúscula y O mayúscula
         */
        const CADENA_NUMEROS = '23456789';
        /**
         * Cadena de letras permitidas en la contraseña
         * Se omiten las letras L, Ñ y O para no confundirse con los número 0 y 1 y porque la Ñ es una codificación particular
         */
        const CADENA_ALFABETO = 'abcdefghijkmnpqrstuvwxyz';
        /**
         * Cadena de símbolos permitidos en la contraseña
         */
        const CADENA_SIGNOS = '!"\'#$%&()*+,-/[]^_{|}~.;:';
        /**
         * Cantidad mínima de números que debe contener la contraseña para considerarla fuerte
         */
        const NUMEROS_MINIMO = 3;
        /**
         * Cantidad mínima de minúsculas que debe contener la contraseña para considerarla fuerte
         */
        const MINUSCULAS_MINIMO = 2;
        /**
         * Cantidad mínima de mayúsculas que debe contener la contraseña para considerarla fuerte
         */
        const MAYUSCULAS_MINIMO = 2;
        /**
         * Cantidad mínima de símbolos que debe contener la contraseña para considerarla fuerte
         */
        const SIGNOS_MINIMO = 1;
    
        /**
         * Genera la contraseña
         * @return string
         */
        public static function generaContrasena();
    }
    


    Ahora la clase abstracta:
    /**
     * Class AContrasena
     */
    abstract class AContrasena implements IContrasena
    {
        /**
         * Genera la contraseña
         * @return string
         */
        public static function generaContrasena()
        {
            $contrasena = '';
            //Si necesita un mínimo de caracteres numéricos, tómalos
            if (self::NUMEROS_MINIMO > 0) {
                $numeros               = self::CADENA_NUMEROS;
                $longitudCadenaNumeros = strlen($numeros);
                for ($i = 0; $i < self::NUMEROS_MINIMO; $i++) {
                    $indice = mt_rand(0, $longitudCadenaNumeros - 1);
                    $contrasena .= $numeros{$indice};
                }
            }
            //Si necesita un mínimo de caracteres alfabéticos (mayúsculas o minúsculas), tómalos
            if ((self::MINUSCULAS_MINIMO + self::MAYUSCULAS_MINIMO) > 0) {
                $alfabeto               = self::CADENA_ALFABETO;
                $longitudCadenaAlfabeto = strlen($alfabeto);
                if (self::MINUSCULAS_MINIMO > 0) {
                    for ($i = 0; $i < self::MINUSCULAS_MINIMO; $i++) {
                        $indice = mt_rand(0, $longitudCadenaAlfabeto - 1);
                        $contrasena .= strtolower($alfabeto{$indice});
                    }
                }
                if (self::MAYUSCULAS_MINIMO > 0) {
                    for ($i = 0; $i < self::MAYUSCULAS_MINIMO; $i++) {
                        $indice = mt_rand(0, $longitudCadenaAlfabeto - 1);
                        $contrasena .= strtoupper($alfabeto{$indice});
                    }
                }
            }
            //Si necesita un mínimo de caracteres no alfanuméricos, tómalos
            if (self::SIGNOS_MINIMO > 0) {
                $signos               = self::CADENA_SIGNOS;
                $longitudCadenaSignos = strlen($signos);
                for ($i = 0; $i < self::SIGNOS_MINIMO; $i++) {
                    $indice = mt_rand(0, $longitudCadenaSignos - 1);
                    $contrasena .= $signos{$indice};
                }
            }
            $longitudContrasena = strlen($contrasena);
            //Con los requerimientos satisfechos, si la longitud mínima no es la requerida, agrega los caracteres necesarios
            if ($longitudContrasena < self::LONGITUD_MINIMA) {
                $cadenaCaracteresCombinados         = self::CADENA_NUMEROS . strtolower(self::CADENA_ALFABETO) . strtoupper(self::CADENA_ALFABETO) . self::CADENA_SIGNOS;
                $longitudCadenaCaracteresCombinados = strlen($cadenaCaracteresCombinados);
                for ($i = $longitudContrasena; $i < self::LONGITUD_MAXIMA; $i++) {
                    $indice = mt_rand(0, $longitudCadenaCaracteresCombinados - 1);
                    $contrasena .= $cadenaCaracteresCombinados{$indice};
                }
            }
            //Mezcla los caracteres
            $contrasena = str_shuffle($contrasena);
            //Regresa la contraseña
            return $contrasena;
        }
    
        /**
         * Segun el algoritmo de esta función, para ser fuerte la password debe tener:
         * por lo menos X mumero
         * por lo menos X minuscula
         * por lo menos X mayuscula
         * por lo menos X signo
         * y debe cumplir con la longitud minima y maxima, todos estos valores configurados en las constantes de la clase.
         * @param string $password password a ser verificada
         * @return boolean es fuerte =true; no es fuerte = false
         */
        public static function esFuerte($password)
        {
            //arrays usados para comprobar la cantidad de ocurrencias.
            $numeros    = [];
            $minusculas = [];
            $mayusculas = [];
            $signos     = [];
    
            //numeros
            preg_match_all('/[[:digit:]]/', $password, $numeros);
            //minuscula
            preg_match_all('/[[:lower:]]/', $password, $minusculas);
            //mayuscula
            preg_match_all('/[[:upper:]]/', $password, $mayusculas);
            //Marca de puntuacion .''?;
            preg_match_all('/[[:punct:]]/', $password, $signos);
    
            if (strlen($password) >= self::LONGITUD_MINIMA &&
                count($numeros[0]) >= self::NUMEROS_MINIMO &&
                count($minusculas[0]) >= self::MINUSCULAS_MINIMO &&
                count($mayusculas[0]) >= self::MAYUSCULAS_MINIMO &&
                count($signos[0]) >= self::SIGNOS_MINIMO
            ) {
                //Es Fuerte
                return true;
            }
            return false;
        }
    }
    


    Finalmente un script de prueba
    //Usando la clase abstracta
    $contrasena1   = AContrasena::generaContrasena();
    $esFuerte1     = (AContrasena::esFuerte($contrasena1)) ? 'Fuerte' : 'Débil';
    echo <<< TXT
    {$contrasena1} es {$esFuerte1}.
    
    TXT;
    
      Votos: 2 - Link respuesta
     
  • Fecha: 26-11-2015 23:59:39 Bien hoy pude hacerme un tiempo y seguir con esta práctica recien termine de analizar la clase que dejo Fernando.

    Estuve investigando sobre algunos temas las constantes, metodo magico __toString, throw new Exception, operador de Resolución de Ámbito(::).

    Las constantes la usaste por que son valores de tipo permanentes, es decir nunca van a cambiar?

    Paso a decir el algoritmo de la clase, el constructor de la clase tiene por defecto el parametro $password = false, entonces si no le pasamos ningun parametro al constructor va a llamar el metodo generarPassword(); este metodo nos generara aletoriamente un password mediante un bucle for. Cuando termina de iterar el bucle for lo que hace por medio de un condicional es llamar ala funcion esFuerte() con el parametro del password generado este metodo comprobara si cumple con los requisitos correspondientes nos devolvera un booleano. Si es falso lo que va hacer es limpiar la contraseña y generarla nuevo hasta que cumpla con los requsitos.

    Aquí podrias usar las constante que declaraste arriba CADENA_SIGNOS
                        $signos = '!"#$%&\'()*+,-/[]^_{|}~.;:';
                        $signo = substr($signos, rand(0, strlen($signos) - 1), 1);


    Remplazar

                        $signo = substr(self::CADENA_SIGNOS, rand(0, strlen(self::CADENA_SIGNOS) - 1), 1);


    Bien mañana analizare la clase de Ernesto y despues le cuento que quiero hacer bien.

    ¡Saludos y gracias por la ayuda!
      Votos: 2 - Link respuesta
     
Para participar activamente de la comunidad primero debes autenticarte, ingresa al sistema.Iniciar Sesión
 
frjcbbae garagebible.com