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:
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.
Esta protección puede personalizarse creando una clase que implemente la VerifySessionCsrfInterface
.
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:
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
.
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).
El servicio se inicia en el archivo
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.
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:
{% 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
.
\Ubiquity\security\data\EncryptionManager::start($config);
Nota
Por defecto, el cifrado se realiza en AES-128
.
Cambiando el cifrado:
Actualización a AES-256:
\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:
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
.
\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..
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
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]);
}
}
}