Перейти к содержанию

Безопасность

📅 16.05.2023

В этой теме описываются встроенные средства защиты Angular от распространенных уязвимостей веб-приложений и атак, таких как межсайтовые скриптовые атаки. В ней не рассматривается безопасность на уровне приложения, такая как аутентификация и авторизация.

Для получения дополнительной информации об атаках и мерах защиты, описанных ниже, см. руководство Open Web Application Security Project (OWASP) Guide.

Вы можете запустить пример в Stackblitz и загрузить код оттуда.

Сообщение об уязвимостях

Angular является частью программы Google Open Source Software Vulnerability Reward Program, об уязвимостях в Angular просьба сообщать здесь.

Более подробную информацию о том, как Google решает проблемы безопасности, можно найти в Философия безопасности Google.

Лучшие практики

Практики Подробности
Следите за последними выпусками библиотек Angular Библиотеки Angular регулярно обновляются, и эти обновления могут устранять дефекты безопасности, обнаруженные в предыдущих версиях. Проверяйте журнал изменений Angular change log на наличие обновлений, связанных с безопасностью.
Не изменяйте свою копию Angular Частные, настраиваемые версии Angular обычно отстают от текущей версии и могут не включать важные исправления и улучшения безопасности. Вместо этого поделитесь своими улучшениями в Angular с сообществом и сделайте запрос на исправление.
Избегайте Angular API, отмеченных в документации как "Security Risk". Для получения дополнительной информации смотрите раздел Trusting safe values этой страницы.

Предотвращение межсайтового скриптинга (XSS)

Межсайтовый скриптинг (XSS) позволяет злоумышленникам внедрять вредоносный код на веб-страницы. Затем такой код может, например, украсть данные пользователя и данные для входа в систему или выполнить действия, выдающие себя за пользователя.

Это одна из самых распространенных атак в Интернете.

Чтобы блокировать XSS-атаки, необходимо предотвратить попадание вредоносного кода в объектную модель документа (DOM). Например, если злоумышленникам удастся обманом вставить тег <script> в DOM, они смогут выполнить произвольный код на вашем сайте.

Атака не ограничивается тегами <script> — многие элементы и свойства в DOM позволяют выполнить код, например, <img alt="" onerror="..."> и <a href="javascript:...">.

Если данные, контролируемые злоумышленником, попадают в DOM, ожидайте уязвимостей безопасности.

Модель безопасности от межсайтового скриптинга в Angular

Чтобы систематически блокировать ошибки XSS, Angular по умолчанию рассматривает все значения как недоверенные. Когда значение вставляется в DOM из привязки шаблона или интерполяции, Angular санирует и экранирует недоверенные значения.

Если значение уже было санировано вне Angular и считается безопасным, сообщите об этом Angular, пометив значение как доверенное.

В отличие от значений, используемых для рендеринга, шаблоны Angular по умолчанию считаются доверенными и должны рассматриваться как исполняемый код. Никогда не создавайте шаблоны путем конкатенации пользовательского ввода и синтаксиса шаблона.

Это позволит злоумышленникам внедрить произвольный код в ваше приложение.

Чтобы предотвратить эти уязвимости, всегда используйте Ahead-Of-Time (AOT) компилятор шаблонов по умолчанию в производственных развертываниях.

Дополнительный уровень защиты может быть обеспечен за счет использования политики безопасности содержимого и доверенных типов. Эти функции веб-платформы работают на уровне DOM, что является наиболее эффективным местом для предотвращения XSS проблем. Здесь их нельзя обойти, используя другие, более низкоуровневые API.

По этой причине настоятельно рекомендуется использовать преимущества этих функций. Для этого настройте политику безопасности контента для приложения и включите внедрение доверенных типов.

Санитаризация и контексты безопасности

Санитизация — это проверка недоверенного значения, превращающая его в значение, которое безопасно для вставки в DOM. Во многих случаях санирование вообще не изменяет значение.

Санирование зависит от контекста:

Значение, безобидное в CSS, потенциально опасно в URL.

Angular определяет следующие контексты безопасности:

Контексты безопасности Подробности
HTML Используется при интерпретации значения как HTML, например, при привязке к innerHtml.
Style Используется при привязке CSS к свойству style.
URL Используется для свойств URL, таких как <a href>.
Resource URL URL, который загружается и выполняется как код, например, в <script src>.

