Docs / rApanel / Seguridad

Endurecer la seguridad de tu rApanel

El instalador deja rApanel funcionando tanto en HTTP como en HTTPS, porque no puede saber de antemano si vas a poner un certificado. Eso significa que algunas protecciones vienen desactivadas a propósito: activarlas antes de tiempo dejaría a todo el mundo — tú incluido — sin poder iniciar sesión. Esta guía las recorre una por una, en el orden correcto.

No hace falta que audites nada a mano. El panel trae Admin → Health Check, que revisa todo lo de esta página sobre tu instalación real y te muestra qué falta. Cada aviso tiene un botón Guía que te trae exactamente a la sección correspondiente.

1 · HTTPS #

HTTPS El sitio funciona por HTTP: las contraseñas y las cookies de sesión viajan sin cifrar. Aviso

Es el primer paso y el que habilita todos los demás. Sin él, las contraseñas de tus jugadores viajan en texto plano por la red: cualquiera en el camino — el wifi del café, el proveedor de internet, un router intermedio — puede leerlas. Los certificados son gratis con Let's Encrypt.

Antes de empezar, comprueba que el registro A de tu dominio apunte a la IP pública de este servidor (tanto tudominio.com como www.tudominio.com) y que los puertos 80 y 443 estén abiertos: Certbot valida por HTTP.

# Nginx
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d tudominio.com -d www.tudominio.com

# Apache2
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache -d tudominio.com -d www.tudominio.com

Cuando pregunte si redirigir HTTP a HTTPS, elige Redirect (opción 2). Después apunta el panel al nuevo esquema en su .env:

APP_URL=https://tudominio.com

Si usas la Consola en Vivo del admin, su WebSocket también tiene que pasar a wss:// — los navegadores bloquean ws:// desde páginas HTTPS:

RA_WS_PUBLIC_URL=wss://tudominio.com/ws-proxy
php artisan config:clear && php artisan cache:clear

Certbot deja instalado un temporizador que renueva solo. Puedes comprobarlo con sudo systemctl status certbot.timer y ensayar la renovación con sudo certbot renew --dry-run.

2 · Modo depuración #

Modo depuración Activado en una instalación de producción: cualquier página de error expone las credenciales de tu .env. Error

Con APP_DEBUG=true, cualquier error del panel muestra una página de diagnóstico con el contenido de tu .env: usuario y contraseña de la base de datos, APP_KEY, claves de las pasarelas de pago. Y no hace falta ser administrador para verla — basta con provocar el error.

APP_DEBUG=false
APP_ENV=production
php artisan config:clear

Si ya tuviste el panel público con APP_DEBUG=true, da por comprometidas esas credenciales: cambia la contraseña de la base de datos y las claves de las pasarelas que tuvieras configuradas.

3 · Cookie de sesión #

Cookie de sesión Se puede reforzar: SESSION_SECURE_COOKIE=true, el prefijo __Host-. Aviso

Enviarla solo por HTTPS

Evita que la cookie de sesión viaje alguna vez en claro, incluso si alguien entra por error a la versión HTTP del sitio.

SESSION_SECURE_COOKIE=true

Solo con el certificado ya funcionando. Si activas esto mientras el sitio sigue en HTTP, el navegador descarta la cookie y nadie podrá iniciar sesión, tú incluido. Si te pasa: vuelve a ponerlo en false y ejecuta php artisan config:clear.

El prefijo __Host-

Es lo que las auditorías externas reportan como «There is no Cookie Prefix on this cookie». El prefijo hace que el navegador rechace la cookie si alguien intenta escribirla desde un subdominio o por HTTP: cierra la puerta a que un subdominio comprometido — un foro viejo, un blog abandonado — le fije una sesión a tus usuarios.

El navegador solo lo acepta si se cumplen las tres condiciones a la vez (las dos últimas ya vienen así de fábrica):

SESSION_SECURE_COOKIE=true
SESSION_PATH=/
SESSION_DOMAIN=null
SESSION_COOKIE=__Host-mitienda-session
php artisan config:clear

El nombre que va después del prefijo lo eliges tú. Lo único obligatorio es que empiece por __Host-; el resto es un nombre cualquiera, y ni siquiera necesita terminar en -session. Estos tres son igual de válidos:

SESSION_COOKIE=__Host-mitienda-session
SESSION_COOKIE=__Host-roh
SESSION_COOKIE=__Host-mi_panel_2026

Usa solo letras, números, - y _ — nada de espacios, acentos ni =, ;, ,. Si no defines la variable, el nombre sale del APP_NAME de tu .env (por ejemplo mi-servidor-session), y por eso hay que escribirlo entero al agregar el prefijo.

Conviene que siga siendo reconocible: cuando estés depurando algo en las herramientas del navegador, agradecerás leer __Host-mitienda-session y no __Host-x7k2. Ofuscarlo no aporta seguridad — el panel se identifica por muchas otras vías.

