Diferencia entre revisiones de «PHP Sesións»
| (No se muestran 14 ediciones intermedias del mismo usuario) | |||
| Línea 1: | Línea 1: | ||
==Concepto== | ==Concepto== | ||
| − | * Unha sesión ' | + | * 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: | ||
| + | :<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 /> | ||
| − | |||
| − | |||
| − | |||
| + | ===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: | ||
| + | |||
| + | <syntaxhighlight lang="ini"> | ||
| + | session.gc_maxlifetime = 1440 | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | 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'''. | |
| − | |||
| − | + | <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''. | |
| + | A execución deste mecanismo depende tamén de: | ||
| − | + | <syntaxhighlight lang="ini"> | |
| − | + | session.gc_probability | |
| − | + | session.gc_divisor | |
| − | + | </syntaxhighlight> | |
| − | |||
| + | Por exemplo, cos valores predeterminados: | ||
| − | + | <syntaxhighlight lang="ini"> | |
| + | session.gc_probability = 1 | ||
| + | session.gc_divisor = 100 | ||
| + | </syntaxhighlight> | ||
| + | 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: | |
| − | |||
| − | + | <syntaxhighlight lang="php"> | |
| − | |||
<?php | <?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(); | ||
| + | ?> </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== | ||
| + | |||
| + | ===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> |
| − | :: | + | |
| − | : | + | 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['nome'] = 'Angel'; | |
?> | ?> | ||
| − | |||
| − | + | <!DOCTYPE html> | |
| − | ::: | + | |
| − | + | <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 | <?php | ||
session_start(); | 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 | ||
| + | session_start(); | ||
| + | |||
| + | unset($_SESSION['nome']); | ||
| + | ?> </syntaxhighlight> | ||
| + | |||
| + | 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(); | ||
| − | + | </syntaxhighlight> | |
| − | + | ||
| − | </syntaxhighlight> | + | elimina os datos almacenados no servidor asociados á sesión actual. |
| + | Por exemplo: | ||
| − | + | <syntaxhighlight lang="php"> | |
| − | |||
<?php | <?php | ||
| − | + | 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 /> | ||
| − | + | ===Pechar completamente unha sesión=== | |
| − | + | ||
| + | Un exemplo básico para pechar a sesión dun usuario é: | ||
| + | |||
| + | <syntaxhighlight lang="php" line> | ||
<?php | <?php | ||
session_start(); | session_start(); | ||
| − | ... | + | |
| − | + | // 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(); | ||
| − | + | ?> </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. | ||
| − | + | Por exemplo: | |
| − | + | <syntaxhighlight lang="php"> | |
| − | |||
<?php | <?php | ||
| − | + | session_start(); | |
| − | ?> | + | |
| − | < | + | // Supoñemos que xa comprobamos usuario e contrasinal. |
| − | + | ||
| + | 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=== | ||
| − | < | + | <syntaxhighlight lang="php" line> |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
<?php | <?php | ||
| − | + | session_start(); | |
| + | $_SESSION['nome'] = 'Angel'; | ||
?> | ?> | ||
| − | + | ||
| − | + | <!DOCTYPE html> | |
| − | + | ||
| − | + | <html lang="gl"> | |
| − | + | <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 142: | Línea 651: | ||
[[Imagen:Php_session_1.jpg|400px]] | [[Imagen:Php_session_1.jpg|400px]] | ||
| − | + | <br /> | |
| − | + | ||
| + | ===Arquivo UD4_session_Ex1_paxina2.php=== | ||
| + | |||
| + | <syntaxhighlight lang="php" line> | ||
<?php | <?php | ||
| − | + | session_start(); | |
| + | |||
| + | $nome = $_SESSION['nome'] ?? 'Non existe'; | ||
?> | ?> | ||
| − | |||
| − | |||
| − | <html> | + | <!DOCTYPE html> |
| − | + | ||
| − | + | <html lang="gl"> | |
| − | + | <head> | |
| − | + | <meta charset="UTF-8"> | |
| − | + | <title>Sesións - Páxina 2</title> | |
| − | + | </head> | |
| − | + | ||
| − | + | <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> | |
| − | |||
| − | |||
| − | |||
</html> | </html> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Línea 169: | Línea 701: | ||
[[Imagen:Php_session_2.jpg|400px]] | [[Imagen:Php_session_2.jpg|400px]] | ||
| + | <br /> | ||
| − | + | ===Arquivo UD4_session_Ex1_paxina3.php=== | |
| − | + | ||
| + | <syntaxhighlight lang="php" line> | ||
<?php | <?php | ||
| − | + | session_start(); | |
| + | $_SESSION = []; | ||
| − | + | if (ini_get('session.use_cookies')) { | |
| − | + | $params = session_get_cookie_params(); | |
| − | |||
| + | ``` | ||
| + | setcookie( | ||
| + | session_name(), | ||
| + | '', | ||
| + | time() - 42000, | ||
| + | $params['path'], | ||
| + | $params['domain'], | ||
| + | $params['secure'], | ||
| + | $params['httponly'] | ||
| + | ); | ||
| + | ``` | ||
| − | + | } | |
| − | |||
| + | session_destroy(); | ||
?> | ?> | ||
| − | |||
| − | |||
| − | <html> | + | <!DOCTYPE html> |
| − | + | ||
| − | + | <html lang="gl"> | |
| − | + | <head> | |
| − | + | <meta charset="UTF-8"> | |
| − | + | <title>Sesións - Páxina 3</title> | |
| − | + | </head> | |
| − | + | ||
| − | + | <body> | |
| + | |||
| + | <h1>A sesión foi eliminada.</h1> | ||
| + | |||
| + | <h2> | ||
| + | <a href="UD4_session_Ex1_paxina1.php"> | ||
| + | Volver ao inicio | ||
| + | </a> | ||
| + | </h2> | ||
| − | + | </body> | |
| − | |||
| − | |||
| − | |||
</html> | </html> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Línea 207: | Línea 755: | ||
[[Imagen:Php_session_3.jpg|400px]] | [[Imagen:Php_session_3.jpg|400px]] | ||
| + | <br /> | ||
| + | |||
| + | ==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: | |
| − | + | # Abrir as '''Ferramentas de desenvolvemento'''. | |
| − | |||
| − | |||
| − | |||
| − | + | # Acceder á sección de almacenamento ou aplicación. | |
| − | |||
| − | + | # Seleccionar '''Cookies'''. | |
| − | + | # 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 /> | ||
| Línea 232: | Línea 791: | ||
==Ataques CSRF (Cross-Site Request Forgery)== | ==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: | ||
| + | |||
| + | <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 /> | ||
| − | + | ===Xerar o token=== | |
| − | + | Podemos utilizar <code>random_bytes()</code> para xerar un valor aleatorio criptograficamente seguro. | |
| − | + | <syntaxhighlight lang="php"> | |
| − | + | <?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: | ||
| − | + | <syntaxhighlight lang="php"> | |
| − | |||
<form action="procesar.php" method="post"> | <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> | </form> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| + | <br /> | ||
| + | |||
| + | ===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 <code>hash_equals()</code>. | ||
| + | |||
| + | <syntaxhighlight lang="php"> | ||
| + | <?php | ||
session_start(); | session_start(); | ||
| − | if ($_POST['csrf_token'] ! | + | |
| − | + | 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... | ||
| + | ?> </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> | </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> | ||
| + | |||
| + | 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 | ||
| + | ]); | ||
| + | |||
| + | session_start(); | ||
| + | ?> </syntaxhighlight> | ||
| + | |||
| + | 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== | ||
| − | + | * [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
Sumario
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
0significa 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
LaxeStrict. session.cookie_samesite = Lax
Laxadoita 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:
- comproba se o navegador enviou unha cookie co identificador da sesión;
- se a sesión existe, carga os seus datos en
$_SESSION;
- 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:
- elimínanse as variables de
$_SESSION;
- 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: 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_siddesactivado; - 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
Refereren 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>
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>
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>
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:
- Abrir as Ferramentas de desenvolvemento.
- Acceder á sección de almacenamento ou aplicación.
- Seleccionar Cookies.
- 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
$_SESSIONdebemos 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_maxlifetimenon 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
- Podedes facer este conxunto de exercicios.