Angular санирует недоверенные значения для HTML, стилей и URL. Дезинфекция URL ресурсов невозможна, поскольку они содержат произвольный код. В режиме разработки Angular печатает предупреждение в консоли, когда ему приходится изменять значение во время санирования.

Пример санирования

Следующий шаблон связывает значение htmlSnippet. Один раз путем интерполяции в содержимое элемента, а другой раз путем привязки к свойству innerHTML элемента:

1
2
3
4
5
6
7
8
<h3>Binding innerHTML</h3>
<p>Bound value:</p>
<p class="e2e-inner-html-interpolated">{{htmlSnippet}}</p>
<p>Result of binding to innerHTML:</p>
<p
    class="e2e-inner-html-bound"
    [innerHTML]="htmlSnippet"
></p>

Интерполированное содержимое всегда экранируется — HTML не интерпретируется, и браузер отображает угловые скобки в текстовом содержимом элемента.

Чтобы HTML интерпретировался, привяжите его к свойству HTML, такому как innerHTML. Помните, что привязка значения, которое может контролировать злоумышленник, к innerHTML обычно приводит к XSS-уязвимости.

Например, можно запустить JavaScript следующим образом:

1
2
3
4
5
export class InnerHtmlBindingComponent {
    // For example, a user/attacker-controlled value from a URL.
    htmlSnippet =
        'Template <script>alert("0wned")</script> <b>Syntax</b>';
}

Angular распознает значение как небезопасное и автоматически санирует его, удаляя элемент script, но сохраняя безопасное содержимое, такое как элемент b.

Снимок экрана, показывающий интерполированные и связанные значения HTML

Прямое использование API DOM и явные вызовы санации

Если вы не используете доверенные типы, встроенные в браузер DOM API не защищают вас от уязвимостей безопасности автоматически. Например, document, узел, доступный через ElementRef, и многие API сторонних разработчиков содержат небезопасные методы.

Аналогично, если вы взаимодействуете с другими библиотеками, которые манипулируют DOM, у вас, скорее всего, не будет такой автоматической санации, как при интерполяции Angular.

Избегайте прямого взаимодействия с DOM и вместо этого используйте шаблоны Angular, где это возможно.

В случаях, когда это неизбежно, используйте встроенные функции санитарии Angular. Санируйте недоверенные значения с помощью метода DomSanitizer.sanitize и соответствующего SecurityContext.

Эта функция также принимает значения, которые были помечены как доверенные с помощью функций bypassSecurityTrust …, и не санирует их, как описано ниже.

Доверие к безопасным значениям

Иногда приложениям действительно необходимо включать исполняемый код, отображать <iframe> из какого-либо URL или создавать потенциально опасные URL. Чтобы предотвратить автоматическую санацию в таких ситуациях, скажите Angular, что вы проверили значение, проверили, как оно было создано, и убедились, что оно безопасно.

Будьте осторожны.

Если вы доверяете значению, которое может быть вредоносным, вы вносите уязвимость в безопасность вашего приложения.

Если вы сомневаетесь, найдите профессионального эксперта по безопасности.

Чтобы пометить значение как доверенное, внедрите DomSanitizer и вызовите один из следующих методов:

  • bypassSecurityTrustHtml
  • bypassSecurityTrustScript
  • bypassSecurityTrustStyle
  • bypassSecurityTrustUrl
  • bypassSecurityTrustResourceUrl

Помните, что безопасность значения зависит от контекста, поэтому выбирайте правильный контекст для предполагаемого использования значения. Представьте, что в следующем шаблоне необходимо связать URL с вызовом javascript:alert(...):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<h4>An untrusted URL:</h4>
<p>
    <a class="e2e-dangerous-url" [href]="dangerousUrl"
        >Click me</a
    >
</p>
<h4>A trusted URL:</h4>
<p>
    <a class="e2e-trusted-url" [href]="trustedUrl"
        >Click me</a
    >
</p>

Обычно Angular автоматически санирует URL, отключает опасный код, а в режиме разработки записывает это действие в консоль. Чтобы предотвратить это, пометьте значение URL как доверенный URL с помощью вызова bypassSecurityTrustUrl:

