Menú
Está libre
registro
hogar  /  Problemas/ Protección de ID de sesión en PHP. Envío de artículos

Protección de ID de sesión en PHP. Envío de artículos

La seguridad del sitio web se basa en la gestión de sesiones. Cuando un usuario se conecta a un sitio seguro, proporciona credenciales, generalmente en forma de nombre de usuario y contraseña. El servidor web no tiene idea de qué usuario ya ha iniciado sesión y cómo navega de una página a otra. El mecanismo de sesión permite a los usuarios evitar tener que ingresar una contraseña cada vez que desean realizar una nueva acción o navegar a una nueva página.

Básicamente, la gestión de sesiones garantiza que el usuario que ha iniciado sesión actualmente esté conectado. Desafortunadamente, las sesiones se han convertido en un objetivo obvio para los piratas informáticos, ya que pueden permitir el acceso a un servidor web sin necesidad de autenticación.

Después de autenticar al usuario, el servidor web le proporciona un ID de sesión. Este identificador se almacena en el navegador y se sustituye siempre que se necesita autenticación. Esto evita procesos repetitivos de ingreso de nombre de usuario / contraseña. Todo esto ocurre en segundo plano y no causa ninguna molestia al usuario. Imagínese si ingresa su nombre de usuario y contraseña cada vez que ve nueva pagina!

En este artículo intentaré describir todas las formas que sé de cómo proteger un identificador de sesión en PHP.

Uso de cookies

De forma predeterminada, toda la información de la sesión, incluida la identificación, se pasa en una cookie. Pero este no es siempre el caso. Algunos usuarios desactivan las cookies en sus navegadores. En este caso, el navegador pasará el ID de sesión a la URL.

Aquí la identificación se pasa a forma abierta, a diferencia de una sesión de cookies, donde la información está oculta en el encabezado HTTP. Lo mas de una manera sencilla La protección de esto será la prohibición de transmitir el identificador de sesión a través de Barra de dirección... Puede hacer esto escribiendo lo siguiente en el archivo de configuración .htaccess del servidor Apache:

Php_flag session.use_only_cookies en

Usando cifrado

Si su sitio necesita manejar información confidencial como números de tarjetas de crédito (hola de Sony), debe usar el cifrado SSL3.0 o TSL1.0. Para hacer esto, al configurar la cookie, debe especificar verdadero para el parámetro seguro.

Si almacena la contraseña de la sesión en la variable $ _SESSION (después de todo, es mejor usar sql), entonces no debe almacenarla en texto sin cifrar.

