Diferencia entre revisiones de «PHP Sesións»

De MediaWiki
Ir a la navegación Ir a la búsqueda
 
(No se muestran 15 ediciones intermedias del mismo usuario)
Línea 1: Línea 1:
−
==Uso de Sessions==
+
==Concepto==
  
−
* <u>Pódese empregar para pasar información 'importante'</u> ou confidencial.
+
* Unha '''sesión''' permite manter información asociada a un usuario entre diferentes peticións HTTP.
  
−
* Unha sesión 'dura' mentres un usuario está conectado a un sitio web.  
+
* HTTP é un protocolo sen estado. Isto significa que, por defecto, cada petición que realiza o navegador é independente das anteriores.
−
:* Empeza cando se conecta por primeira vez.  
+
  : As sesións permiten que o servidor poida identificar diferentes peticións como pertencentes ao mesmo usuario.
−
:* Remata cando sae do sitio web ou leva un tempo de inactividade indicado pola directiva [http://php.net/manual/es/session.configuration.php#ini.session.gc-maxlifetime session.gc_maxlifetime] no arquivo php.ini. Por defecto está establecido en 1440 segundos (24 minutos).
 
  
−
* Dentro da sesión dun usuario é posible crear variables que manterán o seu valor mentres o usuario teña a sesión activa, e poderemos obter ditos valores dende calquera páxina do sitio web.
+
* Cada sesión ten asociado un identificador único denominado '''ID de sesión''' ou '''SID'''.
−
: É importante sinalar que as variables de sesión <u>SE GARDAN NO SERVIDOR</u> a diferenza das cookies que se envían ao cliente e se gardan no seu computador.
+
 
 +
* Por defecto, PHP envía o identificador de sesión ao navegador mediante unha '''cookie'''.
 +
  : O nome predeterminado desta cookie é '''PHPSESSID'''.
 +
 
 +
* Os datos almacenados nas variables de sesión '''gárdanse no servidor'''. O navegador normalmente só almacena o identificador da sesión.
 +
 
 +
* Dentro dunha sesión podemos crear variables que manterán o seu valor entre diferentes páxinas da aplicación:
 +
  :<syntaxhighlight lang="php">
 +
  $_SESSION['usuario'] = 'angel';
 +
 
 +
  </syntaxhighlight>
 +
 
 +
* Estas variables estarán dispoñibles mentres os datos da sesión sigan existindo no servidor e a aplicación considere válida esa sesión.
 +
 
 +
* As sesións son especialmente útiles para almacenar información relacionada co estado dun usuario:
 +
  :** usuario autenticado;
 +
  :** identificador do usuario;
 +
  :** permisos;
 +
  :** carro da compra;
 +
  :** preferencias temporais;
 +
  :** tokens CSRF;
 +
  :** etc.
 +
 
 +
'''Importante:''' aínda que os datos de sesión se almacenen no servidor, non debemos considerar automaticamente segura toda a información gardada en <code>$_SESSION</code>. O identificador de sesión debe protexerse correctamente para evitar que outro usuario poida utilizar a sesión.
  
 
<br />
 
<br />
−
* Cada vez que se inicia unha sesión, se crea un identificador único ('''ID''') o cal pode ser pasado dunha páxina a outra pola URL ou por medio de [https://es.wikipedia.org/wiki/Cookie_(inform%C3%A1tica) cookies], se se ten habilitado o uso das mesmas.
 
−
* Por motivos de seguridade se recomenda pasar o ID a través de cookies e non da URL.
 
  
 +
===Duración dunha sesión===
 +
 +
Non debemos confundir:
 +
 +
* a duración da '''cookie''' que contén o identificador da sesión;
 +
* a duración dos '''datos da sesión no servidor''';
 +
* o tempo máximo de '''inactividade que decide a aplicación'''. Isto é, que dende a aplicación, nos como programadores, podemos controlar o tempo que pasa dende que o usuario se conecta a unha páxina a outra.
  
−
* Se se quere 'obrigar' ao usuario que utilice as cookies para pasar o ID se pode modificar o arquivo php.ini:
+
A directiva:
−
: '''session.use_only_cookies = 1'''
 
  
−
: <u>Nota:</u> Se estamos non aloxamento compartido, non teremos acceso ao arquivo php.ini, polo que poderemos crear un [http://php.net/manual/es/configuration.changes.php arquivo .htaccess] no noso sitio web e engadir dita directiva ao mesmo.
+
<syntaxhighlight lang="ini">
 +
session.gc_maxlifetime = 1440
 +
</syntaxhighlight>
  
 +
ten por defecto o valor '''1440 segundos (24 minutos)'''.
  
−
* Para iniciar ou reanudar unha sesión necesitamos chamar á [http://php.net/manual/es/function.session-start.php función session_start()].
+
Isto '''non significa que PHP peche automaticamente a sesión exactamente despois de 24 minutos de inactividade'''.
−
:<b> É importante lembrar que <u>debemos chamar</u> a dita función sempre que fagamos uso de variables de sesión</b>.
 
−
: Dita chamada ten que facerse <u>antes de enviar nada ao explorador do cliente (é dicir, facer un echo/printf ou calquera outra función que envíe algunha información ao navegador do cliente)</u>.
 
−
: Normalmente sitúase <u>ao principio da páxina php</u>.
 
−
: Isto é debido a que o '''ID da sesión envíase como encabezado HTTP''' ao utilizar unha cookie.
 
  
 +
<code>session.gc_maxlifetime</code> indica o tempo a partir do cal os datos dunha sesión poden considerarse obsoletos e ser eliminados polo mecanismo de recollida de lixo ou ''garbage collector''.
  
−
* Unha vez chamada podemos crear / utilizar as variables de sesión da forma: '''$_SESSION['variable'] = "valor";'''
+
A execución deste mecanismo depende tamén de:
  
 +
<syntaxhighlight lang="ini">
 +
session.gc_probability
 +
session.gc_divisor
 +
</syntaxhighlight>
  
 +
Por exemplo, cos valores predeterminados:
  
−
* Se queremos <u>borrar</u> os valores gardados nas variables de sesión:
+
<syntaxhighlight lang="ini">
−
::* Podemos chamar á [http://php.net/manual/es/function.unset.php función unset]($_SESSION['var']); dentro dunha páxina coa sesión iniciada.
+
session.gc_probability = 1
 +
session.gc_divisor = 100
 +
</syntaxhighlight>
  
−
::: Por exemplo:
+
existe unha probabilidade de 1/100 de executar o recolector de sesións ao inicializar unha sesión.
−
:::<syntaxhighlight lang="php" line enclose="div" highlight="6" >
+
 
 +
Por este motivo, se queremos implementar un límite real de inactividade, é mellor controlalo dende a propia aplicación.
 +
 
 +
Por exemplo, para pechar unha sesión despois de 30 minutos de inactividade:
 +
 
 +
<syntaxhighlight lang="php">
 
<?php
 
<?php
−
    session_start();
+
session_start();
 +
 
 +
$tempoMaximo = 30 * 60;
 +
 
 +
if (
 +
isset($_SESSION['ultima_actividade']) &&
 +
time() - $_SESSION['ultima_actividade'] > $tempoMaximo
 +
) {
 +
$_SESSION = [];
 +
session_destroy();
 +
 
 +
```
 +
header('Location: login.php');
 +
exit;
 +
```
 +
 
 +
}
 +
 
 +
$_SESSION['ultima_actividade'] = time();
 +
?> </syntaxhighlight>
 +
 
 +
Máis información:
 +
[https://www.php.net/manual/es/session.configuration.php Configuración de sesións en PHP]
 +
 
 +
<br />
 +
 
 +
==Configuración==
 +
 
 +
O comportamento das sesións pode configurarse principalmente mediante o arquivo '''php.ini'''.
 +
 
 +
Dependendo da directiva e da configuración do servidor, algúns valores tamén poden modificarse mediante <code>ini_set()</code>, <code>.user.ini</code> ou a configuración do servidor web.
 +
 
 +
As directivas máis importantes son:
 +
 
 +
; '''session.save_handler'''
 +
: Indica o mecanismo empregado para almacenar e recuperar os datos das sesións.
 +
: O valor predeterminado é:
 +
:<syntaxhighlight lang="ini">
 +
session.save_handler = files </syntaxhighlight>
 +
: Neste caso PHP almacena as sesións en ficheiros.
 +
: As extensións de PHP tamén poden proporcionar outros mecanismos de almacenamento.
 +
 
 +
; '''session.save_path'''
 +
: Indica onde se almacenarán os datos cando se emprega o controlador <code>files</code>.
 +
 
 +
; '''session.name'''
 +
: Determina o nome da cookie que almacena o identificador da sesión.
 +
: O valor predeterminado é:
 +
:<syntaxhighlight lang="ini">
 +
session.name = PHPSESSID </syntaxhighlight>
 +
 
 +
; '''session.auto_start'''
 +
: Permite iniciar automaticamente unha sesión ao comezar cada petición PHP.
 +
: O valor predeterminado é:
 +
:<syntaxhighlight lang="ini">
 +
session.auto_start = 0 </syntaxhighlight>
 +
: Normalmente é preferible manter este valor desactivado e iniciar explicitamente a sesión mediante <code>session_start()</code> cando sexa necesario.
 +
 
 +
; '''session.use_cookies'''
 +
: Indica se PHP utilizará cookies para almacenar o identificador da sesión no navegador.
 +
:<syntaxhighlight lang="ini">
 +
session.use_cookies = 1 </syntaxhighlight>
 +
 
 +
; '''session.use_only_cookies'''
 +
: Obriga a utilizar exclusivamente cookies para transportar o identificador de sesión.
 +
:<syntaxhighlight lang="ini">
 +
session.use_only_cookies = 1 </syntaxhighlight>
 +
: Este é o comportamento recomendado.
 +
: Dende PHP 8.4 está obsoleto desactivar esta opción.
 +
 
 +
; '''session.use_trans_sid'''
 +
: Permite propagar automaticamente o identificador da sesión mediante a URL.
 +
: Debe permanecer desactivado:
 +
:<syntaxhighlight lang="ini">
 +
session.use_trans_sid = 0 </syntaxhighlight>
 +
: Dende PHP 8.4 está obsoleto activar esta opción.
 +
 
 +
; '''session.use_strict_mode'''
 +
: Fai que PHP rexeite identificadores de sesión que non foran previamente creados polo servidor.
 +
: Axuda a evitar ataques de '''fixación de sesión'''.
 +
:<syntaxhighlight lang="ini">
 +
session.use_strict_mode = 1 </syntaxhighlight>
 +
: É recomendable activalo.
 +
 
 +
; '''session.cookie_lifetime'''
 +
: Indica durante canto tempo permanece no navegador a cookie que contén o identificador de sesión.
 +
:<syntaxhighlight lang="ini">
 +
session.cookie_lifetime = 0 </syntaxhighlight>
 +
: O valor <code>0</code> significa que a cookie é unha cookie de sesión e normalmente desaparecerá cando finalice a sesión do navegador.
 +
 
 +
; '''session.cookie_path'''
 +
: Indica para que rutas do servidor será válida a cookie.
 +
:<syntaxhighlight lang="ini">
 +
session.cookie_path = / </syntaxhighlight>
 +
 
 +
; '''session.cookie_secure'''
 +
: Se está activada, a cookie da sesión só se enviará mediante conexións HTTPS.
 +
:<syntaxhighlight lang="ini">
 +
session.cookie_secure = 1 </syntaxhighlight>
 +
: En produción debería utilizarse HTTPS e activar esta opción.
 +
: Nun entorno de desenvolvemento local utilizando HTTP pode ser necesario tela desactivada.
 +
 
 +
; '''session.cookie_httponly'''
 +
: Impide que JavaScript acceda directamente á cookie mediante <code>document.cookie</code>.
 +
:<syntaxhighlight lang="ini">
 +
session.cookie_httponly = 1 </syntaxhighlight>
 +
: Axuda a reducir determinados riscos derivados de ataques XSS.
 +
 
 +
; '''session.cookie_samesite'''
 +
: Controla en que peticións procedentes doutros sitios web se envía a cookie.
 +
: Os valores habituais son <code>Lax</code> e <code>Strict</code>.
 +
:<syntaxhighlight lang="ini">
 +
session.cookie_samesite = Lax </syntaxhighlight>
 +
: <code>Lax</code> adoita ser unha opción razoable para aplicacións web convencionais.
 +
 
 +
; '''session.cookie_partitioned'''
 +
: Dispoñible a partir de '''PHP 8.5'''.
 +
: Permite crear cookies particionadas mediante o mecanismo CHIPS dos navegadores.
 +
:<syntaxhighlight lang="ini">
 +
session.cookie_partitioned = 0 </syntaxhighlight>
 +
: Non é necesario activala nas aplicacións web convencionais.
 +
: Resulta especialmente útil en determinados contextos nos que unha aplicación está embebida dentro doutros sitios web.
 +
: As cookies particionadas requiren conexións seguras HTTPS.
 +
 
 +
; '''session.gc_maxlifetime'''
 +
: Indica cantos segundos deben pasar antes de que os datos dunha sesión poidan considerarse obsoletos.
 +
:<syntaxhighlight lang="ini">
 +
session.gc_maxlifetime = 1440 </syntaxhighlight>
 +
: O valor predeterminado son 1440 segundos, é dicir, 24 minutos.
 +
: Non debe empregarse como único mecanismo para implementar o peche automático por inactividade.
 +
 
 +
<br />
 +
 
 +
===Configuración recomendada===
 +
 
 +
Nunha aplicación executada mediante HTTPS poderiamos utilizar unha configuración similar á seguinte:
 +
 
 +
<syntaxhighlight lang="ini">
 +
session.use_cookies = 1
 +
session.use_only_cookies = 1
 +
session.use_trans_sid = 0
 +
 
 +
session.use_strict_mode = 1
 +
 
 +
session.cookie_secure = 1
 +
session.cookie_httponly = 1
 +
session.cookie_samesite = Lax
 +
session.cookie_lifetime = 0
 +
 
 +
session.cookie_partitioned = 0 </syntaxhighlight>
 +
 
 +
'''Nota:''' <code>session.cookie_secure = 1</code> debe utilizarse cando a aplicación funciona mediante HTTPS.
 +
 
 +
Máis información:
 +
[https://www.php.net/manual/es/session.security.ini.php Configuración de seguridade das sesións]
 +
 
 +
<br />
  
−
    .......
+
==Uso==
  
−
    unset($_SESSION['nome']);
+
===Inicializar===
 +
 
 +
Para iniciar unha nova sesión ou recuperar unha sesión anterior utilizamos:
 +
 
 +
<syntaxhighlight lang="php">
 +
session_start();
 +
</syntaxhighlight>
 +
 
 +
Por exemplo:
 +
 
 +
<syntaxhighlight lang="php">
 +
<?php
 +
session_start();
 
?>
 
?>
−
</syntaxhighlight>  
+
</syntaxhighlight>
−
:::* Tamén podemos chamar á [http://php.net/manual/es/function.session-unset.php función session_unset()] coa sesión iniciada, que borra todas as variables de sesión da memoria.
+
 
−
::::<syntaxhighlight lang="php" line enclose="div" highlight="" >
+
Cada vez que PHP inicia ou recupera unha sesión:
 +
 
 +
# comproba se o navegador enviou unha cookie co identificador da sesión;
 +
 
 +
# se a sesión existe, carga os seus datos en <code>$_SESSION</code>;
 +
 
 +
# se non existe unha sesión válida, crea unha nova.
 +
 
 +
A variable:
 +
 
 +
<syntaxhighlight lang="php">
 +
$_SESSION
 +
</syntaxhighlight>
 +
 
 +
é un '''array superglobal'''.
 +
 
 +
Podemos utilizala dende calquera script PHP no que previamente teñamos iniciado a sesión.
 +
 
 +
<br />
 +
 
 +
'''Importante:'''
 +
 
 +
<code>session_start()</code> normalmente debe executarse '''antes de enviar contido ao navegador'''.
 +
 
 +
Por exemplo:
 +
 
 +
<syntaxhighlight lang="php">
 
<?php
 
<?php
 
session_start();
 
session_start();
−
.....
+
 
−
session_unset();
+
$_SESSION['nome'] = 'Angel';
 
?>
 
?>
−
</syntaxhighlight>
 
  
−
::* Podemos chamar á [http://php.net/manual/es/function.session-destroy.php función session_destroy()].
+
<!DOCTYPE html>
−
::: Por exemplo:
+
 
−
:::<syntaxhighlight lang="php" line enclose="div" highlight="" >
+
<html lang="gl">
 +
<head>
 +
    <meta charset="UTF-8">
 +
    <title>Exemplo</title>
 +
</head>
 +
<body>
 +
    <p>Sesión iniciada.</p>
 +
</body>
 +
</html>
 +
</syntaxhighlight>
 +
 
 +
Non debemos facer primeiro:
 +
 
 +
<syntaxhighlight lang="php">
 +
echo 'Ola';
 +
 
 +
session_start(); </syntaxhighlight>
 +
 
 +
xa que PHP necesita poder enviar as cabeceiras HTTP asociadas á cookie da sesión.
 +
 
 +
<br />
 +
 
 +
===Usar variables de sesión===
 +
 
 +
Unha vez iniciada a sesión podemos crear variables:
 +
 
 +
<syntaxhighlight lang="php">
 +
$_SESSION['nome'] = 'Angel';
 +
$_SESSION['idade'] = 25;
 +
$_SESSION['autenticado'] = true;
 +
</syntaxhighlight>
 +
 
 +
E recuperalas noutra páxina:
 +
 
 +
<syntaxhighlight lang="php">
 +
<?php
 +
session_start();
 +
 
 +
echo $_SESSION['nome'];
 +
?> </syntaxhighlight>
 +
 
 +
É recomendable comprobar previamente que unha variable existe:
 +
 
 +
<syntaxhighlight lang="php">
 +
<?php
 +
session_start();
 +
 
 +
if (isset($_SESSION['nome'])) {
 +
echo $_SESSION['nome'];
 +
}
 +
?> </syntaxhighlight>
 +
 
 +
Tamén podemos utilizar o operador <code>??</code>:
 +
 
 +
<syntaxhighlight lang="php">
 +
<?php
 +
session_start();
 +
 
 +
$nome = $_SESSION['nome'] ?? 'Descoñecido';
 +
 
 +
echo $nome;
 +
?> </syntaxhighlight>
 +
 
 +
<br />
 +
 
 +
===Exemplo: contador de visitas===
 +
 
 +
<syntaxhighlight lang="php" line>
 +
<?php
 +
session_start();
 +
 
 +
if (isset($_SESSION['visitas'])) {
 +
$_SESSION['visitas']++;
 +
} else {
 +
$_SESSION['visitas'] = 1;
 +
}
 +
 
 +
echo 'Número de visitas nesta sesión: ' . $_SESSION['visitas'];
 +
?> </syntaxhighlight>
 +
 
 +
Cada usuario terá o seu propio valor de <code>$_SESSION['visitas']</code> asociado ao seu identificador de sesión.
 +
 
 +
<br />
 +
 
 +
===Consultar o identificador da sesión===
 +
 
 +
Podemos obter o identificador da sesión actual mediante:
 +
 
 +
<syntaxhighlight lang="php">
 +
session_id();
 +
</syntaxhighlight>
 +
 
 +
Por exemplo:
 +
 
 +
<syntaxhighlight lang="php">
 +
<?php
 +
session_start();
 +
 
 +
echo session_id();
 +
?> </syntaxhighlight>
 +
 
 +
'''Non debemos mostrar o identificador de sesión nunha aplicación real.'''
 +
 
 +
Neste exemplo faise unicamente con fins didácticos.
 +
 
 +
<br />
 +
 
 +
==Borrar datos dunha sesión==
 +
 
 +
Existen diferentes formas de eliminar información dunha sesión.
 +
 
 +
===Eliminar unha variable===
 +
 
 +
Podemos eliminar unha variable concreta mediante:
 +
 
 +
<syntaxhighlight lang="php">
 +
unset($_SESSION['nome']);
 +
</syntaxhighlight>
 +
 
 +
Por exemplo:
 +
 
 +
<syntaxhighlight lang="php">
 
<?php
 
<?php
 
session_start();
 
session_start();
−
.....
 
−
session_destroy();
 
−
?>
 
−
</syntaxhighlight>
 
−
: <u>Nota:</u> A sesión ten que estar iniciada antes de chamar a session_destroy().
 
  
−
: '''<u>Atención:</u>''' Esta función elimina os datos de sesión gardados no servidor pero non elimina a cookie (método por defecto) que identifica a sesión e que se garda no computador do cliente conectado. Dita cookie seguirá existindo ata que expire ou o cliente peche o navegador.
+
unset($_SESSION['nome']);
−
: Se queremos borrala teremos que executar o seguinte código:
+
?> </syntaxhighlight>
−
:::<syntaxhighlight lang="php" line enclose="div" highlight="7" >
+
 
 +
Isto elimina unicamente esa variable.
 +
 
 +
<br />
 +
 
 +
===Eliminar todas as variables===
 +
 
 +
Podemos baleirar <code>$_SESSION</code>:
 +
 
 +
<syntaxhighlight lang="php">
 +
$_SESSION = [];
 +
</syntaxhighlight>
 +
 
 +
Tamén existe:
 +
 
 +
<syntaxhighlight lang="php">
 +
session_unset();
 +
</syntaxhighlight>
 +
 
 +
que elimina todas as variables da sesión actualmente cargadas.
 +
 
 +
Por exemplo:
 +
 
 +
<syntaxhighlight lang="php">
 
<?php
 
<?php
 
session_start();
 
session_start();
  
−
.....
+
session_unset();
 +
?> </syntaxhighlight>
 +
 
 +
Na práctica, a forma:
 +
 
 +
<syntaxhighlight lang="php">
 +
$_SESSION = [];
 +
</syntaxhighlight>
 +
 
 +
é moi clara e habitual.
 +
 
 +
<br />
 +
 
 +
===Destruír a sesión===
 +
 
 +
A función:
  
 +
<syntaxhighlight lang="php">
 
session_destroy();
 
session_destroy();
−
setcookie(session_name(),'',0,'/');
+
</syntaxhighlight>
−
?>
+
 
−
</syntaxhighlight>  
+
elimina os datos almacenados no servidor asociados á sesión actual.
  
 +
Por exemplo:
  
−
::* Lembrar que $_SESSION é un array e polo tanto podemos percorrelo e borrar todos os elementos:
+
<syntaxhighlight lang="php">
−
:::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 
 
<?php
 
<?php
−
foreach(array_keys($_SESSION) as $k) unset($_SESSION[$k])
+
session_start();
−
?>
+
 
−
</syntaxhighlight>  
+
session_destroy();
 +
?> </syntaxhighlight>
 +
 
 +
'''Importante:''' <code>session_destroy()</code>:
 +
 
 +
* elimina os datos da sesión almacenados no servidor;
 +
* non elimina automaticamente o contido actual do array <code>$_SESSION</code>;
 +
* non elimina automaticamente a cookie de sesión do navegador.
  
 +
Por iso, nun proceso de '''logout''' normalmente queremos realizar as tres operacións.
  
 +
<br />
  
−
* Para ver a diferenza entre session_destroy() e session_unset() recomendo ver [https://rg1024.wordpress.com/2016/02/18/session_unset-vs-session_destroy/ esta entrada dun blog].
+
===Pechar completamente unha sesión===
−
: Basicamente o que indica é que cando facemos un session_start() estamos cargando os datos gardados no disco do servidor no array global $_SESSION.
 
−
: Cando facemos un session_unset() estamos a borrar ese array, pero os datos seguen gardados no disco ata que remata de procesar a páxina, momento en que se pasan os datos do array (borrados) a disco, borrando os datos.
 
−
: Cando facemos un session_destroy() os datos gardados no disco do servidor son borrados, pero a copia local gardada na variable $_SESSION segue existindo na páxina que se está a procesar.
 
  
 +
Un exemplo básico para pechar a sesión dun usuario é:
  
−
* A orde correcta para eliminar todas as variables de sesión e que deixen de ser válidas na páxina que se está a procesar, eliminando o id de sesión gardado en forma de cookie no computador do cliente sería:
+
<syntaxhighlight lang="php" line>
−
:::<syntaxhighlight lang="php" line enclose="div" highlight="" >
 
 
<?php
 
<?php
 
session_start();
 
session_start();
−
.....
+
 
−
session_unset();
+
// Eliminamos todas as variables de sesión.
 +
$_SESSION = [];
 +
 
 +
// Eliminamos tamén a cookie que contén o ID da sesión.
 +
if (ini_get('session.use_cookies')) {
 +
$params = session_get_cookie_params();
 +
 
 +
```
 +
setcookie(
 +
    session_name(),
 +
    '',
 +
    time() - 42000,
 +
    $params['path'],
 +
    $params['domain'],
 +
    $params['secure'],
 +
    $params['httponly']
 +
);
 +
```
 +
 
 +
}
 +
 
 +
// Eliminamos os datos da sesión almacenados no servidor.
 
session_destroy();
 
session_destroy();
−
setcookie(session_name(),'',0,'/');
+
?> </syntaxhighlight>
−
?>
+
 
−
</syntaxhighlight>  
+
Deste xeito:
 +
 
 +
# elimínanse as variables de <code>$_SESSION</code>;
 +
 
 +
# elimínase a cookie que identifica a sesión;
 +
 
 +
# elimínanse os datos gardados no servidor.
 +
 
 +
Nas aplicacións complexas poden existir peticións simultáneas, polo que a destrución inmediata dunha sesión debe xestionarse tendo en conta posibles condicións de carreira.
 +
 
 +
Máis información:
 +
[https://www.php.net/manual/es/function.session-destroy.php session_destroy()]
 +
 
 +
<br />
 +
 
 +
==Seguridade das sesións==
 +
 
 +
O identificador de sesión funciona como unha credencial temporal.
 +
 
 +
Se un atacante consegue obter un identificador de sesión válido podería intentar utilizalo para facerse pasar polo usuario.
 +
 
 +
Por iso debemos protexer correctamente a cookie da sesión.
 +
 
 +
Algunhas recomendacións son:
 +
 
 +
* utilizar sempre '''HTTPS''' en produción;
 +
* activar <code>session.cookie_secure</code>;
 +
* activar <code>session.cookie_httponly</code>;
 +
* utilizar <code>session.cookie_samesite</code>;
 +
* activar <code>session.use_only_cookies</code>;
 +
* manter <code>session.use_trans_sid</code> desactivado;
 +
* activar <code>session.use_strict_mode</code>;
 +
* rexenerar o identificador de sesión cando cambian os privilexios do usuario;
 +
* implementar un tempo máximo de inactividade na propia aplicación cando sexa necesario.
 +
 
 +
<br />
 +
 
 +
===Non enviar o SID pola URL===
 +
 
 +
Non debemos utilizar URLs deste tipo:
 +
 
 +
<syntaxhighlight lang="text">
 +
https://exemplo.com/paxina.php?PHPSESSID=abc123...
 +
</syntaxhighlight>
 +
 
 +
O identificador podería aparecer:
 +
 
 +
* no historial do navegador;
 +
* en logs do servidor;
 +
* en ferramentas de análise;
 +
* en capturas de pantalla;
 +
* en enlaces compartidos;
 +
* na cabeceira HTTP <code>Referer</code> en determinadas situacións.
 +
 
 +
Por iso PHP debe utilizar cookies para transportar o SID.
 +
 
 +
En PHP 8.5:
 +
 
 +
<syntaxhighlight lang="ini">
 +
session.use_only_cookies = 1
 +
session.use_trans_sid = 0
 +
</syntaxhighlight>
 +
 
 +
<br />
 +
 
 +
===Fixación de sesión===
 +
 
 +
Nun ataque de '''fixación de sesión''' un atacante intenta obrigar á vítima a utilizar un identificador de sesión previamente coñecido polo atacante.
 +
 
 +
Unha medida importante para reducir este risco é:
 +
 
 +
<syntaxhighlight lang="ini">
 +
session.use_strict_mode = 1
 +
</syntaxhighlight>
  
 +
Ademais, cando un usuario se autentica correctamente deberíase rexenerar o identificador da sesión.
  
−
* Vexamos un exemplo:
+
Por exemplo:
  
−
* Arquivo '''UD4_session_Ex1_paxina1.php''':
+
<syntaxhighlight lang="php">
−
::<syntaxhighlight lang="java" line enclose="div" highlight="" >
 
 
<?php
 
<?php
−
    session_start();
+
session_start();
−
?>
+
 
−
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+
// Supoñemos que xa comprobamos usuario e contrasinal.
−
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
 
 +
session_regenerate_id();
 +
 
 +
$_SESSION['usuario_id'] = $usuarioId;
 +
$_SESSION['autenticado'] = true;
 +
?> </syntaxhighlight>
 +
 
 +
É recomendable rexenerar o identificador '''antes de almacenar na nova sesión os privilexios obtidos tras a autenticación'''.
 +
 
 +
Máis información:
 +
[https://www.php.net/manual/es/function.session-regenerate-id.php session_regenerate_id()]
 +
 
 +
<br />
 +
 
 +
==Exemplo completo==
 +
 
 +
Vexamos un exemplo moi simple utilizando tres páxinas.
 +
 
 +
===Arquivo UD4_session_Ex1_paxina1.php===
  
−
<html>
+
<syntaxhighlight lang="php" line>
−
    <head>
 
−
        <meta charset="UTF-8">
 
−
        <title></title>
 
−
    </head>
 
−
    <body>
 
 
<?php
 
<?php
−
    $_SESSION['nome']='angel';
+
session_start();
  
 +
$_SESSION['nome'] = 'Angel';
 
?>
 
?>
−
        <h1>Posto o valor da variable session 'nome' a Angel.</h1>
+
 
−
        <h2>Pulsa <a href='UD4_session_Ex1_paxina2.php'> para ver o seu valor</a> noutra páxina...</h2>.
+
<!DOCTYPE html>
−
       
+
 
−
       
+
<html lang="gl">
−
    </body>
+
<head>
 +
    <meta charset="UTF-8">
 +
    <title>Sesións - Páxina 1</title>
 +
</head>
 +
 
 +
<body>
 +
 
 +
<h1>Asignouse o valor Angel á variable de sesión 'nome'.</h1>
 +
 
 +
<h2>
 +
    <a href="UD4_session_Ex1_paxina2.php">
 +
        Ver o seu valor noutra páxina
 +
    </a>
 +
</h2>
 +
 
 +
</body>
 
</html>
 
</html>
 
</syntaxhighlight>
 
</syntaxhighlight>
Línea 136: Línea 651:
 
[[Imagen:Php_session_1.jpg|400px]]
 
[[Imagen:Php_session_1.jpg|400px]]
  
−
* Arquivo '''UD4_session_Ex1_paxina2.php''':
+
<br />
−
::<syntaxhighlight lang="java" line enclose="div" highlight="" >
+
 
 +
===Arquivo UD4_session_Ex1_paxina2.php===
 +
 
 +
<syntaxhighlight lang="php" line>
 
<?php
 
<?php
−
    session_start();
+
session_start();
 +
 
 +
$nome = $_SESSION['nome'] ?? 'Non existe';
 
?>
 
?>
−
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 
−
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
  
−
<html>
+
<!DOCTYPE html>
−
    <head>
+
 
−
        <meta charset="UTF-8">
+
<html lang="gl">
−
        <title></title>
+
<head>
−
    </head>
+
    <meta charset="UTF-8">
−
    <body>
+
    <title>Sesións - Páxina 2</title>
−
<?php
+
</head>
−
    printf("<h1>O valor da variable sesión é: %s</h1>",$_SESSION['nome']);
+
 
−
    printf("<h2>O valor do ID de sesión é: %s</h2>",  session_id());
+
<body>
 +
 
 +
<h1>
 +
    O valor da variable de sesión é:
 +
    <?= htmlspecialchars($nome, ENT_QUOTES, 'UTF-8') ?>
 +
</h1>
 +
 
 +
<p>
 +
    ID de sesión:
 +
    <?= htmlspecialchars(session_id(), ENT_QUOTES, 'UTF-8') ?>
 +
</p>
 +
 
 +
<p>
 +
    <strong>
 +
        O identificador móstrase unicamente con fins didácticos.
 +
        Nunha aplicación real non debería mostrarse ao usuario.
 +
    </strong>
 +
</p>
 +
 
 +
<h2>
 +
    <a href="UD4_session_Ex1_paxina3.php">
 +
        Borrar a sesión
 +
    </a>
 +
</h2>
  
−
?>
+
</body>
−
    <h2>Pulsa <a href='UD4_session_Ex1_paxina3.php'> para borrar o seu valor</a>...</h2>.
 
−
       
 
−
    </body>
 
 
</html>
 
</html>
 
</syntaxhighlight>
 
</syntaxhighlight>
Línea 163: Línea 701:
 
[[Imagen:Php_session_2.jpg|400px]]
 
[[Imagen:Php_session_2.jpg|400px]]
  
 +
<br />
 +
 +
===Arquivo UD4_session_Ex1_paxina3.php===
  
−
* Arquivo '''UD4_session_Ex1_paxina3.php''':
+
<syntaxhighlight lang="php" line>
−
::<syntaxhighlight lang="java" line enclose="div" highlight="" >
 
 
<?php
 
<?php
−
    session_start();
+
session_start();
  
 +
$_SESSION = [];
  
−
    session_unset();  // Poderíamos poñer: session_unset['nome']
+
if (ini_get('session.use_cookies')) {
−
    session_destroy();
+
$params = session_get_cookie_params();
−
    setcookie(session_name(),'',0,'/');
 
  
 +
```
 +
setcookie(
 +
    session_name(),
 +
    '',
 +
    time() - 42000,
 +
    $params['path'],
 +
    $params['domain'],
 +
    $params['secure'],
 +
    $params['httponly']
 +
);
 +
```
  
−
    // Lembrar que o session_id segue estando gardado como cookie no cliente.
+
}
−
    // Se queredes borrala teredes que poñer: setcookie(session_name(),'',0,'/');
 
  
 +
session_destroy();
 
?>
 
?>
−
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 
−
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
  
−
<html>
+
<!DOCTYPE html>
−
    <head>
+
 
−
        <meta charset="UTF-8">
+
<html lang="gl">
−
        <title></title>
+
<head>
−
    </head>
+
    <meta charset="UTF-8">
−
    <body>
+
    <title>Sesións - Páxina 3</title>
−
<?php
+
</head>
−
    printf("<h1>O valor da variable sesión é: %s</h1>",$_SESSION['nome']);
+
 
−
     printf("<h2>O valor do ID de sesión é: %s</h2>",  session_id());
+
<body>
 +
 
 +
<h1>A sesión foi eliminada.</h1>
 +
 
 +
<h2>
 +
    <a href="UD4_session_Ex1_paxina1.php">
 +
        Volver ao inicio
 +
     </a>
 +
</h2>
  
−
?>
+
</body>
−
    <h2>Pulsa <a href='UD4_session_Ex1_paxina1.php'> para volver a darlle un valor</a>...</h2>.
 
−
       
 
−
    </body>
 
 
</html>
 
</html>
 
</syntaxhighlight>
 
</syntaxhighlight>
Línea 201: Línea 755:
 
[[Imagen:Php_session_3.jpg|400px]]
 
[[Imagen:Php_session_3.jpg|400px]]
  
 +
<br />
  
−
<br />
+
==Comprobar a cookie da sesión==
 +
 
 +
Podemos comprobar a cookie enviada por PHP utilizando as ferramentas de desenvolvemento do navegador.
  
−
* Podemos comprobar como dende o servidor se envía unha cookie ao cliente para que se garde o identificador de sesión e así o servidor é capaz de identificar ao cliente.
+
Por exemplo, en Firefox ou Chrome:
  
−
<gallery caption="Valor de cookie que garda o identificador de sesión" widths="350" heights="300px" perrow="2">
+
# Abrir as '''Ferramentas de desenvolvemento'''.
−
Image:Php_session_4.jpg| Captura do [https://addons.mozilla.org/es/firefox/addon/live-http-headers/ plugin http-header] na páxina UD4_session_Ex1_paxina2.php.
 
−
Image:Php_session_5.jpg| O mesmo que o anterior pero indo as preferencias de firefox e na sección de '''Privacidad''' podemos ver o valor da cookie gardada en localhost (se tiveramos un dominio aparecería o nome do dominio).
 
−
</gallery>
 
  
−
<br />
+
# Acceder á sección de almacenamento ou aplicación.
−
* Por iso é importante que cando un usuario decida saír dun programa no que estea autenticado, debe premer a opción saír, xa que un usuario podería obter o id da sesión gardada localmente, modificar a súa id cun [https://addons.mozilla.org/es/firefox/addon/cookies-manager-plus/ plugin calquera] e acceder á información de sesión gardada no servidor.
 
  
−
* O uso da URL para pasar o identificador de sesión (se non temos habilitado o uso de cookies) non é recomendable, xa que é visualmente accesible para calquera e se queremos compartir unha URL estariamos compartindo tamén o noso identificador de sesión.
+
# Seleccionar '''Cookies'''.
  
−
* Tamén se recomenda limitar o tempo de vida dunha cookie. Este parámetro se pode configurar coa directiva [http://php.net/manual/es/session.configuration.php#ini.session.cookie_lifetime session.cookie_lifetime tempo_en_segundos] no que indicamos o tempo en segundos que vai ter validez a cookie enviada ao navegador. O valor por defecto é de 0, que significa que a cookie será válida ata que se pecha o navegador.
+
# Buscar a cookie chamada normalmente <code>PHPSESSID</code>.
  
 +
Tamén podemos observar a cabeceira HTTP enviada polo servidor:
  
 +
<syntaxhighlight lang="text">
 +
Set-Cookie: PHPSESSID=...
 +
</syntaxhighlight>
  
 +
Nunha aplicación correctamente configurada mediante HTTPS poderemos atopar atributos como:
  
 +
<syntaxhighlight lang="text">
 +
Secure
 +
HttpOnly
 +
SameSite=Lax
 +
</syntaxhighlight>
 +
 +
'''Non se debe copiar nin compartir o valor dunha cookie de sesión activa.'''
  
 
<br />
 
<br />
 +
 
==Ataques CSRF (Cross-Site Request Forgery)==
 
==Ataques CSRF (Cross-Site Request Forgery)==
  
−
* Xa vimos [https://wiki.cifprodolfoucha.es/index.php?title=PHP_Paso_de_datos_con_formularios#Seguridade_no_uso_dos_formularios na UD2 desta wiki], diferentes técnicas para evitar ataques cando empregamos formulario.
+
Un ataque '''CSRF''' intenta conseguir que un navegador autenticado realice unha operación nun sitio web sen que o usuario teña intención de facela.
−
: Existe outro ataque que se pode empregar, consistente en que ao meu sitio web veñan os datos dun formulario dende outro sitio web diferente (é dicir, que se faga unha operación post/get dende un formulario diferente ao que o meu sitio web envía). É o que se coñece como CSRF.
+
 
 +
Por exemplo, un usuario podería estar autenticado en:
 +
 
 +
<syntaxhighlight lang="text">
 +
https://banco.exemplo
 +
</syntaxhighlight>
 +
 
 +
e visitar posteriormente unha páxina maliciosa que intente provocar unha petición contra ese sitio.
 +
 
 +
Como o navegador pode enviar automaticamente as cookies asociadas ao dominio, o servidor podería recibir unha petición acompañada dunha sesión válida.
 +
 
 +
Unha das técnicas máis utilizadas para protexer formularios é utilizar un '''token CSRF'''.
 +
 
 +
<br />
  
−
: Para evitalo, se fai uso das variables de sesión para xerar un código único ao cargar o formulario de tal forma que cando o usuario envía os datos do formulario se comproba que dito código é igual ao enviado ao cliente. Se non o fora, non se deixaría facer a operación cós datos do formulario.
+
===Xerar o token===
  
−
O código para implementalo sería:
+
Podemos utilizar <code>random_bytes()</code> para xerar un valor aleatorio criptograficamente seguro.
  
−
'''Xerar o Token:''' Ao cargar o formulario, xera un token único e gardao nunha variable de sesión.
+
<syntaxhighlight lang="php">
−
::<syntaxhighlight lang="java" enclose="div" highlight="" >
+
<?php
 
session_start();
 
session_start();
 +
 +
if (!isset($_SESSION['csrf_token'])) {
 
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
 
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
−
</syntaxhighlight>
+
}
 +
?> </syntaxhighlight>
 +
 
 +
O token almacénase na sesión do usuario.
 +
 
 +
<br />
  
 +
===Incorporar o token no formulario===
  
 +
Incluímos o token nun campo oculto:
  
−
'''Incorporar o Token no Formulario:''' Inclúe este token como un campo oculto dentro do teu formulario HTML.
+
<syntaxhighlight lang="php">
−
::<syntaxhighlight lang="java" enclose="div" highlight="" >
 
 
<form action="procesar.php" method="post">
 
<form action="procesar.php" method="post">
−
    <!-- Outros campos -->
+
 
−
    <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
+
```
−
    <input type="submit" value="Enviar">
+
<!-- Outros campos do formulario -->
 +
 
 +
<input
 +
    type="hidden"
 +
    name="csrf_token"
 +
    value="<?= htmlspecialchars(
 +
        $_SESSION['csrf_token'],
 +
        ENT_QUOTES,
 +
        'UTF-8'
 +
    ) ?>"
 +
>
 +
 
 +
<input type="submit" value="Enviar">
 +
```
 +
 
 
</form>
 
</form>
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
<br />
  
−
'''Verificar o Token:''' Cando o formulario se envíe, compara o token recibido có que está almacenado na variable de sesión.
+
===Verificar o token===
−
::<syntaxhighlight lang="java" enclose="div" highlight="" >
+
 
 +
Ao recibir o formulario comprobamos que:
 +
 
 +
* existe un token almacenado na sesión;
 +
* recibimos un token no formulario;
 +
* ambos coinciden.
 +
 
 +
Para comparar tokens é preferible utilizar <code>hash_equals()</code>.
 +
 
 +
<syntaxhighlight lang="php">
 +
<?php
 
session_start();
 
session_start();
−
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
+
 
−
    // Os tokens non coinciden, pode ser un intento de CSRF.
+
if (
−
    die('Acción non autorizada');
+
!isset($_SESSION['csrf_token'], $_POST['csrf_token']) ||
 +
!is_string($_POST['csrf_token']) ||
 +
!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])
 +
) {
 +
http_response_code(403);
 +
exit('Acción non autorizada');
 
}
 
}
 +
 +
// O formulario é válido.
 +
// Procesamos os datos...
 +
?> </syntaxhighlight>
 +
 +
<code>hash_equals()</code> realiza a comparación intentando evitar que o tempo empregado na operación revele información sobre o token.
 +
 +
<br />
 +
 +
===Invalidar o token===
 +
 +
Se queremos que o token só poida utilizarse unha vez:
 +
 +
<syntaxhighlight lang="php">
 +
unset($_SESSION['csrf_token']);
 +
</syntaxhighlight>
 +
 +
Un novo formulario deberá xerar posteriormente un novo token.
 +
 +
<br />
 +
 +
===SameSite e CSRF===
 +
 +
Configurar:
 +
 +
<syntaxhighlight lang="ini">
 +
session.cookie_samesite = Lax
 +
</syntaxhighlight>
 +
 +
ou, cando sexa posible:
 +
 +
<syntaxhighlight lang="ini">
 +
session.cookie_samesite = Strict
 +
</syntaxhighlight>
 +
 +
axuda a reducir determinados ataques CSRF ao limitar o envío da cookie en peticións iniciadas dende outros sitios.
 +
 +
Porén, '''SameSite non debe considerarse un substituto universal dos tokens CSRF'''.
 +
 +
Nas operacións que modifican datos é recomendable combinar varias medidas:
 +
 +
* utilizar métodos HTTP apropiados, especialmente <code>POST</code>;
 +
* utilizar tokens CSRF;
 +
* configurar correctamente <code>SameSite</code>;
 +
* comprobar os permisos do usuario;
 +
* utilizar HTTPS.
 +
 +
<br />
 +
 +
==Novidades relacionadas coas sesións en PHP 8.5==
 +
 +
PHP 8.5 incorpora soporte directo para '''cookies particionadas'''.
 +
 +
As seguintes funcións admiten agora a opción:
 +
 +
<syntaxhighlight lang="text">
 +
partitioned
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
entre as opcións da cookie:
 +
 +
* <code>session_start()</code>;
 +
* <code>session_set_cookie_params()</code>;
 +
* <code>session_get_cookie_params()</code>;
 +
* <code>setcookie()</code>;
 +
* <code>setrawcookie()</code>.
 +
 +
Por exemplo:
 +
 +
<syntaxhighlight lang="php">
 +
<?php
 +
session_set_cookie_params([
 +
    'lifetime' => 0,
 +
    'path' => '/',
 +
    'secure' => true,
 +
    'httponly' => true,
 +
    'samesite' => 'Lax',
 +
    'partitioned' => true
 +
]);
  
−
'''Destruír o Token:''' Unha vez utilizado, é boa práctica invalidar o token.
+
session_start();
−
::<syntaxhighlight lang="java" enclose="div" highlight="" >
+
?> </syntaxhighlight>
−
unset($_SESSION['csrf_token']);
+
 
 +
As cookies particionadas están pensadas principalmente para situacións nas que unha aplicación utiliza cookies nun contexto entre sitios.
 +
 
 +
Para unha aplicación web convencional normalmente:
 +
 
 +
<syntaxhighlight lang="ini">
 +
session.cookie_partitioned = 0
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
é suficiente.
 +
 +
Máis información:
 +
[https://www.php.net/manual/es/migration85.new-features.php Novidades de PHP 8.5]
 +
 +
<br />
 +
 +
==Resumo==
 +
 +
* As sesións permiten manter datos entre diferentes peticións HTTP.
 +
 +
* Os datos das variables de sesión almacénanse normalmente no servidor.
 +
 +
* O navegador conserva un identificador de sesión mediante unha cookie.
 +
 +
* Para utilizar <code>$_SESSION</code> debemos iniciar ou recuperar a sesión mediante:
 +
  :<syntaxhighlight lang="php">
 +
  session_start();
 +
 +
  </syntaxhighlight>
 +
 +
* Para crear unha variable:
 +
  :<syntaxhighlight lang="php">
 +
  $_SESSION['nome'] = 'Angel';
 +
 +
  </syntaxhighlight>
 +
 +
* Para eliminar unha variable:
 +
  :<syntaxhighlight lang="php">
 +
  unset($_SESSION['nome']);
 +
 +
  </syntaxhighlight>
 +
 +
* Para eliminar todas as variables:
 +
  :<syntaxhighlight lang="php">
 +
  $_SESSION = [];
 +
 +
  </syntaxhighlight>
 +
 +
* Para destruír os datos da sesión:
 +
  :<syntaxhighlight lang="php">
 +
  session_destroy();
 +
 +
  </syntaxhighlight>
 +
 +
* <code>session.gc_maxlifetime</code> '''non representa un temporizador exacto de peche da sesión'''.
  
 +
* En aplicacións reais debemos protexer o identificador de sesión mediante HTTPS, cookies seguras e configuración apropiada.
  
 +
* Recoméndase activar:
 +
  :<syntaxhighlight lang="ini">
 +
  session.use_strict_mode = 1
 +
  session.use_only_cookies = 1
 +
  session.cookie_secure = 1
 +
  session.cookie_httponly = 1
 +
  session.cookie_samesite = Lax
  
 +
  </syntaxhighlight>
  
 +
* O SID non debe enviarse mediante a URL.
 +
 +
* Despois dunha autenticación ou dun cambio de privilexios é recomendable rexenerar o identificador mediante:
 +
  :<syntaxhighlight lang="php">
 +
  session_regenerate_id();
 +
 +
  </syntaxhighlight>
 +
 +
* Para protexer formularios fronte a CSRF podemos empregar tokens xerados con:
 +
  :<syntaxhighlight lang="php">
 +
  bin2hex(random_bytes(32));
 +
 +
  </syntaxhighlight>
 +
 +
e comparalos mediante:
 +
 +
<syntaxhighlight lang="php">
 +
hash_equals();
 +
</syntaxhighlight>
 +
 +
<br />
 +
 +
==Exercicios propostos==
 +
 +
* Podedes facer [https://wiki.cifprodolfoucha.es/index.php?title=PHP_EXERCICIOS_SESSION este conxunto de exercicios].
 +
 +
<br />
  
 +
==Referencias==
  
−
<br> -- [[Usuario:angelfg|Ángel D. Fernández González]] -- (2023).
+
* [https://www.php.net/manual/es/book.session.php Sesións - Manual de PHP]
 +
* [https://www.php.net/manual/es/session.configuration.php Configuración das sesións]
 +
* [https://www.php.net/manual/es/features.session.security.management.php Seguridade das sesións]
 +
* [https://www.php.net/manual/es/session.security.ini.php Configuración INI de seguridade]
 +
* [https://www.php.net/manual/es/function.session-start.php session_start()]
 +
* [https://www.php.net/manual/es/function.session-destroy.php session_destroy()]
 +
* [https://www.php.net/manual/es/function.session-regenerate-id.php session_regenerate_id()]
 +
* [https://www.php.net/manual/es/function.random-bytes.php random_bytes()]
 +
* [https://www.php.net/manual/es/function.hash-equals.php hash_equals()]
 +
* [https://www.php.net/manual/es/migration85.new-features.php Novidades de PHP 8.5]

Revisión actual del 07:41 25 sep 2026

Concepto

  • Unha sesión permite manter información asociada a un usuario entre diferentes peticións HTTP.
  • HTTP é un protocolo sen estado. Isto significa que, por defecto, cada petición que realiza o navegador é independente das anteriores.
 : As sesións permiten que o servidor poida identificar diferentes peticións como pertencentes ao mesmo usuario.
  • Cada sesión ten asociado un identificador único denominado ID de sesión ou SID.
  • Por defecto, PHP envía o identificador de sesión ao navegador mediante unha cookie.
 : O nome predeterminado desta cookie é PHPSESSID.
  • Os datos almacenados nas variables de sesión gárdanse no servidor. O navegador normalmente só almacena o identificador da sesión.
  • Dentro dunha sesión podemos crear variables que manterán o seu valor entre diferentes páxinas da aplicación:

:

  $_SESSION['usuario'] = 'angel';
  • Estas variables estarán dispoñibles mentres os datos da sesión sigan existindo no servidor e a aplicación considere válida esa sesión.
  • As sesións son especialmente útiles para almacenar información relacionada co estado dun usuario:
 :** usuario autenticado;
 :** identificador do usuario;
 :** permisos;
 :** carro da compra;
 :** preferencias temporais;
 :** tokens CSRF;
 :** etc.

Importante: aínda que os datos de sesión se almacenen no servidor, non debemos considerar automaticamente segura toda a información gardada en $_SESSION. O identificador de sesión debe protexerse correctamente para evitar que outro usuario poida utilizar a sesión.


Duración dunha sesión

Non debemos confundir:

  • a duración da cookie que contén o identificador da sesión;
  • a duración dos datos da sesión no servidor;
  • o tempo máximo de inactividade que decide a aplicación. Isto é, que dende a aplicación, nos como programadores, podemos controlar o tempo que pasa dende que o usuario se conecta a unha páxina a outra.

A directiva:

session.gc_maxlifetime = 1440

ten por defecto o valor 1440 segundos (24 minutos).

Isto non significa que PHP peche automaticamente a sesión exactamente despois de 24 minutos de inactividade.

session.gc_maxlifetime indica o tempo a partir do cal os datos dunha sesión poden considerarse obsoletos e ser eliminados polo mecanismo de recollida de lixo ou garbage collector.

A execución deste mecanismo depende tamén de:

session.gc_probability
session.gc_divisor

Por exemplo, cos valores predeterminados:

session.gc_probability = 1
session.gc_divisor = 100

existe unha probabilidade de 1/100 de executar o recolector de sesións ao inicializar unha sesión.

Por este motivo, se queremos implementar un límite real de inactividade, é mellor controlalo dende a propia aplicación.

Por exemplo, para pechar unha sesión despois de 30 minutos de inactividade:

<?php
session_start();

$tempoMaximo = 30 * 60;

if (
isset($_SESSION['ultima_actividade']) &&
time() - $_SESSION['ultima_actividade'] > $tempoMaximo
) {
$_SESSION = [];
session_destroy();

```
header('Location: login.php');
exit;
```

}

$_SESSION['ultima_actividade'] = time();
?>

Máis información: Configuración de sesións en PHP


Configuración

O comportamento das sesións pode configurarse principalmente mediante o arquivo php.ini.

Dependendo da directiva e da configuración do servidor, algúns valores tamén poden modificarse mediante ini_set(), .user.ini ou a configuración do servidor web.

As directivas máis importantes son:

session.save_handler
Indica o mecanismo empregado para almacenar e recuperar os datos das sesións.
O valor predeterminado é:
session.save_handler = files
Neste caso PHP almacena as sesións en ficheiros.
As extensións de PHP tamén poden proporcionar outros mecanismos de almacenamento.
session.save_path
Indica onde se almacenarán os datos cando se emprega o controlador files.
session.name
Determina o nome da cookie que almacena o identificador da sesión.
O valor predeterminado é:
session.name = PHPSESSID
session.auto_start
Permite iniciar automaticamente unha sesión ao comezar cada petición PHP.
O valor predeterminado é:
session.auto_start = 0
Normalmente é preferible manter este valor desactivado e iniciar explicitamente a sesión mediante session_start() cando sexa necesario.
session.use_cookies
Indica se PHP utilizará cookies para almacenar o identificador da sesión no navegador.
session.use_cookies = 1
session.use_only_cookies
Obriga a utilizar exclusivamente cookies para transportar o identificador de sesión.
session.use_only_cookies = 1
Este é o comportamento recomendado.
Dende PHP 8.4 está obsoleto desactivar esta opción.
session.use_trans_sid
Permite propagar automaticamente o identificador da sesión mediante a URL.
Debe permanecer desactivado:
session.use_trans_sid = 0
Dende PHP 8.4 está obsoleto activar esta opción.
session.use_strict_mode
Fai que PHP rexeite identificadores de sesión que non foran previamente creados polo servidor.
Axuda a evitar ataques de fixación de sesión.
session.use_strict_mode = 1
É recomendable activalo.
session.cookie_lifetime
Indica durante canto tempo permanece no navegador a cookie que contén o identificador de sesión.
session.cookie_lifetime = 0
O valor 0 significa que a cookie é unha cookie de sesión e normalmente desaparecerá cando finalice a sesión do navegador.
session.cookie_path
Indica para que rutas do servidor será válida a cookie.
session.cookie_path = /
session.cookie_secure
Se está activada, a cookie da sesión só se enviará mediante conexións HTTPS.
session.cookie_secure = 1
En produción debería utilizarse HTTPS e activar esta opción.
Nun entorno de desenvolvemento local utilizando HTTP pode ser necesario tela desactivada.
session.cookie_httponly
Impide que JavaScript acceda directamente á cookie mediante document.cookie.
session.cookie_httponly = 1
Axuda a reducir determinados riscos derivados de ataques XSS.
session.cookie_samesite
Controla en que peticións procedentes doutros sitios web se envía a cookie.
Os valores habituais son Lax e Strict.
session.cookie_samesite = Lax
Lax adoita ser unha opción razoable para aplicacións web convencionais.
session.cookie_partitioned
Dispoñible a partir de PHP 8.5.
Permite crear cookies particionadas mediante o mecanismo CHIPS dos navegadores.
session.cookie_partitioned = 0
Non é necesario activala nas aplicacións web convencionais.
Resulta especialmente útil en determinados contextos nos que unha aplicación está embebida dentro doutros sitios web.
As cookies particionadas requiren conexións seguras HTTPS.
session.gc_maxlifetime
Indica cantos segundos deben pasar antes de que os datos dunha sesión poidan considerarse obsoletos.
session.gc_maxlifetime = 1440
O valor predeterminado son 1440 segundos, é dicir, 24 minutos.
Non debe empregarse como único mecanismo para implementar o peche automático por inactividade.


Configuración recomendada

Nunha aplicación executada mediante HTTPS poderiamos utilizar unha configuración similar á seguinte:

session.use_cookies = 1
session.use_only_cookies = 1
session.use_trans_sid = 0

session.use_strict_mode = 1

session.cookie_secure = 1
session.cookie_httponly = 1
session.cookie_samesite = Lax
session.cookie_lifetime = 0

session.cookie_partitioned = 0

Nota: session.cookie_secure = 1 debe utilizarse cando a aplicación funciona mediante HTTPS.

Máis información: Configuración de seguridade das sesións


Uso

Inicializar

Para iniciar unha nova sesión ou recuperar unha sesión anterior utilizamos:

session_start();

Por exemplo:

<?php
session_start();
?>

Cada vez que PHP inicia ou recupera unha sesión:

  1. comproba se o navegador enviou unha cookie co identificador da sesión;
  1. se a sesión existe, carga os seus datos en $_SESSION;
  1. se non existe unha sesión válida, crea unha nova.

A variable:

$_SESSION

é un array superglobal.

Podemos utilizala dende calquera script PHP no que previamente teñamos iniciado a sesión.


Importante:

session_start() normalmente debe executarse antes de enviar contido ao navegador.

Por exemplo:

<?php
session_start();

$_SESSION['nome'] = 'Angel';
?>

<!DOCTYPE html>

<html lang="gl">
<head>
    <meta charset="UTF-8">
    <title>Exemplo</title>
</head>
<body>
    <p>Sesión iniciada.</p>
</body>
</html>

Non debemos facer primeiro:

echo 'Ola';

session_start();

xa que PHP necesita poder enviar as cabeceiras HTTP asociadas á cookie da sesión.


Usar variables de sesión

Unha vez iniciada a sesión podemos crear variables:

$_SESSION['nome'] = 'Angel';
$_SESSION['idade'] = 25;
$_SESSION['autenticado'] = true;

E recuperalas noutra páxina:

<?php
session_start();

echo $_SESSION['nome'];
?>

É recomendable comprobar previamente que unha variable existe:

<?php
session_start();

if (isset($_SESSION['nome'])) {
echo $_SESSION['nome'];
}
?>

Tamén podemos utilizar o operador ??:

<?php
session_start();

$nome = $_SESSION['nome'] ?? 'Descoñecido';

echo $nome;
?>


Exemplo: contador de visitas

 1 <?php
 2 session_start();
 3 
 4 if (isset($_SESSION['visitas'])) {
 5 $_SESSION['visitas']++;
 6 } else {
 7 $_SESSION['visitas'] = 1;
 8 }
 9 
10 echo 'Número de visitas nesta sesión: ' . $_SESSION['visitas'];
11 ?>

Cada usuario terá o seu propio valor de $_SESSION['visitas'] asociado ao seu identificador de sesión.


Consultar o identificador da sesión

Podemos obter o identificador da sesión actual mediante:

session_id();

Por exemplo:

<?php
session_start();

echo session_id();
?>

Non debemos mostrar o identificador de sesión nunha aplicación real.

Neste exemplo faise unicamente con fins didácticos.


Borrar datos dunha sesión

Existen diferentes formas de eliminar información dunha sesión.

Eliminar unha variable

Podemos eliminar unha variable concreta mediante:

unset($_SESSION['nome']);

Por exemplo:

<?php
session_start();

unset($_SESSION['nome']);
?>

Isto elimina unicamente esa variable.


Eliminar todas as variables

Podemos baleirar $_SESSION:

$_SESSION = [];

Tamén existe:

session_unset();

que elimina todas as variables da sesión actualmente cargadas.

Por exemplo:

<?php
session_start();

session_unset();
?>

Na práctica, a forma:

$_SESSION = [];

é moi clara e habitual.


Destruír a sesión

A función:

session_destroy();

elimina os datos almacenados no servidor asociados á sesión actual.

Por exemplo:

<?php
session_start();

session_destroy();
?>

Importante: session_destroy():

  • elimina os datos da sesión almacenados no servidor;
  • non elimina automaticamente o contido actual do array $_SESSION;
  • non elimina automaticamente a cookie de sesión do navegador.

Por iso, nun proceso de logout normalmente queremos realizar as tres operacións.


Pechar completamente unha sesión

Un exemplo básico para pechar a sesión dun usuario é:

 1 <?php
 2 session_start();
 3 
 4 // Eliminamos todas as variables de sesión.
 5 $_SESSION = [];
 6 
 7 // Eliminamos tamén a cookie que contén o ID da sesión.
 8 if (ini_get('session.use_cookies')) {
 9 $params = session_get_cookie_params();
10 
11 ```
12 setcookie(
13     session_name(),
14     '',
15     time() - 42000,
16     $params['path'],
17     $params['domain'],
18     $params['secure'],
19     $params['httponly']
20 );
21 ```
22 
23 }
24 
25 // Eliminamos os datos da sesión almacenados no servidor.
26 session_destroy();
27 ?>

Deste xeito:

  1. elimínanse as variables de $_SESSION;
  1. elimínase a cookie que identifica a sesión;
  1. elimínanse os datos gardados no servidor.

Nas aplicacións complexas poden existir peticións simultáneas, polo que a destrución inmediata dunha sesión debe xestionarse tendo en conta posibles condicións de carreira.

Máis información: session_destroy()


Seguridade das sesións

O identificador de sesión funciona como unha credencial temporal.

Se un atacante consegue obter un identificador de sesión válido podería intentar utilizalo para facerse pasar polo usuario.

Por iso debemos protexer correctamente a cookie da sesión.

Algunhas recomendacións son:

  • utilizar sempre HTTPS en produción;
  • activar session.cookie_secure;
  • activar session.cookie_httponly;
  • utilizar session.cookie_samesite;
  • activar session.use_only_cookies;
  • manter session.use_trans_sid desactivado;
  • activar session.use_strict_mode;
  • rexenerar o identificador de sesión cando cambian os privilexios do usuario;
  • implementar un tempo máximo de inactividade na propia aplicación cando sexa necesario.


Non enviar o SID pola URL

Non debemos utilizar URLs deste tipo:

https://exemplo.com/paxina.php?PHPSESSID=abc123...

O identificador podería aparecer:

  • no historial do navegador;
  • en logs do servidor;
  • en ferramentas de análise;
  • en capturas de pantalla;
  • en enlaces compartidos;
  • na cabeceira HTTP Referer en determinadas situacións.

Por iso PHP debe utilizar cookies para transportar o SID.

En PHP 8.5:

session.use_only_cookies = 1
session.use_trans_sid = 0


Fixación de sesión

Nun ataque de fixación de sesión un atacante intenta obrigar á vítima a utilizar un identificador de sesión previamente coñecido polo atacante.

Unha medida importante para reducir este risco é:

session.use_strict_mode = 1

Ademais, cando un usuario se autentica correctamente deberíase rexenerar o identificador da sesión.

Por exemplo:

<?php
session_start();

// Supoñemos que xa comprobamos usuario e contrasinal.

session_regenerate_id();

$_SESSION['usuario_id'] = $usuarioId;
$_SESSION['autenticado'] = true;
?>

É recomendable rexenerar o identificador antes de almacenar na nova sesión os privilexios obtidos tras a autenticación.

Máis información: session_regenerate_id()


Exemplo completo

Vexamos un exemplo moi simple utilizando tres páxinas.

Arquivo UD4_session_Ex1_paxina1.php

 1 <?php
 2 session_start();
 3 
 4 $_SESSION['nome'] = 'Angel';
 5 ?>
 6 
 7 <!DOCTYPE html>
 8 
 9 <html lang="gl">
10 <head>
11     <meta charset="UTF-8">
12     <title>Sesións - Páxina 1</title>
13 </head>
14 
15 <body>
16 
17 <h1>Asignouse o valor Angel á variable de sesión 'nome'.</h1>
18 
19 <h2>
20     <a href="UD4_session_Ex1_paxina2.php">
21         Ver o seu valor noutra páxina
22     </a>
23 </h2>
24 
25 </body>
26 </html>

Php session 1.jpg


Arquivo UD4_session_Ex1_paxina2.php

 1 <?php
 2 session_start();
 3 
 4 $nome = $_SESSION['nome'] ?? 'Non existe';
 5 ?>
 6 
 7 <!DOCTYPE html>
 8 
 9 <html lang="gl">
10 <head>
11     <meta charset="UTF-8">
12     <title>Sesións - Páxina 2</title>
13 </head>
14 
15 <body>
16 
17 <h1>
18     O valor da variable de sesión é:
19     <?= htmlspecialchars($nome, ENT_QUOTES, 'UTF-8') ?>
20 </h1>
21 
22 <p>
23     ID de sesión:
24     <?= htmlspecialchars(session_id(), ENT_QUOTES, 'UTF-8') ?>
25 </p>
26 
27 <p>
28     <strong>
29         O identificador móstrase unicamente con fins didácticos.
30         Nunha aplicación real non debería mostrarse ao usuario.
31     </strong>
32 </p>
33 
34 <h2>
35     <a href="UD4_session_Ex1_paxina3.php">
36         Borrar a sesión
37     </a>
38 </h2>
39 
40 </body>
41 </html>

Php session 2.jpg


Arquivo UD4_session_Ex1_paxina3.php

 1 <?php
 2 session_start();
 3 
 4 $_SESSION = [];
 5 
 6 if (ini_get('session.use_cookies')) {
 7 $params = session_get_cookie_params();
 8 
 9 ```
10 setcookie(
11     session_name(),
12     '',
13     time() - 42000,
14     $params['path'],
15     $params['domain'],
16     $params['secure'],
17     $params['httponly']
18 );
19 ```
20 
21 }
22 
23 session_destroy();
24 ?>
25 
26 <!DOCTYPE html>
27 
28 <html lang="gl">
29 <head>
30     <meta charset="UTF-8">
31     <title>Sesións - Páxina 3</title>
32 </head>
33 
34 <body>
35 
36 <h1>A sesión foi eliminada.</h1>
37 
38 <h2>
39     <a href="UD4_session_Ex1_paxina1.php">
40         Volver ao inicio
41     </a>
42 </h2>
43 
44 </body>
45 </html>

Php session 3.jpg


Comprobar a cookie da sesión

Podemos comprobar a cookie enviada por PHP utilizando as ferramentas de desenvolvemento do navegador.

Por exemplo, en Firefox ou Chrome:

  1. Abrir as Ferramentas de desenvolvemento.
  1. Acceder á sección de almacenamento ou aplicación.
  1. Seleccionar Cookies.
  1. Buscar a cookie chamada normalmente PHPSESSID.

Tamén podemos observar a cabeceira HTTP enviada polo servidor:

Set-Cookie: PHPSESSID=...

Nunha aplicación correctamente configurada mediante HTTPS poderemos atopar atributos como:

Secure
HttpOnly
SameSite=Lax

Non se debe copiar nin compartir o valor dunha cookie de sesión activa.


Ataques CSRF (Cross-Site Request Forgery)

Un ataque CSRF intenta conseguir que un navegador autenticado realice unha operación nun sitio web sen que o usuario teña intención de facela.

Por exemplo, un usuario podería estar autenticado en:

https://banco.exemplo

e visitar posteriormente unha páxina maliciosa que intente provocar unha petición contra ese sitio.

Como o navegador pode enviar automaticamente as cookies asociadas ao dominio, o servidor podería recibir unha petición acompañada dunha sesión válida.

Unha das técnicas máis utilizadas para protexer formularios é utilizar un token CSRF.


Xerar o token

Podemos utilizar random_bytes() para xerar un valor aleatorio criptograficamente seguro.

<?php
session_start();

if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
?>

O token almacénase na sesión do usuario.


Incorporar o token no formulario

Incluímos o token nun campo oculto:

<form action="procesar.php" method="post">

```
<!-- Outros campos do formulario -->

<input
    type="hidden"
    name="csrf_token"
    value="<?= htmlspecialchars(
        $_SESSION['csrf_token'],
        ENT_QUOTES,
        'UTF-8'
    ) ?>"
>

<input type="submit" value="Enviar">
```

</form>


Verificar o token

Ao recibir o formulario comprobamos que:

  • existe un token almacenado na sesión;
  • recibimos un token no formulario;
  • ambos coinciden.

Para comparar tokens é preferible utilizar hash_equals().

<?php
session_start();

if (
!isset($_SESSION['csrf_token'], $_POST['csrf_token']) ||
!is_string($_POST['csrf_token']) ||
!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])
) {
http_response_code(403);
exit('Acción non autorizada');
}

// O formulario é válido.
// Procesamos os datos...
?>

hash_equals() realiza a comparación intentando evitar que o tempo empregado na operación revele información sobre o token.


Invalidar o token

Se queremos que o token só poida utilizarse unha vez:

unset($_SESSION['csrf_token']);

Un novo formulario deberá xerar posteriormente un novo token.


SameSite e CSRF

Configurar:

session.cookie_samesite = Lax

ou, cando sexa posible:

session.cookie_samesite = Strict

axuda a reducir determinados ataques CSRF ao limitar o envío da cookie en peticións iniciadas dende outros sitios.

Porén, SameSite non debe considerarse un substituto universal dos tokens CSRF.

Nas operacións que modifican datos é recomendable combinar varias medidas:

  • utilizar métodos HTTP apropiados, especialmente POST;
  • utilizar tokens CSRF;
  • configurar correctamente SameSite;
  • comprobar os permisos do usuario;
  • utilizar HTTPS.


Novidades relacionadas coas sesións en PHP 8.5

PHP 8.5 incorpora soporte directo para cookies particionadas.

As seguintes funcións admiten agora a opción:

partitioned

entre as opcións da cookie:

  • session_start();
  • session_set_cookie_params();
  • session_get_cookie_params();
  • setcookie();
  • setrawcookie().

Por exemplo:

<?php
session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax',
    'partitioned' => true
]);

session_start();
?>

As cookies particionadas están pensadas principalmente para situacións nas que unha aplicación utiliza cookies nun contexto entre sitios.

Para unha aplicación web convencional normalmente:

session.cookie_partitioned = 0

é suficiente.

Máis información: Novidades de PHP 8.5


Resumo

  • As sesións permiten manter datos entre diferentes peticións HTTP.
  • Os datos das variables de sesión almacénanse normalmente no servidor.
  • O navegador conserva un identificador de sesión mediante unha cookie.
  • Para utilizar $_SESSION debemos iniciar ou recuperar a sesión mediante:

:

  session_start();
  • Para crear unha variable:

:

  $_SESSION['nome'] = 'Angel';
  • Para eliminar unha variable:

:

  unset($_SESSION['nome']);
  • Para eliminar todas as variables:

:

  $_SESSION = [];
  • Para destruír os datos da sesión:

:

  session_destroy();
  • session.gc_maxlifetime non representa un temporizador exacto de peche da sesión.
  • En aplicacións reais debemos protexer o identificador de sesión mediante HTTPS, cookies seguras e configuración apropiada.
  • Recoméndase activar:

:

  session.use_strict_mode = 1
  session.use_only_cookies = 1
  session.cookie_secure = 1
  session.cookie_httponly = 1
  session.cookie_samesite = Lax
  • O SID non debe enviarse mediante a URL.
  • Despois dunha autenticación ou dun cambio de privilexios é recomendable rexenerar o identificador mediante:

:

  session_regenerate_id();
  • Para protexer formularios fronte a CSRF podemos empregar tokens xerados con:

:

  bin2hex(random_bytes(32));

e comparalos mediante:

hash_equals();


Exercicios propostos


Referencias