1
2
3
4
5
6
constructor(private sanitizer: DomSanitizer) {
  // javascript: URLs are dangerous if attacker controlled.
  // Angular sanitizes them in data binding, but you can
  // explicitly tell Angular to trust this value:
  this.dangerousUrl = 'javascript:alert("Hi there")';
  this.trustedUrl = sanitizer.bypassSecurityTrustUrl(this.dangerousUrl);

Снимок экрана, показывающий окно оповещения, созданное на основе доверенного URL-адреса

Если вам нужно преобразовать вводимые пользователем данные в доверенное значение, используйте метод компонента. Следующий шаблон позволяет пользователям вводить идентификатор видео YouTube и загружать соответствующее видео в <iframe>.

Атрибут <iframe src> является контекстом безопасности URL ресурса, поскольку недоверенный источник может, например, тайно загрузить файлы, которые могут запустить ничего не подозревающие пользователи.

Чтобы предотвратить это, вызовите метод на компоненте для построения доверенного URL видео, что заставит Angular разрешить привязку в <iframe src>:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<h4>Resource URL:</h4>
<p>Showing: {{dangerousVideoUrl}}</p>
<p>Trusted:</p>
<iframe
    class="e2e-iframe-trusted-src"
    width="640"
    height="390"
    [src]="videoUrl"
    title="trusted video url"
></iframe>
<p>Untrusted:</p>
<iframe
    class="e2e-iframe-untrusted-src"
    width="640"
    height="390"
    [src]="dangerousVideoUrl"
    title="unTrusted video url"
></iframe>
1
2
3
4
5
6
7
8
9
updateVideoUrl(id: string) {
  // Appending an ID to a YouTube URL is safe.
  // Always make sure to construct SafeValue objects as
  // close as possible to the input data so
  // that it's easier to check if the value is safe.
  this.dangerousVideoUrl = 'https://www.youtube.com/embed/' + id;
  this.videoUrl =
      this.sanitizer.bypassSecurityTrustResourceUrl(this.dangerousVideoUrl);
}

Политика безопасности контента

Политика безопасности содержимого (CSP) — это техника защиты в глубину для предотвращения XSS. Чтобы включить CSP, настройте свой веб-сервер на возврат соответствующего HTTP-заголовка Content-Security-Policy.

Подробнее о политике безопасности контента можно прочитать в руководстве Web Fundamentals guide на сайте Google Developers.

Минимальная политика, необходимая для совершенно нового приложения Angular, такова:

1
default-src 'self'; style-src 'self' 'nonce-randomNonceGoesHere'; script-src 'self' 'nonce-randomNonceGoesHere';

При обслуживании вашего приложения Angular сервер должен включать случайно сгенерированный nonce в HTTP-заголовок каждого запроса. Вы должны указать этот nonce для Angular, чтобы фреймворк мог отображать элементы <style>. Вы можете задать nonce для Angular одним из двух способов:

  1. Установить атрибут ngCspNonce на корневом элементе приложения в виде <app ngCspNonce="randomNonceGoesHere"></app>. Используйте этот подход, если у вас есть доступ к шаблонизатору на стороне сервера, который может добавить nonce как в заголовок, так и в index.html при построении ответа.

  2. Предоставить nonce с помощью инъекционного токена CSP_NONCE. Используйте этот подход, если у вас есть доступ к nonce во время выполнения и вы хотите иметь возможность кэшировать index.html.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import {
    bootstrapApplication,
    CSP_NONCE,
} from '@angular/core';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, {
    providers: [
        {
            provide: CSP_NONCE,
            useValue: globalThis.myRandomNonceValue,
        },
    ],
});

Всегда следите за тем, чтобы предоставляемые вами несы были уникальными для каждого запроса и чтобы они не были предсказуемыми или угадываемыми. Если злоумышленник сможет предсказать будущие несы, он сможет обойти защиту, предлагаемую CSP.

Если вы не можете генерировать несы в своем проекте, вы можете разрешить встроенные стили, добавив 'unsafe-inline' в секцию style-src заголовка CSP.

Секции Потробности
default-src 'self'; Позволяет странице загружать все необходимые ресурсы из одного источника.
style-src 'self' 'nonce-randomNonceGoesHere'; Позволяет странице загружать глобальные стили из того же источника ('self') и стили, вставленные Angular с помощью nonce-randomNonceGoesHere.
script-src 'self' 'nonce-randomNonceGoesHere'; Позволяет странице загружать JavaScript из того же источника ('self') и скрипты, вставленные Angular CLI с nonce-randomNonceGoesHere. Это требуется только в том случае, если вы используете критическую инкрустацию CSS.