If ($ _SESSION ["contraseña"] == $ userpass) (// código)

El código anterior no es seguro ya que la contraseña se almacena en texto sin formato en una variable de sesión. Use el cifrado md5 en su lugar, algo como esto:

If ($ _SESSION ["md5password"] == md5 ($ userpass)) (// código)

Comprobación del navegador

Para eliminar la posibilidad de usar una sesión desde otro navegador (computadora), debe ingresar una marca para el campo de encabezado HTTP del agente de usuario:

Session_start (); if (isset ($ _ SESSION ["HTTP_USER_AGENT"])) (if ($ _SESSION ["HTTP_USER_AGENT"]! = md5 ($ _ SERVER ["HTTP_USER_AGENT"])) (// código)) else ($ _SESSION [" HTTP_USER_AGENT "] = md5 ($ _ SERVER [" HTTP_USER_AGENT "]);)

Caducidad de la sesión

Limite la duración de la sesión y la duración de las cookies. De forma predeterminada, la sesión expira 1440 segundos. Puede cambiar este valor a través de php.ini y .htaccess. Ejemplo de .htaccess:

# Duración de la sesión en segundos
php_value session.gc_maxlifetime 3600
# Duración de la cookie en segundos
php_value session.cookie_lifetime 3600

Vinculación por dirección IP

En ciertas situaciones (no siempre), debe enlazar por dirección IP. Sobre todo cuando el número de usuarios es limitado y tienen una IP estática. La verificación puede ser contra la lista de direcciones IP permitidas,

Incluir ("ip_list.php"); // $ ip_white_list = array ("admin1" => "111.222.333.444", "admin2" => "555.666.777.888"); if (! empty (array_search ($ _ SERVER ["REMOTE_ADDR"], $ ip_white_list))) (header ("Ubicación: admin.php");) else (echo "ACCESS DENY!";)

O por dirección IP para cada solicitud (solo para IP estáticas):

If (isset ($ _ SESSION ["ip"]) y $ _SESSION ["ip"] == $ _SERVER ["REMOTE_ADDR"]) (header ("Ubicación: admin.php");) else (session_unset (); $ _SESSION ["ip"] = $ _SERVER ["REMOTE_ADDR"];)

Debe tener en cuenta que es imposible evitar por completo la piratería. Solo puede hacer que esta piratería sea lo más difícil posible por cualquier medio conocido. Sin embargo, tampoco debes olvidarte de tus usuarios legales, para no complicarles la vida con dicha protección.

Este artículo fue escrito en 2009 y sigue siendo una de nuestras publicaciones más populares. Si está interesado en aprender más sobre PHP y MySQL, puede que le resulte de gran interés.

NOTA: ¡Este artículo se ha actualizado recientemente para que funcione en PHP 4.2 o posterior!

Recientemente, tuve ocasión de trabajar en un pequeño proyecto con un grupo de personas. Desde el principio, habíamos determinado que ese correo electrónico por sí solo no sería suficiente para mantener a todos al tanto, así que me encargaron de crear un pequeño sitio web para el proyecto. Contendría un tablero de mensajes simple, un lugar donde podríamos cargar documentos y otros archivos para que los use el resto del equipo, e información de contacto de los distintos miembros del equipo.

Para que funcionen muchas de estas funciones, sabía que necesitaría que los usuarios iniciaran sesión antes de acceder a las partes relevantes del sitio. Lo que necesitaba era un sistema que permitiera a los usuarios registrarse para obtener una identificación de usuario para acceder al sitio y luego usar esa identificación de inmediato sin ninguna intervención de mi parte.

En este artículo, proporcionaré una descripción general del sistema que desarrollé, comenzando en la primera mitad con el proceso de registro del usuario. En la segunda mitad, me centraré en el sitio en sí, cómo requiere que los usuarios inicien sesión y luego mantiene ese estado de inicio de sesión durante su visita. Prestaré especial atención al uso de las funciones de administración de sesiones en PHP. Al final, debería tener toda la información que necesita para implementar un sistema similar propio.

A lo largo de este artículo, asumiré que tiene una familiaridad básica con el lenguaje PHP, el uso de formularios para enviar información a un script PHP y cómo se puede usar PHP para interactuar con una base de datos MySQL. Si alguno de estos son conceptos ajenos a usted, debería comenzar por leer mi artículo anterior.

Primera parte: el proceso de registro

El formulario de registro

Un lugar natural para comenzar a construir un sitio que requerirá que los usuarios se registren para acceder es el proceso de registro en sí. Como era de esperar, un formulario simple basado en la Web funcionará. Así es como se verá:

Y aquí está el código de este formulario:




Registro de nuevo usuario



Formulario de registro de nuevo usuario


* indica un campo obligatorio


">























*



*

Dirección de correo electrónico




*

Otras notas












En realidad, ya hay una pieza de código PHP incrustada en este formulario:

">

Observe el atributo de acción. En un patrón de diseño familiar, que he llamado paginas multiusos en artículos anteriores, estamos haciendo que el formulario se envíe a la misma página que contiene el código del formulario. Al observar la variable PHP $ _POST ["submitok"], que se creará con el botón enviar en este formulario (aviso nombre = "submitok" en la etiqueta), nuestro script podrá manejar los envíos del formulario así como la visualización del formulario en sí.

También estamos usando una forma de sintaxis relativamente nueva para imprimir la variable $ _SERVER ["PHP_SELF"]. En caso de que no esté familiarizado con esta sintaxis, permítame aclarar queexpresión?> es funcionalmente idéntico aexpresión; ?>. Por lo tanto, en este caso, podríamos haber escrito lo siguiente:

">

Ahora que tenemos el código para nuestro formulario, podemos pasar a escribir el script de registro completo.

El guión de registro

Si está acostumbrado a escribir sitios web basados ​​en bases de datos, la secuencia de comandos de registro debería parecer bastante sencilla. Primero, necesitamos escribir un par de fragmentos de código que realizarán funciones comunes como conectarse a la base de datos que almacenará los nombres de usuario y las contraseñas del sitio. Estos fragmentos tomarán la forma de funciones PHP almacenadas en archivos de inclusión. Usaremos un archivo de inclusión para albergar funciones relacionadas con la base de datos (db.php) y otro para almacenar funciones más generales (common.php).

Primero, aquí está el código para db.php:

localhost"; $ dbuser =" usuario"; $ dbpass =" contraseña"; function dbConnect ($ db =" ") (global $ dbhost, $ dbuser, $ dbpass; $ dbcnx = @mysql_connect ($ dbhost, $ dbuser, $ dbpass) o morir (" La base de datos del sitio parece estar inactiva. " ); si ($ db! = "" y [correo electrónico protegido] _select_db ($ db)) die ("La base de datos del sitio no está disponible"); return $ dbcnx; )?>

La función dbConnect definida aquí se puede llamar con o sin un argumento, porque le hemos asignado un valor predeterminado ("") al argumento $ db en la declaración de la función. La función comienza conectándose al servidor MySQL usando las variables $ dbhost, $ dbuser y $ dbpass en la parte superior del archivo ( Deberá establecerlos en los valores adecuados para su servidor.), y luego, si se le dio un nombre de base de datos, selecciona esa base de datos. Suponiendo que todo avanza sin errores, se devuelve la referencia a la conexión de la base de datos.

El segundo archivo de inclusión, common.php, también contiene una única función:

Usaremos esta función de error para decirle al usuario cuando ha hecho algo mal. Toma un mensaje de error como argumento y luego lo muestra en un mensaje emergente de JavaScript antes de retroceder a la página anterior. Esta función finaliza la secuencia de comandos que la llama con el comando de salida, por lo que es adecuada para usar cuando algo sale mal en una de sus secuencias de comandos.

Con estos aburridos detalles fuera del camino, ahora puede dirigir su atención al script de registro (signup.php). El script comienza cargando los dos archivos de inclusión que acabamos de escribir:

Esto supone que los archivos en cuestión están disponibles en la ruta de inclusión. Considere, por ejemplo, la siguiente ruta de inclusión, que utilizo en mi archivo php.ini:

Include_path = ".; C: php4pear; d: wwwphpinclude"

En un servidor Unix, podría verse así:

Include_path = ".: / Usr / local / php / lib / php: / home / kyank / phpinclude"

En cualquier caso, puede optar por colocar sus archivos de inclusión en el mismo directorio que los archivos que los utilizan, o colocarlos en un directorio que aparece en la ruta de inclusión. La última opción es más segura para archivos que contienen información confidencial como contraseñas, porque si el soporte de PHP en su servidor web falla alguna vez, la información en los archivos PHP no almacenados debajo del directorio raíz web de su servidor no estará expuesta a miradas indiscretas.

A continuación, el script comprueba la presencia de una variable $ _POST ["submitok"], que indicaría que se ha enviado el formulario de registro. Si no se encuentra la variable, el script muestra el formulario de la sección anterior para que el usuario lo complete:

If (! Isset ($ _ POST ["submitok"])): // ¿Mostrar el formulario de registro de usuario?> Registro de nuevo usuario ...

El resto del script es responsable de procesar el envío del formulario agregando el nuevo usuario a la base de datos, así que hagamos una pausa por un momento y observemos el diseño de la tabla de la base de datos. Aquí está el comando MySQL para crear la tabla:

Mysql> CREATE TABLE usuario (-> ID INT PRIMARY KEY AUTO_INCREMENT, -> userid VARCHAR (100) UNIQUE NOT NULL, -> contraseña CHAR (16) NOT NULL, -> nombre completo VARCHAR (100) NOT NULL, -> email VARCHAR ( 100) NOT NULL, -> notas TEXT);

Como puede ver, la tabla es bastante simple. En su aplicación, es posible que necesite columnas adicionales o incluso varias tablas, pero para los propósitos de este ejemplo, este diseño simple será suficiente.

Entonces, volviendo al script, cuando se detecta la variable $ submit, el script debe prepararse para insertar la nueva entrada en la base de datos usando la función dbConnect de db.php:

sesiones");

Tenga en cuenta que supongo que la base de datos que contiene la tabla de usuarios se llama sesiones. Asegúrese de cambiar esto si está usando un nombre de base de datos diferente.

Lo siguiente que debe hacer el script es verificar que los campos requeridos en el formulario estén completos. Si se encuentra que alguna de las variables correspondientes son cadenas vacías, el script llama a la función de error de common.php para decirle al usuario qué salió mal y volver al formulario:

If ($ _POST ["newid"] == "" o $ _POST ["newname"] == "" o $ _POST ["newemail"] == "") (error ("Uno o más campos obligatorios se dejaron en blanco . \ n "." Por favor, complételos e inténtelo de nuevo. ");)

A continuación, el script debe determinar si el ID seleccionado por el usuario ya está presente en la base de datos, en cuyo caso el usuario deberá seleccionar un nuevo ID. El manejo de esta consulta es algo inusual, ya que obtenemos un solo valor (el recuento de entradas con ID de usuario coincidentes) que prácticamente será 0 o 1. Dado que solo hay un valor, no es necesario recorrer el resultado. colocar. En su lugar, usamos la función mysql_result para obtener el valor de la primera columna (índice 0) de la primera fila (índice 0):

// Verifique si hay un usuario existente con la nueva identificación $ sql = "SELECT COUNT (*) FROM user WHERE userid =" $ _POST ""; $ resultado = mysql_query ($ sql); if (! $ result) (error ("Se produjo un error en la base de datos al procesar su envío". ". \ nSi este error persiste, comuníquese con". " [correo electrónico protegido] . ");) if (@mysql_result ($ result, 0,0)> 0) (error (" Ya existe un usuario con el ID de usuario elegido. \ n "." Intente con otro. ");)

¡No olvide cambiar la dirección de correo electrónico en el mensaje de error!

Con todas las comprobaciones realizadas, solo queda una tarea por completar antes de que la entrada se agregue a la base de datos. Es posible que haya notado que el formulario de registro no contiene un campo para que el usuario ingrese una contraseña. Esto se hizo deliberadamente. En lugar de permitir que el usuario establezca una contraseña por adelantado, un truco común que utilizan muchos sitios es generar automáticamente una contraseña inicial y enviarla a la dirección de correo electrónico ingresada por el usuario.

Este es un método eficaz para verificar que se ingrese una dirección válida; sin embargo, en un sistema práctico, podría terminar con muchas entradas “malas” en la base de datos. Para corregir esto, una solución sería agregar dos columnas a la base de datos del usuario. El primero contendría una marca de tiempo para cuando se creó el usuario. El segundo sería un valor booleano (verdadero / falso) que inicialmente se establecería en falso y se establecería en verdadero la primera vez que el usuario inicie sesión. Luego, podría escribir un script que se ejecutaría una vez a la semana como una tarea automatizada (usando cron, por ejemplo) para eliminar las entradas de los usuarios que se habían registrado hace más de una semana y aún no habían iniciado sesión. En la práctica, se trataría de entradas con direcciones de correo electrónico no válidas, cuyas contraseñas nunca llegaron a sus usuarios.

Entonces, la tarea que debe realizar el script es generar una contraseña para el usuario. Si bien hay muchos métodos para generar cadenas de texto semi-aleatorias para usar como contraseñas, la siguiente frase me ha servido bien:

$ newpass = substr (md5 (tiempo ()), 0.6);

Esto funciona tomando la hora actual y realizando un hash MD5 en ella. Se trata básicamente de una codificación criptográfica unidireccional en una cadena de texto, que luego se corta en 6 caracteres utilizando la función substr. El resultado es una contraseña de 6 caracteres que sería bastante difícil de adivinar.

Finalmente, el script realiza la inserción de la base de datos:

$ sql = "INSERT INTO user SET ID de usuario =" $ _POST ", contraseña = PASSWORD (" $ newpass "), fullname =" $ _POST ", email =" $ _POST ", notes =" $ _POST ""; if (! mysql_query ($ sql)) error ("Ocurrió un error en la base de datos al procesar su envío". ". \ nSi este error persiste, por favor". "contacte [correo electrónico protegido] .");

Una vez más, cambie la dirección de correo electrónico del mensaje por la suya.

El único punto digno de mención en el código anterior es el uso de la función PASSWORD de MySQL para codificar la contraseña en la base de datos. Por supuesto, podría almacenar las contraseñas en texto sin formato, pero los usuarios de un sistema a menudo se sienten más cómodos cuando el administrador no puede leer su contraseña. Aunque esto no agrega mucho en cuanto a seguridad, dado que presumiblemente el administrador tiene acceso completo a la base de datos, los usuarios generalmente aprecian cualquier privacidad que puedan obtener.

Ahora que el usuario se ha registrado en la base de datos, el script debe enviar un mensaje de correo electrónico indicando la contraseña que se le ha asignado. Esto se hace fácilmente usando la función de correo electrónico de PHP:

// Envíe por correo electrónico la nueva contraseña a la persona. $ mensaje = ¡Día "G"! ¡Se ha creado su cuenta personal para el sitio web del proyecto! Para iniciar sesión, diríjase a la siguiente dirección: http://www.example.com/ Su ID de inicio de sesión personal y contraseña son los siguientes: ID de usuario: $ _POST contraseña: $ newpass ¡No está atascado con esta contraseña! Puede cambiarla en cualquier momento después de iniciar sesión. Si tiene algún problema, no dude en ponerse en contacto carne<[correo electrónico protegido] >. -Tu nombre Tú sitio Webmaster "; mail ($ _ POST [" newemail "]," Su contraseña para Su página web", $ mensaje," De: Tu nombre <[correo electrónico protegido] >");

Personalice el mensaje para sus propios fines y asegúrese de incluir su propia dirección de correo electrónico y la URL y el nombre de su sitio cuando corresponda.

Finalmente, el script genera el HTML para la página que el usuario verá al registrarse exitosamente:

?> Registro completo

¡Registro de usuario exitoso!

Su nombre de usuario y contraseña se han enviado por correo electrónico a , la dirección de correo electrónico que acaba de proporcionar en su formulario de registro. Para iniciar sesión, haga clic aquí para regresar a la página de inicio de sesión e ingrese su nueva identificación de usuario personal y contraseña.

Tenga en cuenta que este mensaje dirige al usuario a index.php como la "página de inicio de sesión" del sitio. Esto supone que todo su sitio requerirá registrarse para acceder, con la única excepción de la página de registro en sí. Si solo tiene la intención de proteger un área particular de su sitio, probablemente debería cambiar este mensaje para dirigir al usuario al área protegida por contraseña del sitio. No es necesario que los dirija a una página de inicio de sesión en particular; Como veremos en la Parte 2 de este artículo, cualquier página protegida del sitio solicitará acceso si el usuario aún no ha iniciado sesión.

Con el proceso de registro listo para comenzar, hemos sentado las bases para un sitio web que requiere que los usuarios se registren para acceder. Los usuarios ahora pueden crear una cuenta para ellos mismos y tenerla disponible instantáneamente, sin necesidad de intervención por parte del administrador del sitio. Si genera automáticamente una contraseña inicial para cada nuevo usuario y se la envía por correo electrónico a la dirección que proporcionó durante el registro, puede estar seguro de que la dirección de correo electrónico proporcionada por cada usuario es válida. Esto también abre la puerta a una práctica función de "envíeme un correo electrónico con mi contraseña olvidada" si lo considera apropiado.

Segunda parte: control del acceso

El siguiente paso es construir el sitio en sí, y si bien solicitar al usuario un nombre de usuario y contraseña y hacer coincidir los valores ingresados ​​con una entrada en la base de datos MySQL son procesos bastante sencillos en cuanto a implementación, el principal desafío que se debe enfrentar en el La segunda mitad de este artículo lo hace de tal manera que el usuario solo necesita iniciar sesión una vez durante cualquier visita en particular al sitio. Como mostraré, el soporte integrado para sesiones PHP es un medio excelente para este fin.

Sesiones PHP

Si nunca ha utilizado el soporte para sesiones que se agregó a PHP en la versión 4.0, es posible que esté un poco confuso sobre la cuestión de qué son realmente las sesiones. A pesar del nombre que suena complicado, las sesiones son en realidad un método sencillo para crear variables que se mantienen durante la duración de la estadía del usuario en su sitio. A menos que se configure de otra manera, una sesión de PHP funciona configurando automáticamente una cookie en el navegador del usuario que contiene un ID de sesión, que es una larga cadena de letras y números que sirve para identificar de manera única a ese usuario en su sitio durante la duración de la visita. Luego, el navegador envía esa cookie junto con cada solicitud de una página de su sitio para que PHP pueda usarla para identificar a cuál de las potencialmente muchas sesiones en progreso pertenece la solicitud. Usando un conjunto de archivos temporales almacenados en el servidor web, PHP realiza un seguimiento de las variables que se han registrado en cada sesión y sus valores.

Antes de que pueda seguir adelante y usar las funciones de administración de sesiones en PHP, debe asegurarse de que la sección relevante de su archivo php.ini se haya configurado correctamente. Si está utilizando un servidor que pertenece a su proveedor de alojamiento web, probablemente sea seguro asumir que esto se ha hecho por usted. De lo contrario, abra su archivo php.ini en un editor de texto y busque la sección marcada. Debajo, encontrará veinte opciones que comienzan con la palabra sesión. La mayoría de ellos están bien si se dejan como están, pero aquí hay algunos cruciales que querrá verificar:

Session.save_handler = archivos session.save_path = "C: WINDOWSTEMP" session.use_cookies = 1

session.save_path le dice a PHP dónde crear los archivos temporales usados ​​para rastrear sesiones. Debe estar configurado en un directorio que exista en el sistema, o obtendrá mensajes de error desagradables cuando intente crear una sesión en una de sus páginas. En Unix, / tmp es una opción popular. En Windows, puede usar C: WINDOWSTEMP, o algún otro directorio si lo prefiere (yo uso D: PHPSESSIONS). Una vez realizados estos ajustes, reinicie el software del servidor web para permitir que los cambios surtan efecto.

Ahora está listo para comenzar a trabajar con sesiones PHP. Antes de saltar al script de control de acceso, veamos rápidamente las funciones de administración de sesiones más comunes en PHP. Para decirle a PHP que busque una ID de sesión, o que inicie una nueva sesión si no encuentra ninguna, simplemente llame a session_start. Si se encuentra un ID de sesión existente cuando se llama a esta función, PHP restaura las variables que pertenecen a la sesión.

Session_start ();

Para decirle a PHP que desea que se almacene una variable en particular en la sesión actual para que esté disponible para otros scripts que se ejecutan en la misma sesión, simplemente establezca una variable en la matriz $ _SESSION. Por ejemplo, lo siguiente almacenará la variable llamada $ _SESSION ["pwd"] en la sesión actual:

$ _SESSION ["pwd"] = valor;

Para eliminar una variable de la sesión actual, simplemente use la función unset de PHP:

Sin definir ($ _ SESSION ["pwd"]);

Finalmente, si desea finalizar la sesión actual, eliminando todas las variables registradas en el proceso, puede vaciar la matriz $ _SESSION y luego usar session_destroy:

$ _SESSION = array (); session_destroy ();

Para obtener información más detallada sobre estas y otras funciones de gestión de sesiones en PHP, consulte la sección correspondiente del Manual de PHP: Funciones de gestión de sesiones.

El script de control de acceso

Para cada página que desee proteger con este esquema de control de acceso (para que solo los usuarios registrados puedan verla), su secuencia de comandos debe seguir el procedimiento descrito en el diagrama de flujo a continuación.

La primera vez que se solicita una página protegida, el usuario aún no habrá ingresado sus datos de inicio de sesión. La secuencia de comandos detecta esto y solicita al usuario un nombre de usuario y una contraseña con un formulario de inicio de sesión en lugar de mostrar la página solicitada. Cuando se envía ese formulario, la página se vuelve a cargar, esta vez con un nombre de usuario y contraseña especificados. El script ve que se han especificado los detalles de inicio de sesión y los registra como variables de sesión para que permanezcan disponibles durante el resto de la visita del usuario. Finalmente, el script verifica la base de datos para asegurarse de que la combinación de nombre de usuario / contraseña sea válida. Si es así, se muestra la página solicitada. De lo contrario, se muestra un mensaje de "acceso denegado" con un enlace que invita al usuario a intentar iniciar sesión nuevamente.

Dado que este procedimiento será idéntico para todas las páginas protegidas, tiene sentido implementarlo como un archivo de inclusión común. Esto le permitirá proteger una página simplemente agregando la siguiente línea en la parte superior del archivo:

Con el objetivo ahora claro, lo guiaré a través del código para accesscontrol.php. Comience por incluir sus dos prácticos archivos de inclusión:

Yo uso include_once aquí en lugar de include en caso de que el archivo principal también use estos archivos de inclusión. Si common.php se incluyera dos veces, por ejemplo, PHP emitiría una advertencia de que la función de error se había declarado dos veces.

A continuación, llamo session_start para comenzar una nueva sesión (si esta es la primera página de la visita del usuario) o para cargar las variables que pertenecen a la sesión actual del usuario.

Session_start ();

En este punto, los detalles de inicio de sesión del usuario deben estar disponibles ya sea que se hayan enviado desde un formulario de inicio de sesión (en la matriz $ _POST) o almacenados en la sesión del usuario (en la matriz $ _SESSION). Entonces, como primera orden del día, la secuencia de comandos debe extraer las credenciales de inicio de sesión de la matriz $ _POST o $ _SESSION:

$ uid = isset ($ _ POST ["uid"])? $ _POST ["uid"]: $ _SESSION ["uid"]; $ pwd = isset ($ _ POST ["pwd"])? $ _POST ["pwd"]: $ _SESSION ["pwd"];

Estas dos líneas utilizan una sintaxis útil (aunque confusa) llamada operador ternario, que toma esta forma:

¿Condición? valor_si_verdadero: valor_si_falso

Si condición es cierto, la expresión será igual a valor_si_verdadero... Si no, será igual valor_si_falso.

Entonces, si compara esto con la primera línea anterior, verá que si hay un valor "uid" en la matriz $ _POST (isset ($ _ POST ["uid"])), $ uid se establecerá en el valor de $ _POST ["uid"]. De lo contrario, se establecerá en el valor de $ _SESSION ["uid"]. Lo mismo sucede para crear $ pwd a partir de la matriz $ _POST o $ _SESSION.

Si realmente no se siente cómodo con el operador ternario, aquí le mostramos cómo puede hacer lo mismo con las declaraciones if:

If (isset ($ _ POST ["uid"]) ($ uid = $ _POST ["uid"];) else ($ uid = $ _SESSION ["uid"];) if (isset ($ _ POST ["pwd "]) ($ pwd = $ _POST [" pwd "];) else ($ pwd = $ _SESSION [" pwd "];)

Como puede ver, el operador ternario puede ahorrar mucho tiempo de escritura si puede entenderlo.

Ahora, en esta etapa, el único caso en el que la identificación y la contraseña del usuario no estarían disponibles es si no se hubieran ingresado durante esta visita al sitio.

Si (! Isset ($ uid)) (?> Inicie sesión para acceder

Necesario iniciar sesión

Debe iniciar sesión para acceder a esta área del sitio. Si no es un usuario registrado, haga clic aquí para registrarse y obtener acceso instantáneo.

"> ID de usuario:
Contraseña:

Cuando se envíe el formulario de inicio de sesión anterior, la página se volverá a cargar, esta vez con las variables $ uid y $ pwd configuradas en el ID y la contraseña del usuario. El siguiente paso de acuerdo con el diagrama de flujo anterior es registrarlos como variables de sesión, asegurándose de que estén disponibles para todas las demás páginas protegidas que el usuario ve durante esta visita. Tenga en cuenta que, en este punto, la secuencia de comandos aún no sabe si el ID de usuario y la contraseña que ingresó son válidos o no. Explicaré por qué el guión hace las cosas en este orden en breve.

$ _SESSION ["uid"] = $ uid; $ _SESSION ["pwd"] = $ pwd;

Para averiguar si el ID de usuario y la contraseña son válidos, el script busca en la base de datos entradas coincidentes. En la consulta SELECT, he codificado la variable $ pwd usando la función PASSWORD de MySQL para compararla con la contraseña almacenada, que también está codificada. Asegúrese de usar su propio nombre de base de datos aquí (he llamado sesiones mías) y su propia dirección de correo electrónico en el mensaje de error.

DbConnect (" sesiones"); $ sql =" SELECT * FROM user WHERE userid = "$ uid" AND password = PASSWORD ("$ pwd") "; $ result = mysql_query ($ sql); if (! $ result) (error (" A Se produjo un error en la base de datos mientras verificaba sus datos de inicio de sesión ".". \ nSi el error persiste, comuníquese con "." [correo electrónico protegido] ."); }

Si no se encuentran filas coincidentes en la base de datos, los detalles de inicio de sesión proporcionados son incorrectos. El script verifica esto usando la función mysql_num_rows y muestra un mensaje que niega el acceso al sitio e invita al usuario a intentar iniciar sesión nuevamente. Para que esto sea posible, el script también anula el registro de las dos variables de sesión ($ _SESSION ["uid"] y $ _SESSION ["pwd"]) de modo que la próxima vez que se ejecute el script mostrará el formulario de inicio de sesión. Dado que las variables se registraron anteriormente en la secuencia de comandos antes de verificar su validez, la secuencia de comandos no necesita verificar si están registradas antes de intentar anular su registro.

If (mysql_num_rows ($ resultado) == 0) (unset ($ _ SESSION ["uid"]); unset ($ _ SESSION ["pwd"]);?> Acceso denegado

Acceso denegado

Su ID de usuario o contraseña es incorrecta o no es un usuario registrado en este sitio. Para intentar iniciar sesión nuevamente, haga clic en "> aquí. Para registrarse para el acceso instantáneo, haga clic aquí.

Ahora que los detalles de inicio de sesión se almacenaron como variables de sesión y se verificó su validez, el script puede otorgar acceso de manera segura a la página solicitada. Lo último que hago antes de finalizar accesscontrol.php y devolver el control a la página protegida es tomar el nombre completo del usuario, que está disponible en el conjunto de resultados de MySQL generado anteriormente. No es necesario que se registre como una variable de sesión, ya que será recuperada nuevamente por cada página protegida usando los valores "uid" y "pwd" almacenados en la sesión.

$ username = mysql_result ($ resultado, 0, "nombre completo"); ?>

¡Eso completa accesscontrol.php, y con él el sistema de control de acceso! Para reiterar, cualquier página ahora puede restringirse al acceso solo para miembros simplemente incluyendo accesscontrol.php en la parte superior del script. A continuación, se muestra un ejemplo muy simple que utiliza la variable $ username para mostrar el nombre del usuario:

Página solo para miembros

Bienvenido,! Ha ingresado a un área del sitio solo para miembros. ¿No te sientes especial?

Envolver

En este punto, le he proporcionado todas las características de un sistema de control de acceso simple. Aquí está en un archivo ZIP para que descargue una obra de teatro. Todos los lugares donde necesita modificar cosas para usar en su propio servidor se indican en negrita en este articulo.

Sin embargo, aún quedan algunas cosas por agregar:

  • Una página donde los usuarios pueden cambiar su contraseña, su dirección de correo electrónico y cualquier otra opción e información que desee almacenar en sus perfiles de usuario. Obviamente, esta será una página solo para miembros, y la variable $ uid debería ser útil para determinar qué registro actualizar en la base de datos.
  • Una función de "envíeme mi contraseña" que permitirá al usuario haber olvidado los datos de inicio de sesión que se le enviaron por correo electrónico. Por supuesto, no puede almacenar las contraseñas cifradas con la función PASSWORD de MySQL si desea implementar esta función (ya que PASSWORD es una operación unidireccional que no se puede revertir). Si desea mayor seguridad, puede implementar uno de esos esquemas en los que el usuario elige una pregunta durante el registro que debe responder para recuperar una contraseña olvidada.
  • Acceso solo para miembros a archivos que no son HTML. Dado que PHP es igualmente capaz de enviar HTML y información binaria, puede crear un script de paso a través que solo recupere el archivo solicitado si se encuentra una combinación correcta $ uid / $ pwd en la sesión actual.

Estas son solo algunas ideas para que comiences, estoy seguro de que puedes pensar en algunas más interesantes. Los dejaré para que los completes como un ejercicio, pero si te quedas atascado, ¡no dudes en pasar por el para pedir ayuda!

Si disfrutaste leyendo esta publicación, te encantará Learnable; el lugar para aprender nuevas habilidades y técnicas de los maestros. Los miembros obtienen acceso instantáneo a todos los libros electrónicos y cursos interactivos en línea de SitePoint, como PHP y MySQL Web Development for Beginners.

Kevin Yank es un consumado desarrollador web, orador, capacitador y autor de Cree su propio sitio web basado en bases de datos usando PHP y MySQL y coautor de Simply JavaScript y ¡Todo lo que sabe sobre CSS es incorrecto! A Kevin le encanta compartir su gran cantidad de conocimientos y no se limitó a los libros, también es el instructor del curso de 3 cursos en línea sobre desarrollo web. Actualmente, Kevin es el director de ingeniería de front-end en Culture Amp.

Sesiones en PHP o cómo se guardan los datos sobre un usuario o cliente que ha ingresado al sitio al navegar entre las páginas del sitio sin mucha dificultad. La lección es muy importante. Relevante para la creación del 95% de los sitios.

¿Qué es sesión en php?

Las sesiones se utilizan para almacenar información sobre datos temporales (por ejemplo, que el usuario ha ingresado al sitio) cuando navega entre páginas del mismo sitio. Cuando se utilizan sesiones, los datos se guardan en archivos temporales en el servidor.
La mayoría de las veces, las sesiones (y las cookies, sin embargo, también) se utilizan al crear tiendas en línea, foros, tableros de mensajes, redes sociales, blogs y otros recursos. La conveniencia del sistema de sesión es almacenar la información temporal del usuario / comprador que inició sesión, cuyos datos están en acceso rapido tiempo específico... La sesión tiene una fecha de vencimiento natural, hasta que se cierra el navegador. Si cierra solo la página, cuando abra el sitio, los datos sobre el usuario / comprador seguirán estando disponibles.

Lógica de la sesión

La sesión (o sesión) es una especie de almacenamiento temporal de datos. Te advierto de inmediato que vale la pena guardar una pequeña cantidad de datos. Por ejemplo, el nombre de usuario y la contraseña del usuario que inicia sesión o su número de serie en la base de datos.

Ejemplo de trabajo
1. El usuario ingresa un nombre de usuario y contraseña e ingresa al sitio
2. Los datos con nombre de usuario y contraseña se guardan en la sesión de una de las páginas del sitio:

Expediente index.php

Session_start (); // cada archivo en el que desee utilizar datos de sesión debe contener el comando "iniciar sesión" al principio del código

$ login = "admin";
$ contraseña = "pase";
$ _SESSION ["inicio de sesión"] = $ inicio de sesión; // guarda la variable que contiene el inicio de sesión
$ _SESSION ["contraseña"] = $ contraseña; // guarda la variable que contiene la contraseña

3. Cuando vaya a otra página del sitio, estos datos también estarán disponibles:

Expediente ejemplo.php(o cualquier otra página)

Echo "Su inicio de sesión". $ _ SESSION ["inicio de sesión"]; // mostrará "Su nombre de usuario es administrador", ¡aunque no registramos datos en esta página!
¡Mira, es simple!

4. Si desea borrar los datos de la sesión, es suficiente:

Expediente ejemplo.php

Session_start (); // "iniciar la sesión" de nuevo

No establecido ($ _ SESSION ["iniciar sesión"]); // así es como se eliminó el registro o se "destruyó" la variable
echo "Su inicio de sesión". $ _ SESSION ["inicio de sesión"]; // mostrará "Su inicio de sesión". Como lo destruimos en la última línea, tampoco hay datos.

Session_destroy (); // destruye la sesión. Todos los datos, incluido $ _SESSION ["contraseña"], desaparecieron. Cuando se solicite, se mostrará un error
En general, dicha transferencia es similar a Método POST, pero ya no tiene que escribir mucho código innecesario y todos los datos transferidos de una página a otra se almacenan en archivos temporales en el servidor. Nuevamente, las sesiones deben contener pequeñas cantidades de datos, por lo que son adecuadas para almacenar nombre de usuario / contraseña, carrito de compras y otros volúmenes pequeños.

Pasando un valor o matriz usando una sesión PHP

En una sesión, puede escribir no solo una cadena, sino también una matriz de datos. Simplemente no exagere con el tamaño de la matriz, ya que todo esto afectará el rendimiento y el espacio ocupado en el servidor.

Usamos de nuevo un cierto página de inicio index.php

Session_start ();

$ r = matriz ("uno", "dos", "tres");

$ _SESSION ["arr"] = $ r;

A la página donde se muestra todo
Guardamos los datos en la sesión y seguimos el enlace a otra página, donde mostraremos todos los datos.

Destinatario del archivo, página test.php donde abrimos la matriz

Session_start ();
print_r ($ _ SESSION ["arr"]);
// saldrá
/*
Formación
=> uno
=> dos
=> tres
*/
?>
Es posible que desee repasar un tutorial sobre. En general, todo debe quedar claro.

Otras funciones para trabajar con sesiones

session_unregister (cadena)- la sesión olvida el valor de la variable global especificada;
session_destroy ()- la sesión se destruye (por ejemplo, si el usuario ha abandonado el sistema presionando el botón de cierre de sesión);
session_set_cookie_params (int duración [, ruta de la cadena [, dominio de la cadena]])- al usar esta función, puede establecer cuánto tiempo durará la sesión configurando unix_timestamp, que determina la hora de la muerte de la sesión.

Lista de funciones para trabajar con sesiones (sesión) en php
session_cache_expire: devuelve el vencimiento del caché actual
session_cache_limiter - Obtiene y / o establece el límite de caché actual
session_commit - un alias para session_write_close ()
session_decode: decodifica los datos de la sesión de la cadena
session_destroy: destruye todos los datos registrados para la sesión
session_encode: cifra los datos de la sesión actual como una cadena
session_get_cookie_params - Obtener parámetros de cookies de sesión
session_id: obtiene y / o establece la identificación de la sesión actual
session_is_registered: determina si una variable está registrada en la sesión
session_module_name: obtenga y / o instale el módulo para la sesión actual
session_name - obtiene y / o establece el nombre de la sesión actual
session_regenerate_id: modifica la identificación de la sesión actual con la recién generada
session_register: registra una o más variables para la sesión actual
session_save_path: obtiene y / o establece la ruta para guardar la sesión actual
session_set_cookie_params - establece los parámetros de la cookie de sesión
session_set_save_handler - establece funciones de almacenamiento de sesión a nivel de usuario
session_start - inicializa los datos de la sesión
session_unregister: anula el registro de una variable de la sesión actual
session_unset: libera todas las variables de sesión
session_write_close: escribe los datos de la sesión y el final de la sesión

Ejemplos de sesiones

El contador de páginas vistas durante la sesión. Un ejemplo ilustrativo de trabajo. Sin embargo, después de cerrar el navegador, la cuenta regresiva comenzará de nuevo.

Contador de visitas a una página en una sesión

// Un ejemplo simple de uso de sesiones sin cookies.
session_name ("prueba");
session_start ();
$ _SESSION ["count"] = @ $ _ SESSION ["count"] + 1;
?>

Encimera


En la sesión actual de trabajo con el navegador, ha abierto esta página.
tiempo (s).
Cierre su navegador para restablecer este contador.
¡Haga clic aquí para actualizar la página!
Con cada transición, el contador aumentará en 1)

¡Gracias por la atención! ¡Buena suerte en tus esfuerzos!

Hola querida comunidad.

En primer lugar, quiero agradecerles por un recurso muy útil. Más de una vez he encontrado aquí muchas ideas interesantes y consejos prácticos.

El propósito de este artículo es resaltar las trampas de usar sesiones en PHP. Por supuesto, hay documentación PHP y un montón de ejemplos, y este artículo no pretende ser guía completa... Está diseñado para revelar algunos de los matices de trabajar con sesiones y proteger a los desarrolladores de perder tiempo innecesariamente.

El caso de uso más común para las sesiones es, por supuesto, la autorización del usuario. Comencemos con la implementación más básica para que podamos evolucionarla constantemente a medida que surgen nuevos desafíos.

(Para ahorrar espacio y tiempo, nos limitaremos en los ejemplos solo a las funciones de trabajar con sesiones, en lugar de construir una aplicación de prueba completa aquí con una hermosa jerarquía de clases, manejo exhaustivo de errores y otras cosas correctas).

Función startSession () (// Si la sesión ya se ha iniciado, detenga la ejecución y devuelva TRUE // (el parámetro session.auto_start en el archivo de configuración php.ini debe estar deshabilitado - el valor predeterminado) if (session_id ()) return true; else return session_start (); // Nota: Antes de la versión 5.3.0, la función session_start () devolvía TRUE incluso en caso de error. // Si está utilizando una versión anterior a 5.3.0, realice una validación adicional session_id ( ) // después de llamar a session_start ()) function destroySession () (if (session_id ()) (// Si hay sesión activa, eliminar las cookies de sesión, setcookie (session_name (), session_id (), time () - 60 * 60 * 24); // y destruye la sesión session_unset (); session_destroy (); ))

Nota: Se supone que el lector tiene un conocimiento básico de las sesiones de PHP, por lo que no cubriremos cómo funcionan las funciones session_start () y session_destroy (). Las tareas de maquetación del formulario de inicio de sesión y autenticación de usuarios no están relacionadas con el tema del artículo, por lo que también las omitiremos. Permítanme recordarles que para identificar al usuario en cada solicitud posterior, en el momento del inicio de sesión exitoso, debemos guardar el ID de usuario en una variable de sesión (con el nombre de usuario, por ejemplo), que estará disponible en todos los siguientes solicitudes dentro de la vida útil de la sesión. También es necesario implementar el procesamiento del resultado de nuestra función startSession (). Si la función devuelve FALSE, muestre el formulario de inicio de sesión en el navegador. Si la función devuelve VERDADERO y la variable de sesión que contiene el identificador del usuario autorizado (en nuestro caso, el ID de usuario) existe, muestre la página del usuario autorizado (para obtener más detalles sobre el manejo de errores, consulte la adición de 2013-06- 07 en el apartado de variables de sesión).

Hasta ahora todo ESTÁ CLARO. Las preguntas comienzan cuando es necesario implementar el control de la inactividad del usuario (tiempo de espera de la sesión), permitir que varios usuarios trabajen simultáneamente en un navegador y también proteger las sesiones del uso no autorizado. Esto será discutido abajo.

Controlar la inactividad del usuario con herramientas PHP integradas

La primera pregunta que suele surgir entre los desarrolladores de todo tipo de consolas para los usuarios es el final automático de la sesión en caso de inactividad por parte del usuario. No podría ser más fácil hacer esto con las capacidades integradas de PHP. (Esta opción no es muy confiable y flexible, pero consideremos que está completa).

Función startSession () (// Tiempo de espera de inactividad del usuario (en segundos) $ sessionLifetime = 300; if (session_id ()) return true; // Establecer la duración de la cookie ini_set ("session.cookie_lifetime", $ sessionLifetime); // Si el Se establece el tiempo de espera de inactividad del usuario, establezca la duración de la sesión en el servidor // Nota: Para un servidor de producción, se recomienda preestablecer estos parámetros en el archivo php.ini if ​​($ sessionLifetime) ini_set ("session.gc_maxlifetime", $ sessionLifetime ); if (session_start ()) (setcookie (session_name (), session_id (), time () + $ sessionLifetime); devuelve verdadero;) de lo contrario devuelve falso;)

Pocas explicaciones. Como sabe, PHP determina qué sesión comenzar por el nombre de la cookie pasada por el navegador en el encabezado de la solicitud. El navegador, a su vez, recibe esta cookie del servidor, donde la función session_start () la coloca. Si la cookie ha caducado en el navegador, no se transmitirá en la solicitud, lo que significa que PHP no podrá determinar qué sesión iniciar y considerará esto como la creación de una nueva sesión. Parámetro Configuración de PHP session.gc_maxlifetime, que se establece en nuestro tiempo de espera de inactividad del usuario, establece la vida útil de la sesión PHP y es monitoreada por el servidor. El control de duración de la sesión funciona de la siguiente manera (aquí consideramos un ejemplo de almacenamiento de sesión en archivos temporales como la versión más común y predeterminada en PHP).

En el momento de crear una nueva sesión, se crea un archivo llamado sess_ en el directorio establecido como directorio para almacenar sesiones en el parámetro de configuración de PHP session.save_path. , dónde - identificador de sesión. Además, en cada solicitud, en el momento de iniciar una sesión existente, PHP actualiza la hora de modificación de este archivo. Así, en cada solicitud subsiguiente PHP, por la diferencia entre la hora actual y la hora de la última modificación del archivo de sesión, puede determinar si la sesión está activa, o su vida ya ha expirado. (El mecanismo para eliminar archivos de sesión antiguos se analiza con más detalle en la siguiente sección).

Nota: Cabe señalar aquí que el parámetro session.gc_maxlifetime afecta a todas las sesiones dentro de un servidor (más precisamente, dentro de un proceso PHP principal). En la práctica, esto significa que si varios sitios se están ejecutando en el servidor, y cada uno de ellos tiene su propio tiempo de espera de inactividad de los usuarios, la configuración de este parámetro en uno de los sitios conducirá a su instalación también en otros sitios. Lo mismo ocurre con el alojamiento compartido. Para evitar esta situación, se utilizan directorios de sesión separados para cada sitio dentro de un servidor. La configuración de la ruta al directorio de la sesión se realiza mediante el parámetro session.save_path en el archivo de configuración php.ini, o llamando a la función ini_set (). Después de eso, las sesiones de cada sitio se almacenarán en directorios separados, y el parámetro session.gc_maxlifetime, establecido en uno de los sitios, será válido solo para su sesión. No consideraremos este caso en detalle, sobre todo porque tenemos en stock una opción más flexible para controlar la inactividad del usuario.

Controlar la inactividad del usuario mediante variables de sesión

Parecería que la opción anterior, a pesar de su sencillez (solo un par de líneas adicionales de código), da todo lo que necesitamos. Pero, ¿qué pasa si no todas las solicitudes pueden considerarse como resultado de la actividad del usuario? Por ejemplo, la página tiene un temporizador que periódicamente realiza una solicitud AJAX para recibir actualizaciones del servidor. Dicha solicitud no puede considerarse actividad del usuario, lo que significa que la extensión automática de la vida útil de la sesión no es correcta en este caso. Pero sabemos que PHP actualiza la hora de modificación del archivo de sesión automáticamente con cada llamada a la función session_start (), lo que significa que cualquier solicitud conducirá a una extensión de la vida útil de la sesión, y el tiempo de espera de inactividad del usuario nunca ocurrirá. Además, la última nota de la sección anterior sobre las complejidades del parámetro session.gc_maxlifetime puede parecer demasiado confusa y complicada de implementar.

Para resolver este problema, abandonaremos el uso de mecanismos PHP integrados e introduciremos varias variables de sesión nuevas que nos permitirán controlar el tiempo de inactividad del usuario por nuestra cuenta.

Función startSession ($ isUserActivity = true) ($ sessionLifetime = 300; if (session_id ()) return true; // Establecer la duración de la cookie hasta que se cierre el navegador (controlaremos todo en el lado del servidor) ini_set ("session.cookie_lifetime ", 0); if (! Session_start ()) return false; $ t = time (); if ($ sessionLifetime) (// Si se establece el tiempo de espera de inactividad del usuario, // verifique el tiempo transcurrido desde la última actividad del usuario / / (hora de la última solicitud cuando se actualizó la variable de sesión de última actividad) if (isset ($ _ SESSION ["lastactivity"]) && $ t - $ _ SESSION ["lastactivity"]> = $ sessionLifetime) (// Si el tiempo transcurrido desde la última actividad del usuario / / más tiempo de espera de inactividad, entonces la sesión ha expirado y necesita finalizar la sesión destroySession (); return false;) else (// Si el tiempo de espera aún no ha ocurrido, // y si la solicitud se produjo como resultado de la actividad del usuario, // actualice la última variable de actividad con el valor de la vr actual emeny, // extendiendo así el tiempo de la sesión en otros segundos sessionLifetime if ($ isUserActivity) $ _SESSION ["lastactivity"] = $ t; )) devuelve verdadero; )

Resumamos. En cada solicitud, comprobamos si se ha alcanzado el timeout desde el momento de la última actividad del usuario hasta el momento actual, y si se alcanza, destruimos la sesión e interrumpimos la ejecución de la función, devolviendo FALSE. Si no se ha alcanzado el tiempo de espera y el parámetro $ isUserActivity con el valor TRUE se pasa a la función, actualizamos la hora de la última actividad del usuario. Todo lo que tenemos que hacer es determinar en el script de llamada si la solicitud es el resultado de la actividad del usuario, y si no, llamar a la función startSession con el valor del parámetro $ isUserActivity igual a FALSE.

Actualización a 07/06/2013
Procesando el resultado de la función sessionStart ()

En los comentarios, notamos que devolver FALSE no da una comprensión completa de la causa del error, y esto es absolutamente cierto. No publiqué el manejo detallado de errores aquí (el volumen del artículo no es pequeño de todos modos), ya que esto no se relaciona directamente con el tema del artículo. Pero dados los comentarios, aclararé.

Como puede ver, la función sessionStart puede devolver FALSE en dos casos. O la sesión no pudo iniciarse debido a algunos errores internos del servidor (por ejemplo, configuraciones de sesión incorrectas en php.ini), o la sesión ha expirado. En el primer caso, tenemos que redirigir al usuario a una página con un error de que hay problemas en el servidor, y una forma de contactar con el soporte. En el segundo caso, debemos transferir al usuario al formulario de inicio de sesión y mostrar en él el mensaje correspondiente de que la sesión ha caducado. Para hacer esto, necesitamos ingresar códigos de error y devolver el código correspondiente en lugar de FALSE, y verificarlo en el método de llamada y actuar en consecuencia.

Ahora, incluso si la sesión en el servidor todavía existe, se destruirá la primera vez que se acceda a ella, si el tiempo de espera de inactividad del usuario ha expirado. Y esto sucederá independientemente de la duración de la sesión establecida en la configuración global de PHP.

Nota:¿Qué sucede si el navegador se cierra y la cookie del nombre de la sesión se destruye automáticamente? La solicitud al servidor la próxima vez que se abra el navegador no contendrá cookies de sesión y el servidor no podrá abrir una sesión y verificar el tiempo de espera de inactividad del usuario. Para nosotros, esto equivale a crear una nueva sesión y no afecta la funcionalidad ni la seguridad de ninguna manera. Pero surge una pregunta justa: ¿quién destruirá la sesión anterior, si la hemos destruido hasta ahora después de que expire el tiempo de espera? ¿O se quedará colgado en el directorio de la sesión para siempre? Para limpiar sesiones antiguas, PHP tiene un mecanismo llamado recolección de basura. Comienza en el momento de la siguiente solicitud al servidor y limpia todas las sesiones antiguas según la fecha de la última modificación de los archivos de sesión. Pero el mecanismo de recolección de basura no se inicia en cada solicitud al servidor. La frecuencia (más precisamente, la probabilidad) de lanzamiento está determinada por dos parámetros de configuración session.gc_probability y session.gc_divisor. El resultado de dividir el primer parámetro por el segundo es la probabilidad de iniciar el mecanismo de recolección de basura. Por lo tanto, para que el mecanismo de limpieza de la sesión se inicie en cada solicitud al servidor, estos parámetros deben establecerse en valores iguales, por ejemplo, "1". Este enfoque asegura que el directorio de la sesión esté limpio, pero obviamente es demasiado para el servidor. Por lo tanto, en los sistemas de producción, session.gc_divisor se establece en 1000 de forma predeterminada, lo que significa que el motor de recolección de basura se iniciará con una probabilidad de 1/1000. Si experimenta con estas configuraciones en su archivo php.ini, notará que en el caso anterior, cuando el navegador se cierra y borra todas sus cookies, todavía hay sesiones antiguas en el directorio de sesiones por un tiempo. Pero eso no debería preocuparte, porque como ya se mencionó, esto no afecta de ninguna manera la seguridad de nuestro mecanismo.

Actualización a 07/06/2013

Evitar que los scripts se cuelguen debido al bloqueo del archivo de sesión

En los comentarios, plantearon el problema de la congelación de scripts que se ejecutan simultáneamente debido al bloqueo del archivo de sesión (como la opción más brillante: encuesta larga).

Para empezar, observo que este problema no depende directamente de la carga del servidor o del número de usuarios. Por supuesto, cuantas más solicitudes, más lento se ejecutan los scripts. Pero esta es una dependencia indirecta. El problema aparece solo dentro de una sesión, cuando el servidor recibe varias solicitudes en nombre de un usuario (por ejemplo, una de ellas es una encuesta larga y el resto son solicitudes ordinarias). Cada solicitud intenta acceder al mismo archivo de sesión, y si la solicitud anterior no desbloqueó el archivo, la siguiente quedará pendiente.

Para mantener el bloqueo de archivos de sesión al mínimo, se recomienda encarecidamente cerrar la sesión llamando a la función session_write_close () inmediatamente después de que se hayan realizado todas las acciones con variables de sesión. En la práctica, esto significa que no debe almacenar todo en las variables de sesión y hacer referencia a ellas durante la ejecución del script. Y si es necesario almacenar algunos datos de trabajo en variables de sesión, léalos inmediatamente al inicio de la sesión, guárdelos en variables locales para su uso posterior y cierre la sesión (es decir, cerrar la sesión usando la función session_write_close, y no destruir usando session_destroy).

En nuestro ejemplo, esto significa que inmediatamente después de abrir una sesión, verificando su vida útil y la existencia de un usuario autorizado, debemos leer y guardar todas las variables de sesión adicionales necesarias para la aplicación (si las hay), luego cerrar la sesión usando session_write_close ( ) llamar y continuar la ejecución de un script, ya sea una encuesta larga o una solicitud regular.

Proteger las sesiones del uso no autorizado

Imaginemos una situación. Uno de sus usuarios engancha un troyano que roba la cookie del navegador (en la que se almacena nuestra sesión) y la envía al correo electrónico especificado. El atacante recibe una cookie y la usa para falsificar una solicitud en nombre de nuestro usuario autorizado. El servidor acepta y procesa con éxito esta solicitud como si proviniera de un usuario autorizado. Si no se implementa una verificación adicional de la dirección IP, dicho ataque conducirá a un pirateo exitoso de la cuenta del usuario con todas las consecuencias consiguientes.

¿Por qué es esto posible? Obviamente, debido a que el nombre y el identificador de sesión son siempre los mismos durante toda la vida útil de la sesión, y si recibe estos datos, puede enviar solicitudes libremente en nombre de otro usuario (naturalmente, durante la vida útil de esta sesión). Quizás este no sea el tipo de ataque más común, pero en teoría todo parece bastante factible, especialmente considerando que un troyano de este tipo ni siquiera necesita derechos de administrador para robar las cookies del navegador del usuario.

¿Cómo defenderse de ataques de este tipo? Nuevamente, obviamente limitando la vida útil del identificador de sesión y cambiando periódicamente el identificador dentro de una sesión. También podemos cambiar el nombre de la sesión, eliminando por completo la anterior y creando una nueva sesión, copiando todas las variables de sesión de la anterior en ella. Pero esto no afecta la esencia del enfoque, por lo tanto, por simplicidad, nos limitaremos solo al identificador de sesión.

Está claro que cuanto más corta sea la vida útil del identificador de sesión, menos tiempo tendrá un atacante para obtener y utilizar cookies para falsificar la solicitud de un usuario. Idealmente, se debe usar un nuevo identificador para cada solicitud, lo que minimizará la posibilidad de usar la sesión de otra persona. Pero consideraremos el caso general en el que el tiempo para regenerar el ID de sesión se establece de forma arbitraria.

(Omitamos la parte del código que ya se ha discutido).

Función startSession ($ isUserActivity = true) (// Duración de la ID de sesión $ idLifetime = 60; ... if ($ idLifetime) (// Si se establece la duración de la ID de la sesión, // verifique el tiempo transcurrido desde que se creó la sesión o la última regeneración // (hora de la última solicitud cuando se actualizó la variable de sesión hora de inicio) if (isset ($ _ SESSION ["starttime"])) (if ($ t - $ _ SESSION ["starttime"]> = $ idLifetime) (// Tiempo de ID de sesión caducado // Generar un nuevo ID session_regenerate_id (true); $ _SESSION ["starttime"] = $ t;)) else (// Llegamos aquí si la sesión acaba de ser creada // Establecer el tiempo de generación de ID de sesión en el momento actual $ _SESSION ["starttime"] = $ t;)) return true;)

Entonces, al crear una nueva sesión (que ocurre cuando el usuario inicia sesión con éxito), configuramos la variable de sesión starttime, que almacena para nosotros la hora de la última generación del identificador de sesión, en un valor igual a la hora actual del servidor. Además, en cada solicitud, verificamos si ha pasado suficiente tiempo (idLifetime) desde la última generación del identificador, y si ha pasado, generamos uno nuevo. Por lo tanto, si dentro de la vida útil especificada del identificador, el atacante que recibió la cookie del usuario autorizado no logra usarla, el servidor considerará la solicitud falsa como no autorizada y el atacante será llevado a la página de inicio de sesión.

Nota: El nuevo identificador de sesión entra en la cookie del navegador cuando se llama a la función session_regenerate_id (), que envía una nueva cookie, similar a la función session_start (), por lo que no necesitamos actualizar las cookies nosotros mismos.

Si queremos asegurar nuestras sesiones tanto como sea posible, basta con establecer la vida útil del identificador en uno, o incluso poner la función session_regenerate_id () fuera de los corchetes y eliminar todos los controles, lo que conducirá a la regeneración del identificador en cada solicitud. (No he probado el impacto de este enfoque en el rendimiento, y solo puedo decir que la función session_regenerate_id (true) esencialmente realiza solo 4 acciones: generar un nuevo identificador, crear un encabezado a partir de la cookie de sesión, eliminar el anterior y creando un nuevo archivo de sesión).

Digresión lírica: Si el troyano resulta ser tan inteligente que no enviará cookies al atacante, pero él mismo organiza el envío de una solicitud falsa preparada de antemano inmediatamente después de recibir la cookie, lo más probable es que el método descrito anteriormente no pueda proteger contra tal ataque, porque entre el momento en que el troyano recibe la cookie y el envío de la falsa, prácticamente no habrá diferencia con la solicitud, y existe una alta probabilidad de que el ID de sesión no se regenere en este punto.

Capacidad para trabajar simultáneamente en un navegador en nombre de varios usuarios

La última tarea que me gustaría considerar es la posibilidad de trabajar simultáneamente en un navegador para varios usuarios. Esta función es especialmente útil en la etapa de prueba, cuando necesita emular el trabajo simultáneo de los usuarios, y es recomendable hacerlo en su navegador favorito, y no usar todo el arsenal disponible ni abrir múltiples instancias del navegador en "incógnito " modo.

En nuestros ejemplos anteriores, no establecimos explícitamente el nombre de la sesión, por lo que se usó el nombre PHP predeterminado (PHPSESSID). Esto significa que todas las sesiones que hemos creado hasta ahora han enviado cookies al navegador con el nombre PHPSESSID. Obviamente, si el nombre de la cookie es siempre el mismo, entonces no hay forma dentro del mismo navegador de organizar dos sesiones con el mismo nombre. Pero si usáramos un nombre de sesión diferente para cada usuario, entonces el problema estaría resuelto. Entonces lo haremos.

Función startSession ($ isUserActivity = true, $ prefix = null) (... if (session_id ()) return true; // Si se pasa el prefijo de usuario en los parámetros, // establezca un nombre de sesión único que incluya este prefijo, // de lo contrario, establezca un nombre común para todos los usuarios (por ejemplo, MYPROJECT) session_name ("MYPROJECT". ($ prefix? "_". $ prefix: "")); ini_set ("session.cookie_lifetime", 0) ; si (! session_start ()) devuelve falso; ...)

Ahora todo lo que queda es asegurarse de que el script de llamada pase un prefijo único para cada usuario a la función startSession (). Esto se puede hacer, por ejemplo, pasando un prefijo en los parámetros GET / POST de cada solicitud, o mediante una cookie adicional.

Conclusión

En conclusión, daré el código final completo de nuestras funciones para trabajar con sesiones PHP, incluidas todas las tareas discutidas anteriormente.

Función startSession ($ isUserActivity = true, $ prefix = null) ($ sessionLifetime = 300; $ idLifetime = 60; if (session_id ()) return true; session_name ("MYPROJECT". ($ Prefix? "_". $ Prefix: "")); ini_set ("session.cookie_lifetime", 0); if (! session_start ()) return false; $ t = time (); if ($ sessionLifetime) (if (isset ($ _ SESSION ["lastactivity" ]) && $ t - $ _ SESSION ["lastactivity"]> = $ sessionLifetime) (destroySession (); return false;) else (if ($ isUserActivity) $ _SESSION ["lastactivity"] = $ t;)) if ( $ idLifetime) (if (isset ($ _ SESSION ["starttime"])) (if ($ t - $ _ SESSION ["starttime"]> = $ idLifetime) (session_regenerate_id (true); $ _SESSION ["starttime"] = $ t;)) else ($ _SESSION ["hora de inicio"] = $ t;)) return true;) función destroySession () (if (session_id ()) (session_unset (); setcookie (session_name (), session_id () , tiempo () -60 * 60 * 24); session_destroy ();))

Con suerte, este artículo les ahorrará algo de tiempo a aquellos que nunca han entrado realmente en el mecanismo de sesión, y les dará una comprensión suficiente de este mecanismo para aquellos que recién están comenzando a familiarizarse con PHP.

Necesita un nombre de usuario y contraseña?

Para enviar artículos a través de Internet y comprobar el estado de los artículos enviados, debe registrarse e iniciar sesión con su cuenta.

Lista de verificación para preparar un artículo para enviarlo

Como parte del proceso de envío del artículo, los autores deben verificar que su artículo cumpla con todos los puntos siguientes, los artículos pueden ser devueltos a los autores si no cumplen con estos requisitos.

    El artículo ha sido elaborado de acuerdo con los requisitos.

Condiciones de transferencia de derechos de autor

Los autores conservan los derechos de autor del trabajo y transfieren la primera publicación a la revista con el trabajo, mientras lo otorgan bajo una licencia de atribución Creative Commons que permite a otros redistribuirlo. este trabajo con la indicación obligatoria de la autoría de este trabajo y un enlace a la publicación original en esta revista.

Declaracion de privacidad

Nombres y direcciones Correo electrónico ingresado en el sitio de esta revista se utilizará únicamente para los fines indicados por esta revista y no se utilizará para ningún otro propósito ni se proporcionará a otras personas y organizaciones.

Antes de registrarse en el sistema, el usuario acepta la política de procesamiento y almacenamiento de datos personales.

Pagos de autor

1500 caracteres con espacios: 300,00 (RUB)

Publicación de 1 página del manuscrito (1500 caracteres) - 300 rublos. Los materiales gráficos / tablas se pagan por separado: 50 rublos / 1 pieza. La copia del autor, incluido el envío dentro de Rusia, se paga a pedido del autor: 400 rublos. Transferencia al extranjero - 800 rublos. El costo de enviar un certificado de aceptación del material para su publicación es de 150 rublos.

Traducción de la información adjunta (nombre completo, lugar de trabajo de los autores; título; resumen; palabras clave) en inglés 0,5 rublos por cada carácter, incluidos los espacios.

¡Atención! Los autores (candidatos y doctores en ciencias) que, según elibrary.ru, tienen 300 o más citas (la proporción de autocitas no debe ser superior al 30%) se publican de forma gratuita. Si tiene derecho a una publicación gratuita, al enviar material en el campo de comentarios, indique un enlace a su perfil en la biblioteca con el número de citas. Los gastos de envío de la recogida se pagan por separado.