Módulo de seguridad

Instalación

Instale el módulo Ubiquity-security desde la línea de comandos o desde Webtools (parte Composer).

composer require phpmv/ubiquity-security

A continuación, active la visualización de la parte Seguridad en el Webtools:

../_images/display-security.png

CSRF de Sesión

La sesión está protegida por defecto contra ataques CSRF mediante la clase VerifyCsrfToken (incluso sin el módulo Ubiquity-security). |Se genera una instancia del token (CSRFToken) al iniciar la sesión. La validez del token se comprueba mediante una cookie en cada petición.

../_images/security-part.png

Esta protección puede personalizarse creando una clase que implemente la VerifySessionCsrfInterface.

app/session/MyCsrfProtection.php
class MyCsrfProtection implements VerifySessionCsrfInterface {
   private AbstractSession $sessionInstance;

   public function __construct(AbstractSession $sessionInstance) {
      $this->sessionInstance = $sessionInstance;
   }

   public function init() {
      //TODO when the session starts
   }

   public function clear() {
      //TODO when the session ends
   }

   public function start() {
      //TODO When the session starts or is resumed
   }

   public static function getLevel() {
      return 1; //An integer to appreciate the level of security
   }
}

Iniciar la protección personalizada en los servicios:

app/config/services.php
use Ubiquity\utils\http\session\PhpSession;
use Ubiquity\controllers\Startup;
use app\session\MyCsrfProtection;

Startup::setSessionInstance(new PhpSession(new MyCsrfProtection()));

Desactivar la protección

Si no necesita proteger su sesión contra ataques Csrf, inicie la sesión con la clase NoCsrfProtection.

app/config/services.php
use Ubiquity\utils\http\session\PhpSession;
use Ubiquity\controllers\Startup;
use Ubiquity\utils\http\session\protection\NoCsrfProtection;

Startup::setSessionInstance(new PhpSession(new NoCsrfProtection()));

Gestor de CSRF

El servicio CsrfManager puede iniciarse directamente desde la interfaz webtools.
Su papel es proporcionar herramientas para proteger las rutas sensibles de los ataques Csrf (los que permiten la validación de formularios por ejemplo).

../_images/csrf-manager-started.png
  • El servicio se inicia en el archivo services.php.

app/config/services.php
 \Ubiquity\security\csrf\CsrfManager::start();

Ejemplo de protección de formularios:

La vista del formulario:

<form id="frm-bar" action='/submit' method='post'>
   {{ csrf('frm-bar') }}
   <input type='text' id='sensitiveData' name='sensitiveData'>
</form>

El método csrf genera un token para el formulario (Añadiendo un campo oculto en el formulario correspondiente al token).

El formulario de envío en un controlador:

use Ubiquity\security\csrf\UCsrfHttp;

#[Post('/submit')]
public function submit(){
   if(UCsrfHttp::isValidPost('frm-bar')){
      //Token is valid! => do something with post datas
   }
}

Nota

También es posible gestionar esta protección mediante cookies.

Ejemplo de protección con ajax:

El meta campo csrf-token se genera en todas las páginas.

app/controllers/BaseController.php
abstract class ControllerBase extends Controller{
   protected $headerView = "@activeTheme/main/vHeader.html";
   protected $footerView = "@activeTheme/main/vFooter.html";

   public function initialize() {
      if (! URequest::isAjax ()) {
         $meta=UCsrfHttp::getTokenMeta('postAjax');
         $this->loadView ( $this->headerView,['meta'=>$meta] );
      }
   }
}

Este campo se añade en el headerView:

app/views/main/vHeader.html
{% block header %}
   <base href="{{config["siteUrl"]}}">
   <meta charset="UTF-8">
   <link rel="icon" href="data:;base64,iVBORw0KGgo=">
   {{meta | raw}}
   <title>Tests</title>
{% endblock %}

Ejemplo con un botón que envía datos vía ajax. El parámetro csrf se establece en true. Así que cuando se envía la solicitud, el csrf-token se envía en las cabeceras de la solicitud.

