hacktricks/network-services-pentesting/pentesting-web/angular.md
Translator workflow 35c6b081d2 Translated to Greek
2024-02-10 22:40:18 +00:00

30 KiB
Raw Blame History

Angular

Ο Έλεγχος

Έλεγχος από εδώ.

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

Τι είναι το Angular

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

Αρχιτεκτονική πλαισίου

Για να κατανοήσουμε καλύτερα τα βασικά στοιχεία του 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.

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

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

Ρύθμιση του Sourcemap

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

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

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

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

Δέσμευση δεδομένων

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

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

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

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

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

Εισαγωγή HTML

Αυτή η ευπάθεια συμβαίνει όταν η είσοδος του χρήστη συνδέεται με οποιαδήποτε από τις τρεις ιδιότητες: innerHTML, outerHTML ή iframe srcdoc. Ενώ η σύνδεση με αυτά τα χαρακτηριστικά ερμηνεύει το HTML όπως είναι, η είσοδος απολυμαίνεται χρησιμοποιώντας το SecurityContext.HTML. Έτσι, είναι δυνατή η εισαγωγή HTML, αλλά δεν είναι δυνατή η επίθεση cross-site scripting (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 αναφέρεται στο πεδίο εφαρμογής της ιδιότητας constructor του αντικειμένου, επιτρέποντάς μας να καλέσουμε τον κατασκευαστή του String και να εκτελέσουμε κώδικα της επιλογής μας.

Απομακρυσμένη απεικόνιση στην πλευρά του διακομιστή (SSR)

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

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

XSS

Διεπαφές DOM

Όπως αναφέρθηκε προηγουμένως, μπορούμε να έχουμε άμεση πρόσβαση στο DOM χρησιμοποιώντας τη διεπαφή Document. Εάν η είσοδος του χρήστη δεν επικυρωθεί προηγουμένως, μπορεί να οδηγήσει σε ευπάθειες διασποράς σελίδων (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

Υπάρχουν μερικές κλάσεις που μπορούν να χρησιμοποιηθούν για να δουλέψουν με στοιχεία DOM στο Angular: ElementRef, Renderer2, Location και Document. Μια λεπτομερής περιγραφή των δύο τελευταίων κλάσεων δίνεται στην ενότητα Ανοικτές ανακατευθύνσεις. Η κύρια διαφορά μεταξύ των πρώτων δύο είναι ότι η διεπαφή Renderer2 παρέχει ένα επίπεδο αφαίρεσης μεταξύ του στοιχείου DOM και του κώδικα του στοιχείου, ενώ η ElementRef απλώς κρατά μια αναφορά στο στοιχείο. Συνεπώς, σύμφωνα με την τεκμηρίωση του Angular, η διεπαφή 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 παρέχει μια διεπαφή που μπορεί να χρησιμοποιηθεί με ασφάλεια ακόμα και όταν δεν υποστηρίζεται άμεση πρόσβαση σε φυσικά στοιχεία, εξακολουθεί να έχει μερικά προβλήματα ασφάλειας. Με την 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. Ωστόσο, δεν καταφέραμε να βρούμε κανένα έγκυρο διάνυσμα επίθεσης για αυτές τις μεθόδους λόγω των λειτουργικών περιορισμών τους.

jQuery

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

  • Η μέθοδος html() αποκτά το περιεχόμενο HTML του πρώτου στοιχείου στο σύνολο των ταιριαστών στοιχείων ή ορίζει το περιεχόμενο HTML κάθε ταιριαστού στοιχείου. Ωστόσο, κατά σχεδιασμό, οποιαδήποτε κατασκευή ή μέθοδος του jQuery που δέχεται μια συμβολοσειρά HTML μπορεί δυνητικά ν

Κλάσεις Angular

  • Σύμφωνα με την τεκμηρίωση του Angular, η κλάση Document του Angular είναι ίδια με το DOM document, πράγμα που σημαίνει ότι είναι δυνατή η χρήση κοινών διανυσμάτων για το DOM document για την εκμετάλλευση ευπαθειών πελάτη στο 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()">Κάντε κλικ εδώ!</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')

Αναφορές