# Angular ## La Lista de Verificación Verificar que: * [ ] Angular se considera un framework del lado del cliente y no se espera que proporcione protección del lado del servidor * [ ] El sourcemap para los scripts está desactivado en la configuración del proyecto * [ ] La entrada de usuario no confiable siempre se interpola o se sanea antes de ser utilizada en las plantillas * [ ] El usuario no tiene control sobre las plantillas del lado del servidor o del lado del cliente * [ ] La entrada de usuario no confiable se sanea utilizando un contexto de seguridad adecuado antes de ser confiada por la aplicación * [ ] No se utilizan métodos `BypassSecurity*` con entrada no confiable * [ ] La entrada de usuario no confiable no se pasa a clases de Angular como `ElementRef`, `Renderer2` y `Document`, ni a otros puntos de entrada de JQuery/DOM ## ¿Qué es Angular? Angular es un potente framework del lado del cliente que se utiliza ampliamente para construir aplicaciones web dinámicas. Es de código abierto y está mantenido por Google. Una de las características clave de Angular es su uso de TypeScript, un superset tipado de JavaScript que facilita la lectura, el mantenimiento y la depuración del código. Los mecanismos de seguridad de Angular están diseñados para prevenir vulnerabilidades comunes del lado del cliente, como el cross-site scripting (XSS) y las redirecciones abiertas. Sin embargo, Angular también se puede utilizar en el lado del servidor para generar páginas estáticas. Por lo tanto, la seguridad de Angular debe considerarse desde ambos lados. ## Arquitectura del framework Para comprender mejor los conceptos básicos de Angular, veamos sus conceptos esenciales. Un proyecto de Angular común suele tener el siguiente aspecto: ```bash my-workspace/ ├── ... #workspace-wide configuration files ├── src │ ├── app │ │ ├── app.module.ts #defines the root module, that tells Angular how to assemble the application │ │ ├── app.component.ts #defines the logic for the application's root component │ │ ├── app.component.html #defines the HTML template associated with the root component │ │ ├── app.component.css #defines the base CSS stylesheet for the root component │ │ ├── app.component.spec.ts #defines a unit test for the root component │ │ └── app-routing.module.ts #provides routing capability for the application │ ├── lib │ │ └── src #library-specific configuration files │ ├── index.html #main HTML page, where the component will be rendered in │ └── ... #application-specific configuration files ├── angular.json #provides workspace-wide and project-specific configuration defaults └── tsconfig.json #provides the base TypeScript configuration for projects in the workspace ``` Según la documentación, cada aplicación Angular tiene al menos un componente, el componente raíz (`AppComponent`) que conecta una jerarquía de componentes con el DOM. Cada componente define una clase que contiene datos y lógica de la aplicación, y está asociado con una plantilla HTML que define una vista a ser mostrada en un entorno objetivo. El decorador `@Component()` identifica la clase inmediatamente debajo de él como un componente, y proporciona la plantilla y metadatos específicos del componente. El `AppComponent` se define en el archivo `app.component.ts`. Los módulos de Angular (`NgModules`) declaran un contexto de compilación para un conjunto de componentes que está dedicado a un dominio de aplicación, un flujo de trabajo o un conjunto de capacidades estrechamente relacionadas. Cada aplicación Angular tiene un módulo raíz, convencionalmente llamado `AppModule`, que proporciona el mecanismo de arranque que inicia la aplicación. Una aplicación típicamente contiene muchos módulos funcionales. El `AppModule` se define en el archivo `app.module.ts`. El módulo `Router` de Angular proporciona un servicio que te permite definir una ruta de navegación entre los diferentes estados de la aplicación y jerarquías de vistas en tu aplicación. El `RouterModule` se define en el archivo `app-routing.module.ts`. Para datos o lógica que no están asociados con una vista específica y que deseas compartir entre componentes, creas una clase de servicio. La definición de una clase de servicio está precedida inmediatamente por el decorador `@Injectable()`. El decorador proporciona los metadatos que permiten que otros proveedores sean inyectados como dependencias en tu clase. La inyección de dependencias (DI) te permite mantener tus clases de componentes livianas y eficientes. No obtienen datos del servidor, validan la entrada del usuario o registran directamente en la consola; delegan esas tareas a los servicios. ## Configuración de Sourcemap El framework Angular traduce los archivos TypeScript a código JavaScript siguiendo las opciones del archivo `tsconfig.json` y luego construye un proyecto con la configuración de `angular.json`. Al observar el archivo `angular.json`, hemos observado una opción para habilitar o deshabilitar un sourcemap. Según la documentación de Angular, la configuración predeterminada tiene un archivo sourcemap habilitado para los scripts y no está oculto por defecto: ```json "sourceMap": { "scripts": true, "styles": true, "vendor": false, "hidden": false } ``` Generalmente, los archivos de sourcemap se utilizan con fines de depuración, ya que mapean los archivos generados a sus archivos originales. Por lo tanto, no se recomienda utilizarlos en un entorno de producción. Si los sourcemaps están habilitados, mejoran la legibilidad y ayudan en el análisis de archivos al replicar el estado original del proyecto Angular. Sin embargo, si están deshabilitados, un revisor aún puede analizar un archivo JavaScript compilado manualmente buscando patrones de seguridad. Además, un archivo JavaScript compilado con un proyecto Angular se puede encontrar en las herramientas de desarrollo del navegador → Fuentes (o Depurador y Fuentes) → \[id].main.js. Dependiendo de las opciones habilitadas, este archivo puede contener la siguiente línea al final `//# sourceMappingURL=[id].main.js.map` o puede que no, si la opción **hidden** está establecida en **true**. Sin embargo, si el sourcemap está deshabilitado para **scripts**, las pruebas se vuelven más complejas y no podemos obtener el archivo. Además, el sourcemap se puede habilitar durante la construcción del proyecto como `ng build --source-map`. ## Data binding La vinculación se refiere al proceso de comunicación entre un componente y su vista correspondiente. Se utiliza para transferir datos hacia y desde el framework Angular. Los datos se pueden pasar a través de varios medios, como eventos, interpolación, propiedades o mediante el mecanismo de vinculación bidireccional. Además, los datos también se pueden compartir entre componentes relacionados (relación padre-hijo) y entre dos componentes no relacionados utilizando la función de servicio. Podemos clasificar la vinculación por flujo de datos: * Fuente de datos al destino de la vista (incluye _interpolación_, _propiedades_, _atributos_, _clases_ y _estilos_); se puede aplicar utilizando `[]` o `{{}}` en la plantilla; * Destino de la vista a la fuente de datos (incluye _eventos_); se puede aplicar utilizando `()` en la plantilla; * Bidireccional; se puede aplicar utilizando `[()]` en la plantilla. La vinculación se puede aplicar a propiedades, eventos y atributos, así como a cualquier miembro público de una directiva fuente: | TIPO | DESTINO | EJEMPLOS | | --------- | -------------------------------------------------------- | ------------------------------------------------------------------- | | Propiedad | Propiedad del elemento, propiedad del componente, propiedad de la directiva | \ | | Evento | Evento del elemento, evento del componente, evento de la directiva | \ ``` * Para establecer la propiedad de un elemento del DOM, se puede utilizar el método `Renderer2.setProperty()` y desencadenar un ataque XSS: ```tsx //app.component.ts import {Component, Renderer2, ElementRef, ViewChild, AfterViewInit } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { public constructor ( private renderer2: Renderer2 ){} @ViewChild("img") img!: ElementRef; setProperty(){ this.renderer2.setProperty(this.img.nativeElement, 'innerHTML', ''); } } //app.component.html ``` Durante nuestra investigación, también examinamos el comportamiento de otros métodos de `Renderer2`, como `setStyle()`, `createComment()` y `setValue()`, en relación con las inyecciones XSS y CSS. Sin embargo, no pudimos encontrar vectores de ataque válidos para estos métodos debido a sus limitaciones funcionales. #### jQuery jQuery es una biblioteca de JavaScript rápida, pequeña y rica en funciones que se puede utilizar en proyectos de Angular para ayudar con la manipulación de objetos del DOM HTML. Sin embargo, como se sabe, los métodos de esta biblioteca pueden ser explotados para lograr una vulnerabilidad XSS. Con el fin de discutir cómo se pueden explotar algunos métodos vulnerables de jQuery en proyectos de Angular, hemos agregado esta subsección. * El método `html()` obtiene el contenido HTML del primer elemento en el conjunto de elementos coincidentes o establece el contenido HTML de cada elemento coincidente. Sin embargo, por diseño, cualquier constructor o método de jQuery que acepte una cadena HTML puede ejecutar código potencialmente. Esto puede ocurrir mediante la inyección de etiquetas `"); }); } } //app.component.html

