¿Cual es la diferencia entre ambas funciones? En el resultado final parece que no hay mucha diferencia ya que  tanto si se llama a session_unset como a session_destroy al finalizar el script el resultado casi siempre es igual: se han borrado las variables de sesión. ¿donde esta entonces la diferencia? En la linea de tiempo. Las información de una sesión esta almacenada, normalmente en el sistema de archivos; al hacer session_start(), esa información se carga en memoria y esta disponible para el resto del script. Al finalizar el script, los cambios en la copia de la información de sesión de la memoria se almacenan de nuevo.

Session_destroy destruye los datos almacenados de la sesión, pero no la copia en memoria, como puedes ver aquí:

session_start();
$_SESSION["foo"] = "bar";
session_destroy();
echo $_SESSION["foo"];

-> devuelve “bar”.

¡ojo! Cuando finalize el script, la copia de los datos de sesión de la memoria no se guardan.

Session_unset borra los datos de sesión la memoria aunque estos todavía están en el almacén el breve tiempo en que termina el script. Al finalizar serán borrados por el gestor de sesiones.

Bien, ahora la pregunta es la siguiente: ¿debemos usar session_destroy seguido de session_unset o al reves?. Veamos un ejemplo:

session_start();
$_SESSION["foo"] = "bar";
session_destroy();
session_unset()
echo $_SESSION["foo"];

-> devuelve “bar”.

¿sorprendente? Lo que ha pasado es que al llamar primero a session_destroy, la copia de memoria queda desvinculada a la sesión, así que el session_unset no tiene efecto alguno (aunque los datos en memoria no sobrevivirán al script).

El orden correcto es este:

session_start();$_SESSION["foo"] = "bar";
session_unset();
session_destroy()
echo $_SESSION["foo"]; // a modo de test..falla.
-> genera un error warning..

En todo caso, haz siempre un session_unset. Si usas un session_destroy por ejemplo en el logout de un usuario registrado, es posible que otras partes de tu script consulten la datos de sesión de memoria, donde el usuario todavía figura como logeado.

