Cross-Site Scripting (XSS):
Cross-Site Request Forgery (CSRF):
Inyección de Código:
Angular tiene un enfoque sólido para mitigar las vulnerabilidades XSS a través del data binding y la sanitización.
<!-- Seguro: Angular escapa los valores por defecto -->
<div></div>
import { Component } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
@Component({
selector: 'app-example',
template: `<div [innerHTML]="sanitizedContent"></div>`
})
export class ExampleComponent {
sanitizedContent: any;
constructor(private sanitizer: DomSanitizer) {
const unsafeHtml = '<script>alert("XSS")</script>';
this.sanitizedContent = this.sanitizer.bypassSecurityTrustHtml(unsafeHtml);
}
}
Para proteger contra ataques CSRF, Angular utiliza un mecanismo de tokens. La biblioteca Angular realiza automáticamente el envío de un token de CSRF en cada solicitud HTTP. Esto se gestiona en la configuración del servidor y requiere que el servidor valide el token.
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient) {}
realizarPeticion() {
return this.http.get('/api/endpoint'); // El token CSRF se enviará automáticamente
}
}
La seguridad también implica garantizar que solo los usuarios autenticados y autorizados tengan acceso a ciertas partes de la aplicación.
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
const isAuthenticated = // lógica de autenticación aquí
if (!isAuthenticated) {
this.router.navigate(['/login']);
return false;
}
return true;
}
}
Mejoras en la seguridad.
La accesibilidad se basa en varios principios, conocidos como los principios POUR:
Perceptible: La información y los componentes de la interfaz de usuario deben ser presentados de manera que puedan ser percibidos por todos los usuarios.
Operable: Los componentes de la interfaz deben ser operables por todos los usuarios, independientemente de sus habilidades.
Comprensible: La información y la operación de la interfaz deben ser comprensibles.
Robusto: El contenido debe ser robusto para que pueda ser interpretado por una amplia variedad de agentes de usuario, incluidos los lectores de pantalla.
Prácticas de accesibilidad en Angular.
Ejemplo:
<button aria-label="Cerrar" (click)="cerrar()">
<span aria-hidden="true">✖</span>
</button>
En este ejemplo, el atributo aria-label proporciona una descripción del botón para los lectores de pantalla.
Ejemplo:
<a href="#" (keydown.enter)="accion()" tabindex="0">Acción</a>
El uso de tabindex permite que el enlace sea accesible mediante la tecla de tabulación.
Ejemplo:
<form [formGroup]="miFormulario">
<input formControlName="nombre" aria-describedby="nombreAyuda">
<div *ngIf="miFormulario.get('nombre').invalid && miFormulario.get('nombre').touched">
<small id="nombreAyuda">El nombre es obligatorio.</small>
</div>
</form>
El uso de aria-describedby ayuda a los lectores de pantalla a conectar la entrada con su mensaje de error.
Ejemplo:
<label for="email">Correo Electrónico</label>
<input type="email" id="email" aria-required="true">
Es fundamental realizar pruebas de accesibilidad en tu aplicación. Utiliza herramientas como:
Una herramienta de auditoría de accesibilidad.
Una herramienta integrada en Chrome que puede evaluar la accesibilidad de tu aplicación.
Ejemplo:
<img [src]="imageUrl" [alt]="imageDescription">
Mejores prácticas.
<img src="" alt="">
<img [src]="imageUrl" [alt]="imageDescription">
- Minimiza el número de cambios en las propiedades enlazadas para mejorar el rendimiento. Los cambios frecuentes pueden llevar a un rendimiento deficiente debido a las múltiples actualizaciones del DOM.
updateImage() {
this.imageUrl = 'new-image-url.jpg'; // Cambiar frecuentemente
}
Mejor Práctica:
Agrupa los cambios o usa un enfoque que limite las actualizaciones:
updateImageBatch() {
this.imageUrls = ['url1.jpg', 'url2.jpg', 'url3.jpg'];
}
-
@Component({
selector: 'app-example',
templateUrl: './example.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ExampleComponent {
// Propiedades
}
Ejemplo:
// Mejor práctica: usa nombres claros
public imageUrl: string;
public imageDescription: string;
Ejemplo:
<!-- Enlace innecesario -->
<div [class.active]="isActive" [style.display]="isVisible ? 'block' : 'none'"></div>
En lugar de enlazar ambos, considerar la lógica de negocio para simplificar.
Ejemplo:
<input #inputElement type="text" (input)="onInput(inputElement.value)">
Ejemplos prácticos.
<div [ngClass]="{'active': isActive}" [style.color]="textColor"></div>
Mejora del Rendimiento: Al cargar solo los módulos necesarios, se reduce el tamaño del archivo JavaScript que se envía al navegador, lo que resulta en un tiempo de carga más rápido.
Optimización de Recursos: Se utilizan menos recursos en el navegador, lo que mejora la experiencia del usuario en dispositivos móviles y conexiones lentas.
Escalabilidad: Permite estructurar la aplicación en módulos independientes, facilitando el mantenimiento y la expansión de la aplicación en el futuro.
Implementación de carga diferida.
ng generate module admin --route admin --module app.module
Este comando generará un módulo AdminModule y configurará automáticamente las rutas en el AppRoutingModule para que se cargue de forma diferida.
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
},
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: '**', redirectTo: '/home' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
En este ejemplo, la ruta /admin se configura para cargar AdminModule de forma diferida. La función loadChildren utiliza la sintaxis de función de flecha para devolver una promesa que resuelve el módulo.
ng generate component admin/dashboard
ng generate component admin/users
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DashboardComponent } from './dashboard/dashboard.component';
import { UsersComponent } from './users/users.component';
const routes: Routes = [
{ path: '', component: DashboardComponent },
{ path: 'users', component: UsersComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AdminRoutingModule { }
Gestión de Errores: Es importante manejar posibles errores al cargar módulos diferidos. Puedes utilizar el manejo de errores en el loadChildren para gestionar casos en los que el módulo no se carga correctamente.
Prefetching: Considerar el uso de estrategias de prefetching para anticipar la carga de ciertos módulos que el usuario podría necesitar a continuación.
Tamaño del Paquete: Evaluar el tamaño de los módulos. Si un módulo es muy grande, considera dividirlo en módulos más pequeños que se carguen de forma diferida.