@angular-helpers/security 21.1.0 → 21.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.es.md +161 -1
- package/package.json +1 -1
package/README.es.md
CHANGED
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
# Angular Security Helpers
|
|
6
6
|
|
|
7
|
-
Paquete de seguridad para aplicaciones Angular que previene ataques comunes como ReDoS (Regular Expression Denial of Service) usando Web Workers para ejecución segura.
|
|
7
|
+
Paquete de seguridad para aplicaciones Angular que previene ataques comunes como ReDoS (Regular Expression Denial of Service), XSS, y proporciona utilidades criptográficas usando Web Workers y Web Crypto API para ejecución segura.
|
|
8
|
+
|
|
9
|
+
> **Versión**: 21.2.0 — [CHANGELOG](./CHANGELOG.md)
|
|
10
|
+
|
|
11
|
+
---
|
|
8
12
|
|
|
9
13
|
## 🛡️ Características
|
|
10
14
|
|
|
@@ -18,9 +22,32 @@ Paquete de seguridad para aplicaciones Angular que previene ataques comunes como
|
|
|
18
22
|
### **Web Crypto API**
|
|
19
23
|
|
|
20
24
|
- **Cifrado/Descifrado**: Soporte AES-GCM para manejo seguro de datos
|
|
25
|
+
- **Firmas HMAC**: Firmar y verificar datos con HMAC-SHA256/384/512
|
|
21
26
|
- **Hashing**: SHA-256 y otros algoritmos
|
|
22
27
|
- **Gestión de Claves**: Generar, importar y exportar claves criptográficas
|
|
23
28
|
- **Aleatorio Seguro**: Valores aleatorios criptográficamente seguros
|
|
29
|
+
- **Generación UUID**: UUIDs RFC4122 v4
|
|
30
|
+
|
|
31
|
+
### **Almacenamiento Seguro**
|
|
32
|
+
|
|
33
|
+
- **Cifrado Transparente**: Almacenamiento cifrado AES-GCM sobre localStorage/sessionStorage
|
|
34
|
+
- **Modo Efímero**: Claves en memoria para seguridad de sesión única
|
|
35
|
+
- **Modo Passphrase**: Claves derivadas PBKDF2 para persistencia entre sesiones
|
|
36
|
+
- **Aislamiento por Namespace**: Organiza datos almacenados con prefijos
|
|
37
|
+
|
|
38
|
+
### **Sanitización de Input**
|
|
39
|
+
|
|
40
|
+
- **Prevención XSS**: Limpieza de HTML con lista de permitidos
|
|
41
|
+
- **Validación de URLs**: Esquemas de URL seguros
|
|
42
|
+
- **Escape de HTML**: Caracteres especiales para interpolación segura
|
|
43
|
+
- **JSON Seguro**: Parseo seguro sin eval
|
|
44
|
+
|
|
45
|
+
### **Fuerza de Contraseña**
|
|
46
|
+
|
|
47
|
+
- **Puntuación basada en Entropía**: Calcular fuerza en bits de entropía
|
|
48
|
+
- **Detección de Contraseñas Comunes**: Bloquear contraseñas débiles conocidas
|
|
49
|
+
- **Detección de Patrones**: Detectar patrones de teclado y secuencias
|
|
50
|
+
- **Feedback Accionable**: Sugerencias específicas para mejorar
|
|
24
51
|
|
|
25
52
|
### **Patrón Builder**
|
|
26
53
|
|
|
@@ -44,7 +71,16 @@ import { provideSecurity } from '@angular-helpers/security';
|
|
|
44
71
|
bootstrapApplication(AppComponent, {
|
|
45
72
|
providers: [
|
|
46
73
|
provideSecurity({
|
|
74
|
+
// Servicios core (habilitados por defecto)
|
|
47
75
|
enableRegexSecurity: true,
|
|
76
|
+
enableWebCrypto: true,
|
|
77
|
+
|
|
78
|
+
// Nuevos servicios (opt-in, deshabilitados por defecto)
|
|
79
|
+
enableSecureStorage: true,
|
|
80
|
+
enableInputSanitizer: true,
|
|
81
|
+
enablePasswordStrength: true,
|
|
82
|
+
|
|
83
|
+
// Configuración global
|
|
48
84
|
defaultTimeout: 5000,
|
|
49
85
|
safeMode: false,
|
|
50
86
|
}),
|
|
@@ -52,6 +88,27 @@ bootstrapApplication(AppComponent, {
|
|
|
52
88
|
});
|
|
53
89
|
```
|
|
54
90
|
|
|
91
|
+
### **Providers Individuales**
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import {
|
|
95
|
+
provideRegexSecurity,
|
|
96
|
+
provideWebCrypto,
|
|
97
|
+
provideSecureStorage,
|
|
98
|
+
provideInputSanitizer,
|
|
99
|
+
providePasswordStrength,
|
|
100
|
+
} from '@angular-helpers/security';
|
|
101
|
+
|
|
102
|
+
// Usar solo los servicios que necesites
|
|
103
|
+
bootstrapApplication(AppComponent, {
|
|
104
|
+
providers: [
|
|
105
|
+
provideSecureStorage({ storage: 'session', pbkdf2Iterations: 600_000 }),
|
|
106
|
+
provideInputSanitizer({ allowedTags: ['b', 'i', 'em', 'strong'] }),
|
|
107
|
+
providePasswordStrength(),
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
55
112
|
### **Inyección de Servicios**
|
|
56
113
|
|
|
57
114
|
```typescript
|
|
@@ -142,6 +199,109 @@ export class SecureStorageComponent {
|
|
|
142
199
|
generateUUID(): string {
|
|
143
200
|
return this.cryptoService.randomUUID();
|
|
144
201
|
}
|
|
202
|
+
|
|
203
|
+
async signAndVerify(data: string): Promise<boolean> {
|
|
204
|
+
// Generar clave HMAC para SHA-256
|
|
205
|
+
const key = await this.cryptoService.generateHmacKey('HMAC-SHA-256');
|
|
206
|
+
|
|
207
|
+
// Firmar los datos
|
|
208
|
+
const signature = await this.cryptoService.sign(key, data);
|
|
209
|
+
|
|
210
|
+
// Verificar la firma
|
|
211
|
+
return await this.cryptoService.verify(key, data, signature);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### **SecureStorageService — Almacenamiento Cifrado**
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
import { SecureStorageService } from '@angular-helpers/security';
|
|
220
|
+
|
|
221
|
+
export class UserSettingsComponent {
|
|
222
|
+
private storage = inject(SecureStorageService);
|
|
223
|
+
|
|
224
|
+
async saveUserToken(token: string): Promise<void> {
|
|
225
|
+
// Modo efímero (por defecto): datos sobreviven solo esta sesión
|
|
226
|
+
await this.storage.set('authToken', { token, createdAt: Date.now() });
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async getUserToken(): Promise<{ token: string; createdAt: number } | null> {
|
|
230
|
+
return await this.storage.get<{ token: string; createdAt: number }>('authToken');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async initWithPassphrase(passphrase: string): Promise<void> {
|
|
234
|
+
// Modo passphrase: datos sobreviven recargas de página
|
|
235
|
+
await this.storage.initWithPassphrase(passphrase);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async saveWithNamespace(userId: string, data: unknown): Promise<void> {
|
|
239
|
+
// Aislamiento por namespace
|
|
240
|
+
await this.storage.set('profile', data, `user:${userId}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
clearUserData(userId: string): void {
|
|
244
|
+
// Limpiar solo los datos de este usuario
|
|
245
|
+
this.storage.clear(`user:${userId}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### **InputSanitizerService — Prevención XSS**
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import { InputSanitizerService } from '@angular-helpers/security';
|
|
254
|
+
|
|
255
|
+
export class CommentComponent {
|
|
256
|
+
private sanitizer = inject(InputSanitizerService);
|
|
257
|
+
|
|
258
|
+
sanitizeUserComment(html: string): string {
|
|
259
|
+
// Limpiar tags peligrosos, mantener seguros (b, i, em, a, etc.)
|
|
260
|
+
return this.sanitizer.sanitizeHtml(html);
|
|
261
|
+
// Ejemplo: '<b>Hola</b><script>alert(1)</script>' → '<b>Hola</b>'
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
validateUserLink(url: string): string | null {
|
|
265
|
+
// Solo permitir URLs http/https
|
|
266
|
+
return this.sanitizer.sanitizeUrl(url);
|
|
267
|
+
// Ejemplo: 'javascript:alert(1)' → null
|
|
268
|
+
// Ejemplo: 'https://example.com' → 'https://example.com/'
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
escapeForDisplay(text: string): string {
|
|
272
|
+
// Seguro para nodos de texto HTML
|
|
273
|
+
return this.sanitizer.escapeHtml(text);
|
|
274
|
+
// Ejemplo: '<b>hola</b>' → '<b>hola</b>'
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
parseUserJson(json: string): unknown | null {
|
|
278
|
+
// Parseo seguro de JSON sin eval
|
|
279
|
+
return this.sanitizer.sanitizeJson(json);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### **PasswordStrengthService — Evaluación de Contraseñas**
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { PasswordStrengthService } from '@angular-helpers/security';
|
|
288
|
+
|
|
289
|
+
export class RegistrationComponent {
|
|
290
|
+
private passwordStrength = inject(PasswordStrengthService);
|
|
291
|
+
|
|
292
|
+
checkPasswordStrength(password: string): void {
|
|
293
|
+
const result = this.passwordStrength.assess(password);
|
|
294
|
+
|
|
295
|
+
console.log(`Score: ${result.score}/4`); // 0-4
|
|
296
|
+
console.log(`Label: ${result.label}`); // 'very-weak' a 'very-strong'
|
|
297
|
+
console.log(`Entropía: ${result.entropy} bits`); // entropía calculada
|
|
298
|
+
console.log('Feedback:', result.feedback); // sugerencias de mejora
|
|
299
|
+
|
|
300
|
+
// Ejemplos de resultados:
|
|
301
|
+
// 'password' → score: 0, label: 'very-weak', feedback: ['Contraseña común']
|
|
302
|
+
// 'P@ssw0rd!' → score: 2, label: 'fair', feedback: ['Evita patrones de teclado']
|
|
303
|
+
// 'xK#9mZ$vLq2@rBnT7' → score: 4, label: 'very-strong', feedback: []
|
|
304
|
+
}
|
|
145
305
|
}
|
|
146
306
|
```
|
|
147
307
|
|