Recientemente he empezado a codificar mi código según las guías de estilo PSR-1 y PSR-2 ( ver en http://www.php-fig.org/). Los primeros días cuesta deshacerse de viejos hábitos, pero el esfuerzo vale la pena. Las mejoras son:

  1. Una decisión menos que tomar. ¿hay que poner espacio antes del paréntesis?¿después? . Usa un estándar y dedícate a programar.
  2. Correctores. Existen muchos plugins y herramientas que corrigen o nos avisan cuando de errores de estilo en nuestro código. Así que ahora, no solo tengo un estandár sino que sé que lo estoy usando correctamente.
  3. Algunos errores menos y un código mas claro.
  4. Estas preparado para publicar tu código o integrarte en cualquier equipo.

Cuando empecé a escribir este blog el mundo de desarrollo web esta dominado por “nuestro amigo IE”. Usar CSS avanzado era imposible. El CSS3 era una muy muy lejana promesa. Pero ahora las cosas han cambiado. Hay competencia entre navegadores, y lo que es mas importante: colaboración. Los nuevos estándares se implementan, sin esperara a la aprobación del W3C (que nunca llega).

Una de las novedades que ya puedes utilizar con seguridad son las nuevas unidades relativas: vw y vh, que hacen referencia al tamaño del viewport. vx es un 1% del ancho, y vh es un 1% del alto. La diferencia con el viejo % es notable:

  • 1 vx siempre vale lo mismo en cualquier elemento. Por contra,  %  es relativa contenedor del  elemento actual.
  • Se pueden ‘mezclar’. Por ejemplo, como hacer que un elemento sea un cuadrado con un ancho del 25%. Fácil: width:25vx; height: 25vx. Con % no hay manera ya que poner height:25% no equivale a un 25 del ancho, sino del alto. Un sutil cambio consistente en usar dos unidades vx, y vh frente a un único simbolo %, simplifica enormente el CSS.
  • Permite un mayor control tipográfico de titulares. Basta usar font-size: 5vx y la fuente se adapta a la ventana del navegador.
  • Hacer una presentación web. y dividir cada parte en una ventana es fácil: .parte { height: 100vh}
  • Estirar la página hasta el final:  body { min-height: 100vh}

Resumiendo, una idea genial !!

Secretos en php

diciembre 3, 2014

Cualquier web con usuarios registrados debe contar con la funcionalidad de “¿Olvido su contraseña?”.
Implementar dicha funcionalidad es muy sencillo. Veamos: mandaremos al usuario un email con la url de login, seguida de un código.
Por ejemplo: http://www.example.com/login/1.
Esta dirección daría acceso al usuario 1. Problema: es perfectamente visible. Añadamos por tanto, un poco de md5 para enmarañar la url que quedaria asi:
http://www.example.com/login/c4ca4238a0b923820dcc509a6f75849b
Ya no es visible, pero presenta dos problemas: es vulnerable a ataques por diccionario, y ataques de fortuna (alguien puede probar a introducir http://www.example.com/login/ + md5(123).

Solución: combinamos la id del usuario, un campo del propio del usuario (fecha de alta por ejemplo), con una semilla elegida al azar mas la marca de tiempo.
http://www.example.com/login/ + md5( $id . $f_alta . “jhdjdhhj “. date(“Ymd”));

Esta sencilla clave es única para un usuario con determinda id y fecha de alta, para cierta aplicación y para cierto periodo de tiempo (el día de hoy).

Ahora ya podemos incluir el código de nuestra aplicación de ‘palabras secretas’. Os adjunto un sencillo pseudo-codigo:


const SEED = "uiusiui j"; // cualquier valor escogido al azar. Hazlo único para cada instalación.
const SECRET_VALID ="Ymd"; // notación date de PHP.

function semilla_secreta(){
return SEED . date(SECRET_VALID);
}

function secreto( $datos ) {
return md5(implode($datos) . semilla_secreta());
}

// devuelve la id del usuario al que corresponde el secreto.
function check_secreto ($clave) {
$sql = sprintf("SELECT id FROM usuarios WHERE MD5(Concat(id,alta,'%s'))='%s',
addslashes(semilla_secreta()),
addslashes($clave));
return query_var($sql); // debes implementar esta función.
}

// enviar el secreto a cierto email,
function enviar_secreto($email) {
// buscamos los datos (id y alta) de un usuario con el email dado.
$sql = sprintf("SELECT id,alta FROM usuarios WHERE email='%s', addslashes($email))
$datos = query_registro($sql); // debes implementar esta funcion que debe devolver un array.
if ( $datos ) {
...
$mensaje .= sprintf("click aqui", secreto($datos));
mail($email,"Acceso.", $mensaje);
}
}

PD: para los más paranoicos.
Este login todavía es vulnerable a un ataque de fuerza bruta, pero con una posibilidad muy baja de éxito 1 entre 2**128 para cada petición.

Algo de encriptación

abril 6, 2014

PHP tiene una fabulosa librería de encriptación llamada mcrypt. Si no tienes esta librería instalada en tu servidor, o eres de esos paranoicos que desea ver en todo momento como se generan los mensajes codificados, aquí tienes un sencilla librería. Puedes usarla por ejemplo, para encriptar cookies o pequeños mensajes entre usuarios.

La librería tiene 3 funciones útiles:
libreta_de_un_solo_uso($texto) que genera una clave aleatorio con la misma longitud que el texto. Mas información en la wikipedia.
encripta($texto,$llave) y desencripta($texto,$llave)
Encripta y desencripta un texto.La llave puede ser una frase (cuanto mas larga mejor) o puedes generar una libreta de un solo uso con la primera función.
-Hay una cuarta función, cipher, que es en realidad la que hace el trabajo duro.

Leer el resto de esta entrada »

Unas de las preguntas del test de ejemplo de la certificación ZEND es similar a:

1:function f(){
2: $g= 20;
3: global $g;
4: echo $g;
5:}
6:$g=30;
7:f(); //

Para responderla correctamente tienes que entender que el ámbito de global no es la función entera, sino la que empieza en la linea 3. La variable local creada en la linéa 2, es sobre-escrita la línea 3, como una referencia a $GLOBALS[‘g’]. El resultado es “30”;
Parece fácil, pero ¿cuál es el resultado de?


1:function f(){
2: $g= 20;
3: global $g;
4: unset($g);
5:}
6:$g=30;
7:f();
8:echo $g;

¿un error? ¿20 ?
El resultado es 30, ya que la línea 4, no destruye la variable global $GLOBALS[‘g’] sino $g.
Si dentro de una función quieres destruir una variable global debes poner:

function foo(){
..
unset($GLOBALS['NOMBRE_DE_LA_VARIABLE_A_BORRAR']);
..
}

Y que pasa si dentro de una función creamos y destruimos una variable global

function f(){
global $g;
$g=20;
unset($g);
}
f();
echo $g;

¿el resultado es un error o 20? Global es una construcción del lenguaje que equivaldría mas o menos a:

if ( !isset($GLOBALS['g']) {
$GLOBALS['g']= NULL;
}
$g= &$GLOBALS['g'];

Así que el resultado es, 20.

Si quieres conocer certificarte en PHP, deberías saber:
– empty evalúa una variable y nos dice si esta vacía: es 0, “0”, “” (cadena vacía), null, o un array vacío. (boolean) evalúa expresiones del mismo modo que empty, aunque al revés: devuelve true para cualquier valor que no sea 0,”0″,””, NULL, o un array vacío.
– is_null devuelve true si la expresión es NULL, igual que isset que evalua si la variable es null, o no ha sido inicializada.

Lo interesante:
– empty no es una función. Es un construcción del lenguaje; admite un único parámetro que debe ser una variable, la clave de un array o una propiedad.
– isset tampoco es una función, pero la gente olvida que puede aceptar mas de un parámetro, aunque todos deben ser variables (como empty).
– empty e isset no generan error al evaluar una variable inexistente.
– is_null y (boolean) si generan un error al evaluar una variable inexistente.

Resumiendo:

empty(20); // GENERA ERROR
empty(UNA_CONSTANTE); // GENERA ERROR

isset(E_WARNING) ; // GENERA_ERROR
isset($a,$b[1],$c->p) ; // es valido!!

is_null($a) // GENERA un warning si $a no existe.
(boolean) $a+10; // GENERA un warning si $a no existe.