Cambiar el nombre de la cookie cierra todas las sesiones abiertas, la tuya incluida. Hazlo en un momento de poco tráfico, vuelve a entrar, y luego no lo toques más: cada cambio vuelve a cerrarlas, porque para el navegador es una cookie distinta y no la misma renombrada.

4 · Permisos del archivo .env #

Permisos del .env Legible por cualquier usuario del sistema (644). Aviso

El .env guarda las credenciales de tus bases de datos y la APP_KEY con la que se cifran las sesiones. Si queda legible por todo el sistema, cualquier cuenta del servidor — o un script de otro sitio alojado en la misma máquina, algo habitual en hosting compartido — puede leerlo.

sudo chown root:www-data .env
sudo chmod 640 .env

No ejecutes solo el chmod. Si el grupo del archivo no es el del servidor web, le quitas el acceso a PHP y el panel entero responde 500. El chown de la primera línea es el que garantiza que siga leyéndolo.

En algunas distribuciones el usuario del servidor web no es www-data sino apache, nginx o www. Compruébalo con:

ps aux | grep -E 'php-fpm|apache' | head -2

El Health Check del panel ya mira el dueño real de tu archivo y te muestra el comando adaptado, así que puedes copiarlo tal cual desde ahí.

5 · Cabeceras de seguridad #

Cabeceras de seguridad Faltan en tu servidor web: Referrer-Policy, Permissions-Policy. Aviso

Son instrucciones que tu servidor web le da al navegador sobre cómo tratar el sitio. Las instalaciones nuevas ya las traen puestas; este aviso aparece sobre todo en paneles instalados con versiones anteriores, o cuando un proxy intermedio las descarta.

Nginx

Dentro del bloque server de /etc/nginx/sites-available/rapanel:

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
sudo nginx -t && sudo systemctl reload nginx

Apache

Dentro del <VirtualHost> de /etc/apache2/sites-available/rapanel.conf:

Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
sudo a2enmod headers
sudo apache2ctl configtest && sudo systemctl reload apache2

En nginx, declarar un add_header dentro de un location descarta todas las cabeceras heredadas del bloque server. Si añades una en un location, tienes que repetir ahí todas las demás.

6 · Content Security Policy #

Content Security Policy Se están enviando dos políticas a la vez (la del panel y la de tu servidor web). Error

El CSP es la defensa de fondo contra los ataques XSS: le dice al navegador exactamente de dónde puede cargar scripts, y bloquea todo lo demás. En rApanel no lo configuras tú — lo emite la propia aplicación en cada petición, con un valor único e irrepetible (un nonce) que marca sus scripts legítimos. Un atacante no puede adivinarlo.

No agregues una cabecera Content-Security-Policy en nginx ni en Apache. Si llegan dos políticas, el navegador aplica la intersección de ambas: se rompen cosas sin que aparezca ningún mensaje de error, y encontrar la causa cuesta horas.

Un servidor web no puede generar un valor distinto por petición, así que cualquier CSP escrito a mano ahí tiene que permitir todos los scripts inline ('unsafe-inline') — que es precisamente lo que un CSP debería impedir. Si el Health Check te avisa de esto, busca y elimina la línea de tu configuración:

sudo grep -rn "Content-Security-Policy" /etc/nginx/ /etc/apache2/

Si en cambio el aviso dice que no está llegando al navegador, el culpable suele ser un proxy o un CDN intermedio que la descarta.

7 · HSTS con preload #

El instalador ya envía Strict-Transport-Security con max-age=31536000; includeSubDomains, que es lo correcto para la mayoría de los servidores. Si además quieres entrar en la lista de precarga que traen los navegadores de fábrica, añade preload a esa cabecera y date de alta en hstspreload.org.

Esto es prácticamente irreversible. Una vez en la lista, los navegadores se niegan a abrir tu dominio y todos sus subdominios por HTTP durante meses, aunque quites la cabecera. Hazlo solo si estás seguro de que todo lo que cuelga de tu dominio irá por HTTPS para siempre.

8 · Lo que las auditorías marcan y no es un problema #

Si pasas tu dominio por securityheaders.com o por el HTTP Observatory de MDN, vas a ver tres avisos que no tienes que arreglar:

XSRF-TOKEN sin HttpOnly

Es intencional y es cómo funciona Laravel: el JavaScript del panel necesita leer esa cookie para enviar el token anti-CSRF en cada petición. Si le pusieras HttpOnly, el panel dejaría de funcionar. La cookie de sesión, que es la que da acceso, sí lo tiene.

X-XSS-Protection obsoleta

Es una cabecera antigua que los navegadores modernos ignoran; se envía solo por compatibilidad con versiones viejas. La protección real la da el Content Security Policy.

Cabeceras Cross-Origin-* en «Upcoming Headers»

Son opcionales y todavía están en adopción. Su ausencia no baja la nota, y activarlas sin cuidado puede romper la carga de imágenes o fuentes externas.

Con los puntos 1 a 6 aplicados, una instalación de rApanel obtiene A+ en securityheaders.com y pasa el análisis de CSP del Observatory de MDN.