Сам Angular требует только этих настроек для правильной работы. По мере роста вашего проекта вам может понадобиться расширить настройки CSP, чтобы учесть дополнительные возможности, характерные для вашего приложения.

Обеспечение доверенных типов

Рекомендуется использовать Trusted Types для защиты приложений от атак межсайтовых сценариев. Trusted Types — это функция веб-платформы, которая может помочь вам предотвратить атаки межсайтовых сценариев путем внедрения более безопасных методов кодирования. Trusted Types также может упростить аудит кода приложений.

Доверенные типы могут быть доступны не во всех браузерах, на которые нацелено ваше приложение. В случае, если ваше приложение с поддержкой Trusted-Types запускается в браузере, который не поддерживает Trusted-Types, функции приложения сохраняются. Ваше приложение защищено от XSS с помощью DomSanitizer от Angular.

Текущую поддержку браузеров смотрите на caniuse.com/trusted-types.

Чтобы внедрить Trusted Types для вашего приложения, вы должны настроить веб-сервер вашего приложения на выдачу HTTP-заголовков с одной из следующих политик Angular:

Policies Detail
angular Эта политика используется в проверенном на безопасность коде, который является внутренним для Angular, и необходима для работы Angular, когда применяются доверенные типы. Любые встроенные значения шаблонов или содержимое, санируемое Angular, рассматриваются этой политикой как безопасные.
angular#unsafe-bypass Эта политика используется для приложений, которые используют любой из методов в DomSanitizer Angular, которые обходят безопасность, например bypassSecurityTrustHtml. Любое приложение, использующее эти методы, должно включить эту политику.
angular#unsafe-jit Эта политика используется Just-In-Time (JIT) компилятором. Вы должны включить эту политику, если ваше приложение напрямую взаимодействует с JIT-компилятором или работает в режиме JIT с помощью platform browser dynamic.
angular#bundler Эта политика используется Angular CLI bundler при создании файлов lazy chunk.

Вы должны настроить HTTP-заголовки для доверенных типов в следующих местах:

  • Производственная обслуживающая инфраструктура
  • Angular CLI (ng serve), используя свойство headers в файле angular.json, для локальной разработки и сквозного тестирования
  • Karma (ng test), используя свойство customHeaders в файле karma.config.js для модульного тестирования.

Ниже приведен пример заголовка, специально настроенного для Trusted Types и Angular:

1
Content-Security-Policy: trusted-types angular; require-trusted-types-for 'script';

Пример заголовка, специально настроенного для Trusted Types и приложений Angular, которые используют любой из методов Angular в DomSanitizer в обход безопасности:

1
Content-Security-Policy: trusted-types angular angular#unsafe-bypass; require-trusted-types-for 'script';

Ниже приведен пример заголовка, специально настроенного для Trusted Types и приложений Angular, использующих JIT:

1
Content-Security-Policy: trusted-types angular angular#unsafe-jit; require-trusted-types-for 'script';

Ниже приведен пример заголовка, специально настроенного для Trusted Types и приложений Angular, использующих ленивую загрузку модулей:

1
Content-Security-Policy: trusted-types angular angular#bundler; require-trusted-types-for 'script';

Вклад сообщества

Чтобы узнать больше о диагностике конфигураций Trusted Type, может быть полезен следующий ресурс:

Предотвращение уязвимостей межсайтового скриптинга на основе DOM с помощью Trusted Types

Используйте компилятор шаблонов AOT

Компилятор шаблонов AOT предотвращает целый класс уязвимостей, называемых инъекциями шаблонов, и значительно повышает производительность приложений. Компилятор шаблонов AOT является компилятором по умолчанию, используемым в приложениях Angular CLI, и вы должны использовать его во всех производственных развертываниях.

Альтернативой AOT-компилятору является JIT-компилятор, который компилирует шаблоны в исполняемый код шаблона в браузере во время выполнения. Angular доверяет коду шаблонов, поэтому динамическая генерация шаблонов и их компиляция, в частности шаблонов, содержащих пользовательские данные, обходит встроенные средства защиты Angular. Это антипаттерн безопасности.

Информацию о безопасном динамическом построении форм см. в руководстве Dynamic Forms.

Защита от XSS на стороне сервера