#[Get(path: "/ajax")]
public function ajax(){
   $this->jquery->postOnClick('#bt','/postAjax','{id:55}','#myResponse',['csrf'=>true]);
   $this->jquery->renderDefaultView();
}

La ruta de envío puede comprobar la presencia y validez del token:

#[Post(path: "postAjax")]
public function postAjax(){
   if(UCsrfHttp::isValidMeta('postAjax')){
      var_dump($_POST);
   }else{
      echo 'invalid or absent meta csrf-token';
   }
}

Gestor de cifrado

El servicio EncryptionManager puede iniciarse directamente desde la interfaz webtools.

  • En este caso, se genera una clave en el archivo de configuración app/config/config.php.

  • El servicio se inicia en el archivo services.php.

app/config/services.php
 \Ubiquity\security\data\EncryptionManager::start($config);

Nota

Por defecto, el cifrado se realiza en AES-128.

../_images/encryption-manager-started.png

Cambiando el cifrado:

Actualización a AES-256:

app/config/services.php
\Ubiquity\security\data\EncryptionManager::startProd($config, Encryption::AES256);

Generar una nueva clave:

Ubiquity new:key 256

La nueva clave se genera en el archivo app/config/config.php.

Modelo de encriptación de datos

El transformador Crypt también puede utilizarse en los miembros de un modelo:

app/models/User.php
 class Foo{
     #[Transformer(name: "crypt")]
     private $secret;
     ...
 }

Uso:

$o=new Foo();
$o->setSecret('bar');
TransformersManager::transformInstance($o);// secret member is encrypted

Cifrado genérico de datos

Cifrado de cadenas:

$encryptedBar=EncryptionManager::encryptString('bar');

Para luego desencriptarlo:

echo EncryptionManager::decryptString($encryptedBar);

Es posible cifrar cualquier tipo de datos:

$encryptedUser=EncryptionManager::encrypt($user);

Para luego desencriptarlo, con posible serialización/deserialización si se trata de un objeto:

$user=EncryptionManager::decrypt($encryptedUser);

Gestor de políticas de seguridad de contenidos

El servicio ContentSecurityManager puede iniciarse directamente desde la interfaz webtools.

  • El servicio se inicia en el archivo services.php.

app/config/services.php
\Ubiquity\security\csp\ContentSecurityManager::start(reportOnly: true,onNonce: function($name,$value){
     if($name==='jsUtils') {
             \Ubiquity\security\csp\ContentSecurityManager::defaultUbiquityDebug()->addNonce($value, \Ubiquity\security\csp\CspDirectives::SCRIPT_SRC)->addHeaderToResponse();
     }
});

Nota

Con esta configuración por defecto, se añade un nonce a los scripts jquery generados con phpmv-ui. El control de CSP se realiza en el modo Report-only..

../_images/csp-manager-started.png

Añadir un nonce

Ejemplo de adición de nonce en las páginas de cabecera y pie de página:

Actualización del controlador de base

app/controllers/ControllerBase.php
namespace controllers;

use Ubiquity\controllers\Controller;
use Ubiquity\security\csp\ContentSecurityManager;
use Ubiquity\utils\http\URequest;

/**
 * controllers$ControllerBase
 */
abstract class ControllerBase extends Controller {

     protected $headerView = "@activeTheme/main/vHeader.html";

     protected $footerView = "@activeTheme/main/vFooter.html";

     protected $nonce;

     public function initialize() {
             $this->nonce=ContentSecurityManager::getNonce('jsUtils');
             if (! URequest::isAjax()) {
                     $this->loadView($this->headerView,['nonce'=>$this->nonce]);
             }
     }

     public function finalize() {
             if (! URequest::isAjax()) {
                     $this->loadView($this->footerView,['nonce'=>$this->nonce]);
             }
     }
}

Gestión de contraseñas

Tokens de usuario