algún texto aquí

``` * El método `jQuery.parseHTML()` utiliza métodos nativos para convertir la cadena en un conjunto de nodos DOM, que luego se pueden insertar en el documento. ```tsx jQuery.parseHTML(data [, context ] [, keepScripts ]) ``` Como se mencionó anteriormente, la mayoría de las API de jQuery que aceptan cadenas HTML ejecutarán scripts que se incluyen en el HTML. El método `jQuery.parseHTML()` no ejecuta scripts en el HTML analizado a menos que `keepScripts` sea explícitamente `true`. Sin embargo, aún es posible ejecutar scripts indirectamente en la mayoría de los entornos; por ejemplo, a través del atributo ``. ```tsx //app.component.ts import { Component, OnInit } from '@angular/core'; import * as $ from 'jquery'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { ngOnInit() { $("button").on("click", function() { var $palias = $("#palias"), str = "", html = $.parseHTML(str), nodeNames = []; $palias.append(html); }); } } //app.component.html

algún texto

``` ### Redirecciones abiertas #### Interfaces del DOM Según la documentación de W3C, los objetos `window.location` y `document.location` se tratan como alias en los navegadores modernos. Es por eso que tienen una implementación similar de algunos métodos y propiedades, lo que podría causar una redirección abierta y ataques de XSS en el DOM con el esquema `javascript://`, como se menciona a continuación. * `window.location.href`(y `document.location.href`) La forma canónica de obtener el objeto de ubicación DOM actual es utilizando `window.location`. También se puede utilizar para redirigir el navegador a una nueva página. Como resultado, tener control sobre este objeto nos permite explotar una vulnerabilidad de redirección abierta. ```tsx //app.component.ts ... export class AppComponent { goToUrl(): void { window.location.href = "https://google.com/about" } } //app.component.html ``` El proceso de explotación es idéntico para los siguientes escenarios. * `window.location.assign()`(y `document.location.assign()`) Este método hace que la ventana cargue y muestre el documento en la URL especificada. Si tenemos control sobre este método, podría ser un punto de entrada para un ataque de redirección abierta. ```tsx //app.component.ts ... export class AppComponent { goToUrl(): void { window.location.assign("https://google.com/about") } } ``` * `window.location.replace()`(y `document.location.replace()`) Este método reemplaza el recurso actual con el que se encuentra en la URL proporcionada. La diferencia con el método `assign()` es que después de usar `window.location.replace()`, la página actual no se guardará en el historial de sesiones. Sin embargo, también es posible explotar una vulnerabilidad de redirección abierta cuando tenemos control sobre este método. ```tsx //app.component.ts ... export class AppComponent { goToUrl(): void { window.location.replace("http://google.com/about") } } ``` * `window.open()` El método `window.open()` toma una URL y carga el recurso que identifica en una nueva o existente pestaña o ventana. Tener control sobre este método también podría ser una oportunidad para desencadenar una vulnerabilidad de XSS o redirección abierta. ```tsx //app.component.ts ... export class AppComponent { goToUrl(): void { window.open("https://google.com/about", "_blank") } } ``` #### Clases de Angular * Según la documentación de Angular, la clase `Document` de Angular es igual que el documento DOM, lo que significa que es posible utilizar vectores comunes para el documento DOM para explotar vulnerabilidades del lado del cliente en Angular. Las propiedades y métodos de `Document.location` pueden ser puntos de entrada para ataques exitosos de redirección abierta, como se muestra en el ejemplo: ```tsx //app.component.ts import { Component, Inject } from '@angular/core'; import { DOCUMENT } from '@angular/common'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(@Inject(DOCUMENT) private document: Document) { } goToUrl(): void { this.document.location.href = 'https://google.com/about'; } } //app.component.html ``` * Durante la fase de investigación, también revisamos la clase `Location` de Angular en busca de vulnerabilidades de redirección abierta, pero no se encontraron vectores válidos. `Location` es un servicio de Angular que las aplicaciones pueden utilizar para interactuar con la URL actual del navegador. Este servicio tiene varios métodos para manipular la URL proporcionada, como `go()`, `replaceState()` y `prepareExternalUrl()`. Sin embargo, no podemos utilizarlos para redireccionar a un dominio externo. Por ejemplo: ```tsx //app.component.ts import { Component, Inject } from '@angular/core'; import {Location, LocationStrategy, PathLocationStrategy} from '@angular/common'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], providers: [Location, {provide: LocationStrategy, useClass: PathLocationStrategy}], }) export class AppComponent { location: Location; constructor(location: Location) { this.location = location; } goToUrl(): void { console.log(this.location.go("http://google.com/about")); } } ``` Resultado: `http://localhost:4200/http://google.com/about` * La clase `Router` de Angular se utiliza principalmente para navegar dentro del mismo dominio y no introduce ninguna vulnerabilidad adicional en la aplicación: ```jsx //app-routing.module.ts const routes: Routes = [ { path: '', redirectTo: 'https://google.com', pathMatch: 'full' }] ``` Resultado: `http://localhost:4200/https:` Los siguientes métodos también navegan dentro del ámbito del dominio: ```jsx const routes: Routes = [ { path: '', redirectTo: 'ROUTE', pathMatch: 'prefix' } ] this.router.navigate(['PATH']) this.router.navigateByUrl('URL') ``` ## Referencias * [Angular](https://angular.io/) * [Angular Security: The Definitive Guide (Parte 1)](https://lsgeurope.com/post/angular-security-the-definitive-guide-part-1) * [Angular Security: The Definitive Guide (Parte 2)](https://lsgeurope.com/post/angular-security-the-definitive-guide-part-2) * [Angular Security: The Definitive Guide (Parte 3)](https://lsgeurope.com/post/angular-security-the-definitive-guide-part-3) * [Angular Security: Lista de verificación](https://lsgeurope.com/post/angular-security-checklist) * [Estructura de archivos de Workspace y proyecto](https://angular.io/guide/file-structure) * [Introducción a los componentes y plantillas](https://angular.io/guide/architecture-components) * [Configuración de mapas de origen](https://angular.io/guide/workspace-config#source-map-configuration) * [Sintaxis de enlace](https://angular.io/guide/binding-syntax) * [Angular Context: Enlace de datos fácil para árboles de componentes anidados y el enrutador de salida](https://medium.com/angular-in-depth/angular-context-easy-data-binding-for-nested-component-trees-and-the-router-outlet-a977efacd48) * [Sanitización y contextos de seguridad](https://angular.io/guide/security#sanitization-and-security-contexts) * [GitHub - angular/dom\_security\_schema.ts](https://github.com/angular/angular/blob/main/packages/compiler/src/schema/dom\_security\_schema.ts) * [XSS en Angular y AngularJS](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/XSS%20Injection/XSS%20in%20Angular.md) * [Angular Universal](https://angular.io/guide/universal) * [DOM XSS](https://book.hacktricks.xyz/pentesting-web/xss-cross-site-scripting/dom-xss) * [Angular ElementRef](https://angular.io/api/core/ElementRef) * [Angular Renderer2](https://angular.io/api/core/Renderer2) * [Ejemplo de Renderer2: Manipulación del DOM en Angular - TekTutorialsHub](https://www.tektutorialshub.com/angular/renderer2-angular/) * [Documentación de jQuery API](http://api.jquery.com/) * [Cómo utilizar jQuery con Angular (cuando sea absolutamente necesario)](https://blog.bitsrc.io/how-to-use-jquery-with-angular-when-you-absolutely-have-to-42c8b6a37ff9) * [Angular Document](https://angular.io/api/common/DOCUMENT) * [Angular Location](https://angular.io/api/common/Location) * [Angular Router](https://angular.io/api/router/Router)