HTML, созданный на сервере, уязвим для инъекционных атак. Инъекция шаблонного кода в приложение Angular — это то же самое, что инъекция исполняемого кода в приложение:

Это дает злоумышленнику полный контроль над приложением.

Чтобы предотвратить это, используйте язык шаблонов, который автоматически экранирует значения для предотвращения XSS-уязвимостей на сервере.

Не создавайте шаблоны Angular на стороне сервера с помощью языка шаблонов. Это сопряжено с высоким риском появления уязвимостей, связанных с внедрением шаблонов.

Уязвимости на уровне HTTP

Angular имеет встроенную поддержку для предотвращения двух распространенных уязвимостей HTTP, межсайтовой подделки запросов (CSRF или XSRF) и межсайтового включения сценариев (XSSI). Обе эти уязвимости должны быть устранены в основном на стороне сервера, но Angular предоставляет вспомогательные средства для облегчения интеграции на стороне клиента.

Подделка межсайтовых запросов

При подделке межсайтового запроса (CSRF или XSRF) злоумышленник обманывает пользователя, заставляя его посетить другую веб-страницу (например, evil.com) с вредоносным кодом. Эта веб-страница тайно отправляет вредоносный запрос на веб-сервер приложения (например, example-bank.com).

Предположим, что пользователь вошел в приложение по адресу example-bank.com. Пользователь открывает электронное письмо и нажимает на ссылку evil.com, которая открывается в новой вкладке.

Страница evil.com немедленно отправляет вредоносный запрос на example-bank.com. Возможно, это запрос на перевод денег со счета пользователя на счет злоумышленника.

Браузер автоматически отправляет с этим запросом куки example-bank.com, включая куки аутентификации.

Если сервер example-bank.com не имеет защиты XSRF, он не сможет отличить законный запрос от приложения от поддельного запроса от evil.com.

Чтобы предотвратить это, приложение должно убедиться, что запрос пользователя исходит от настоящего приложения, а не от другого сайта. Сервер и клиент должны сотрудничать, чтобы предотвратить эту атаку.

В распространенной технике защиты от XSRF сервер приложений отправляет случайно созданный маркер аутентификации в файле cookie. Клиентский код считывает куки и добавляет пользовательский заголовок запроса с маркером во все последующие запросы.

Сервер сравнивает полученное значение cookie со значением заголовка запроса и отклоняет запрос, если значения отсутствуют или не совпадают.

Эта техника эффективна, поскольку все браузеры реализуют политику одинакового происхождения. Только код сайта, на котором установлены куки, может читать куки с этого сайта и устанавливать пользовательские заголовки в запросах к этому сайту.

Это означает, что только ваше приложение может прочитать этот маркер cookie и установить пользовательский заголовок.

Вредоносный код на evil.com не может.

Angular HttpClient имеет встроенную поддержку клиентской части этой техники. Подробнее об этом читайте в руководстве HttpClient guide.

Информацию о CSRF на Open Web Application Security Project (OWASP), смотрите Cross-Site Request Forgery (CSRF) и Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet. Документ Стэнфордского университета Robust Defenses for Cross-Site Request Forgery является богатым источником подробностей.

См. также доклад Дэйва Смита доклад о XSRF на AngularConnect 2016.

Межсайтовое включение сценариев (XSSI)

Межсайтовое включение сценариев, также известное как JSON-уязвимость, может позволить веб-сайту злоумышленника считывать данные из JSON API. Атака работает на старых браузерах путем переопределения встроенных конструкторов объектов JavaScript, а затем включения URL API с помощью тега <script>.

Атака успешна только в том случае, если возвращаемый JSON исполняется как JavaScript. Серверы могут предотвратить атаку, добавляя префикс ко всем ответам JSON, чтобы сделать их неисполнимыми, по соглашению, используя известную строку ")]}',\n".

Библиотека Angular HttpClient распознает это соглашение и автоматически удаляет строку ")]}',\n" из всех ответов перед дальнейшим разбором.

Для получения дополнительной информации см. раздел XSSI в этой статье Google web security blog post.

Аудит приложений Angular

Angular-приложения должны следовать тем же принципам безопасности, что и обычные веб-приложения, и должны подвергаться аудиту как таковому. Специфические для Angular API, которые должны проверяться в ходе обзора безопасности, такие как методы bypassSecurityTrust, отмечены в документации как чувствительные к безопасности.

Комментарии