Arquitectura y seguridad en intranets modernas
Históricamente, una intranet era sinónimo de un servidor físico escondido detrás de un firewall corporativo, accesible solo si estabas en la oficina o conectado a través de una VPN lenta. Hoy, el trabajo remoto, la adopción de la nube y las expectativas de los usuarios han transformado ese concepto.
Muchas empresas construyen hoy sus intranets con frameworks modernos de aplicaciones de página única (SPA) como React o Angular, y las alojan en infraestructuras de nube pública como AWS, Azure, Vercel o Firebase. La pregunta entonces es cómo hacer que un portal web accesible públicamente a nivel de red siga siendo una intranet privada y segura. La respuesta está en la arquitectura Zero Trust (confianza cero) y en una gestión de identidad sólida.
Arquitectura de una intranet moderna
Cuando usamos React o Angular desplegamos aplicaciones estáticas (HTML, CSS y JS). Esas aplicaciones corren en el navegador del usuario, no en el servidor. Por eso la arquitectura típica se divide en tres capas:
| Capa | Función | Ejemplos |
|---|---|---|
| Presentación (Frontend SPA) | La aplicación alojada en un CDN público | CloudFront, Azure Static Web Apps |
| Identidad (IdP) | Maneja autenticación y autorización | Microsoft Entra ID, Okta, Auth0, Google Workspace |
| Datos y lógica (Backend/APIs) | Microservicios que solo responden con un token JWT válido | Servicios REST en la nube |
El portal web en sí está en Internet público, pero los datos están protegidos por capas de identidad y seguridad.
La identidad es el nuevo perímetro
En este modelo la red deja de ser el perímetro de seguridad: la identidad lo reemplaza.
- Autenticación obligatoria (OIDC/OAuth2): el SPA no debe mostrar ninguna información sensible hasta que el usuario inicie sesión a través del IdP de la empresa.
- JSON Web Tokens (JWT): una vez autenticado, el frontend recibe un token de acceso que debe adjuntar a cada petición hacia el backend.
- CORS y CSRF: los backends deben configurarse de manera estricta para aceptar peticiones solo desde el dominio exacto donde está alojada la intranet.
Ruta protegida en React
En React se usa un proveedor de autenticación (por ejemplo Auth0 o MSAL para Microsoft) para proteger las rutas. Una "ruta privada" redirige al usuario al login si no está autenticado.
// PrivateRoute.jsx (React)
import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import { useAuth0 } from '@auth0/auth0-react';
const PrivateRoute = () => {
const { isAuthenticated, isLoading } = useAuth0();
if (isLoading) {
return <div>Cargando Intranet...</div>;
}
// Si no esta autenticado, redirige al login.
// Si lo esta, renderiza el componente hijo (la Intranet)
return isAuthenticated ? <Outlet /> : <Navigate to="/login" replace />;
};
export default PrivateRoute;
Interceptor HTTP en Angular
En Angular no basta con proteger la ruta con un AuthGuard: cada petición HTTP que sale hacia los microservicios debe llevar el token de seguridad. Esto se logra con un interceptor HTTP.
// auth.interceptor.ts (Angular)
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = this.authService.getAccessToken();
if (token) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
return next.handle(request);
}
}
Validación del token en el backend
El frontend puede ser manipulado en el navegador del usuario, así que la verdadera seguridad reside en el backend, que debe validar matemáticamente la firma del JWT antes de devolver datos corporativos.
// authMiddleware.js (Node.js / Express)
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
// Cliente para obtener la llave publica del IdP (Auth0, Entra ID, etc.)
const client = jwksClient({
jwksUri: 'https://tu-tenant.auth0.com/.well-known/jwks.json'
});
function getKey(header, callback){
client.getSigningKey(header.kid, function(err, key) {
var signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
const verificarToken = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ message: 'Acceso Denegado' });
jwt.verify(token, getKey, { algorithms: ['RS256'] }, (err, decoded) => {
if (err) return res.status(401).json({ message: 'Token invalido corporativo' });
req.empleado = decoded;
next();
});
};
module.exports = verificarToken;
Medidas adicionales para intranets públicas
- Cifrado de extremo a extremo (TLS 1.3): todo el tráfico, sin excepción, debe ir por HTTPS.
- WAF (Web Application Firewall): colocar un WAF frente al CDN y los APIs para filtrar ataques como inyección SQL o DDoS.
- Prevención de indexación: al estar en Internet público hay que asegurarse de que los motores de búsqueda no indexen el portal. Se usa un archivo
robots.txtrestrictivo (Disallow: /) y la cabeceraX-Robots-Tag: noindex, nofollow.
Conclusión
Construir una intranet corporativa con React o Angular alojada en infraestructura pública es viable y recomendable por su escalabilidad y rendimiento. Requiere, eso sí, un cambio de mentalidad: abandonar la falsa sensación de seguridad de las redes privadas y adoptar un enfoque Zero Trust, donde el código delega la autenticación a proveedores de identidad robustos y el backend nunca confía en el frontend, verificando cada solicitud de forma individual.
© Arturo Juárez Monroy