Repository navigation
Cifrar secretos con la clave de la instalación (AppKey) - #2058
Conversation
AppKey firma tokens con FS_APP_KEY, pero no había una forma común de cifrar los secretos que hay que guardar (contraseñas de certificados, de buzones de correo, etc.) y cada plugin los guardaba en claro o con su propio esquema. - AppKey::encrypt() y decrypt(): AES-256-GCM con una subclave de FS_APP_KEY derivada con HKDF-SHA256 y el propósito, y un contexto autenticado (por ejemplo, el id de la fila) para que el valor no sirva en otra fila. Formato "v1:" + base64(IV, etiqueta y texto cifrado). - AppKey::canEncrypt(): sin FS_APP_KEY no se cifra, porque la clave derivada cambia con los datos de conexión y se perdería lo guardado. - Tests de ida y vuelta, propósito, contexto, valores manipulados y formato.
|
@daniel89fg graciasss!!! porfin llegó!!! hacia falta desde hace tiempo hice esta pr por si te sirve, pero vamos que creo que es lo mismo que has hecho. es que en aquella epoca(del PR) necesitaba guardar unas keys de un servicio y claro las guarda en texto plano o se pueden hacer trucos, pero esto es mejor. para dejarlo seguro del todo se debería poder configurar FS_APP_KEY en las variables de entorno que ahí ya no puede acceder nadie aunque entren en el servidor. |
testWidgetSubcuentaSearchShowsSelectedFirst buscaba por el código de la
cuenta de prueba (9999) en el código y la descripción de las subcuentas
del ejercicio. Los clientes aleatorios de otros tests ("Empresa 19999")
dejan subcuentas cuya descripción puede contenerlo, y entonces el test
fallaba con una subcuenta de más (por ejemplo, 4300000010).
NeoRazorX
left a comment
There was a problem hiding this comment.
Revisado: correcto. phpcs limpio y AppKeyTest, MyFilesTokenTest y PanelControllerTest pasan en local con PHP 8.3 + MariaDB (sin FS_APP_KEY), PHP 8.1 + MySQL y PHP 8.4 + PostgreSQL. Añado después en master que canEncrypt() compruebe que está la extensión openssl.
Descripción
AppKeyfirma los tokens conFS_APP_KEY, pero no había una forma común de cifrar los secretos que hay que guardar: contraseñas de certificados, de buzones de correo, de servicios externos, etc. Cada plugin los guarda en claro o con su propio esquema. Esta PR añade aAppKeyel par que faltaba para cifrar y descifrar, con el mismo enfoque quesign()/verify(): un propósito que separa cada uso.No cambia nada de lo que ya existe: son tres métodos nuevos y sus tests.
Cambios realizados
AppKey::encrypt(string $purpose, string $plain, string $context = ''): ?string: AES-256-GCM con una subclave deFS_APP_KEYderivada con HKDF-SHA256 y el propósito (nunca conFS_APP_KEYtal cual, que firma los tokens). El contexto se autentica sin cifrarlo: por ejemplo, el id de la fila, para que el valor cifrado no sirva si se copia a otra fila. Formato:v1:+ base64(IV de 12 bytes ‖ etiqueta de 16 ‖ texto cifrado). El prefijo permite cambiar el formato en el futuro sin perder lo guardado.AppKey::decrypt(string $purpose, ?string $value, string $context = ''): ?string: devuelvenullsi el valor no tiene el formato, se ha modificado, ha cambiadoFS_APP_KEYo el propósito o el contexto no coinciden.AppKey::canEncrypt(): bool: sinFS_APP_KEYen elconfig.phpno se cifra (encrypt()devuelvenull), porque la clave derivada cambia al cambiar los datos de conexión y se perdería lo guardado. Quien lo use puede mostrar el avisoapp-key-missingque ya tiene el core.AppKeyTest: sin clave no cifra; ida y vuelta (también con un secreto vacío y con caracteres especiales); IV aleatorio; otro propósito, otro contexto, valor modificado, sin prefijo, corto, vacío onull; y un test que fija el formato (testEncryptFormat).Uso típico:
Ya lo usan, con este mismo formato, los plugins fiscales españoles (contraseñas de los certificados de empresa y del buzón IMAP de Facturae), que hoy llevan su propia copia. Con el mismo propósito, lo que ya han cifrado se descifra con
AppKey::decrypt(). Más adelante, el core podría cifrar con esto la contraseña del correo (ConfigEmail), que hoy se guarda en claro; eso sería otra PR.PanelControllerTest(commit aparte):testWidgetSubcuentaSearchShowsSelectedFirstbuscaba por el código de la cuenta de prueba (9999) en el código y la descripción de las subcuentas del ejercicio. Los clientes aleatorios de otros tests ("Empresa 19999") dejan subcuentas cuya descripción puede contenerlo, y el test fallaba con una de más (así falló la primera ejecución de la CI de esta PR, con4300000010). Ahora busca por una descripción única de sus subcuentas.Cómo probar
FS_APP_KEYen elconfig.php:AppKey::decrypt('test', AppKey::encrypt('test', 'secreto', '1'), '1')devuelvesecreto; con otro propósito o con otro contexto devuelvenull.FS_APP_KEY:AppKey::canEncrypt()esfalseyAppKey::encrypt()devuelvenull.AppKeyTest(testCanEncrypt,testEncryptAndDecrypt,testEncryptFormat). La batería completa del core pasa en PHP 8.1–8.5 con MySQL y PostgreSQL.