Angular 安全

2022-07-13 10:18 更新

安全

本主題會講述 Angular 為防范 Web 應(yīng)用常見的安全漏洞和攻擊(比如跨站腳本攻擊)內(nèi)置的保護(hù)措施,但不會涉及應(yīng)用級安全,比如用戶認(rèn)證(這個用戶是誰?)和授權(quán)(這個用戶能做什么?)。

你可以運行現(xiàn)場演練 / 下載范例,在 Stackblitz 中試用并下載本頁的代碼。

舉報漏洞
給我們(security@angular.io)發(fā)郵件,報告 Angular 本身的漏洞。
要了解關(guān)于“谷歌如何處理安全問題”的更多信息,參閱谷歌的安全哲學(xué)。

最佳實踐

實踐

詳情

及時把 Angular 包更新到最新版本

我們會頻繁的更新 Angular 庫,這些更新可能會修復(fù)之前版本中發(fā)現(xiàn)的安全漏洞。查看 Angular 的更新記錄,了解與安全有關(guān)的更新。

不要修改你的 Angular 副本

私有的、定制版的 Angular 往往跟不上最新版本,這可能導(dǎo)致你忽略重要的安全修復(fù)與增強。反之,應(yīng)該在社區(qū)共享你對 Angular 所做的改進(jìn)并創(chuàng)建 Pull Request。.

避免使用本文檔中帶“安全風(fēng)險”標(biāo)記的 Angular API

要了解更多信息,請參閱本章的信任安全值部分。

防范跨站腳本(XSS)攻擊

跨站腳本(XSS)允許攻擊者將惡意代碼注入到頁面中。這些代碼可以偷取用戶數(shù)據(jù)(特別是它們的登錄數(shù)據(jù)),還可以冒充用戶執(zhí)行操作。它是 Web 上最常見的攻擊方式之一。

為了防范 XSS 攻擊,你必須阻止惡意代碼進(jìn)入 DOM。比如,如果某個攻擊者能騙你把 ?<script>? 標(biāo)簽插入到 DOM,就可以在你的網(wǎng)站上運行任何代碼。除了 ?<script>?,攻擊者還可以使用很多 DOM 元素和屬性來執(zhí)行代碼,比如 ?<img onerror="...">?、?<a href="javascript:...">?。如果攻擊者所控制的數(shù)據(jù)混進(jìn)了 DOM,就會導(dǎo)致安全漏洞。

Angular 的“跨站腳本安全模型”

為了系統(tǒng)性的防范 XSS 問題,Angular 默認(rèn)把所有值都當(dāng)做不可信任的。 當(dāng)值從模板中以屬性(Property)、DOM 元素屬性(Attribte)、CSS 類綁定或插值等途徑插入到 DOM 中的時候, Angular 將對這些值進(jìn)行無害化處理(Sanitize),對不可信的值進(jìn)行編碼。如果某個值已經(jīng)在 Angular 之外進(jìn)行過無害化處理,可以確信是安全的,可以把這個值標(biāo)記為安全的來把這一點通知 Angular。

與用于渲染的值不同,默認(rèn)情況下,Angular 模板被認(rèn)為是受信任的,應(yīng)被視為可執(zhí)行代碼。切勿通過串聯(lián)用戶輸入和模板語法來生成模板。這樣做會使攻擊者能夠將任意代碼注入你的應(yīng)用程序。為避免這些漏洞,請始終在生產(chǎn)部署中使用默認(rèn)的 AOT 模板編譯器

借助內(nèi)容安全策略和可信類型,可以提供額外的保護(hù)層。這些 Web 平臺特性會在 DOM 級別運行,這是用來防范 XSS 問題的最有效位置,因為即使使用其它低級 API 也無法繞過它們。出于這個原因,我們強烈建議開發(fā)人員通過為其應(yīng)用程序配置內(nèi)容安全策略并啟用強制可信類型來利用這些特性。

無害化處理與安全環(huán)境

無害化處理會審查不可信的值,并將它們轉(zhuǎn)換成可以安全插入到 DOM 的形式。多數(shù)情況下,這些值并不會在處理過程中發(fā)生任何變化。無害化處理的方式取決于所在的環(huán)境:一個在 CSS 里面無害的值,可能在 URL 里很危險。

Angular 定義了四個安全環(huán)境 - HTML,樣式,URL,和資源 URL:

安全上下文

詳情

HTML

值需要被解釋為 HTML 時使用,比如當(dāng)綁定到 innerHTML 時。

樣式

值需要作為 CSS 綁定到 style 屬性時使用。

URL

值需要被用作 URL 屬性時使用,比如 <a href>。

資源 URL

值需要作為代碼進(jìn)行加載并執(zhí)行,比如 <script src> 中的 URL。

Angular 會對前三項中種不可信的值進(jìn)行無害化處理,但不能對第四種資源 URL 進(jìn)行無害化,因為它們可能包含任何代碼。在開發(fā)模式下,如果在進(jìn)行無害化處理時需要被迫改變一個值,Angular 就會在控制臺上輸出一個警告。

無害化范例

下面的例子綁定了 ?htmlSnippet ?的值,一次把它放進(jìn)插值里,另一次把它綁定到元素的 ?innerHTML ?屬性上。

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

插值的內(nèi)容總會被編碼 - 其中的 HTML 不會被解釋,所以瀏覽器會在元素的文本內(nèi)容中顯示尖括號。

如果希望這段 HTML 被正常解釋,就必須綁定到一個 HTML 屬性上,比如 ?innerHTML?。但是如果把一個可能被攻擊者控制的值綁定到 ?innerHTML ?就會導(dǎo)致 XSS 漏洞。比如,某些人可以用這種方式來執(zhí)行惡意代碼:

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

Angular 認(rèn)為這些值是不安全的,并自動進(jìn)行無害化處理。它會移除 ?script ?元素,但保留安全的內(nèi)容,比如該片段中的 ?<b>? 元素。


避免直接使用 DOM API

除非你強制使用可信類型(Trusted Types),否則瀏覽器內(nèi)置的 DOM API 不會自動保護(hù)你免受安全漏洞的侵害。比如 ?document?、通過 ?ElementRef ?拿到的節(jié)點和很多第三方 API,都可能包含不安全的方法。如果你使用能操縱 DOM 的其它庫,也同樣無法借助像 Angular 插值那樣的自動清理功能。所以,要避免直接和 DOM 打交道,而是盡可能使用 Angular 模板。

瀏覽器內(nèi)置的 DOM API 不會自動針對安全漏洞進(jìn)行防護(hù)。比如,?document?(它可以通過 ?ElementRef ?訪問)以及其它第三方 API 都可能包含不安全的方法。 要避免直接與 DOM 交互,只要可能,就盡量使用 Angular 模板。

信任安全值

有時候,應(yīng)用程序確實需要包含可執(zhí)行的代碼,比如使用 URL 顯示 ?<iframe>?,或者構(gòu)造出有潛在危險的 URL。為了防止在這種情況下被自動無害化,可以告訴 Angular,你已經(jīng)審查了這個值,檢查了它是怎么生成的,并確信它總是安全的。但是千萬要小心!如果你信任了一個可能是惡意的值,就會在應(yīng)用中引入一個安全漏洞。如果你有疑問,請找一個安全專家復(fù)查下。

注入 ?DomSanitizer ?服務(wù),然后調(diào)用下面的方法之一,你就可以把一個值標(biāo)記為可信任的。

  • ?bypassSecurityTrustHtml ?
  • ?bypassSecurityTrustScript ?
  • ?bypassSecurityTrustStyle ?
  • ?bypassSecurityTrustUrl ?
  • ?bypassSecurityTrustResourceUrl?

記住,一個值是否安全取決于它所在的環(huán)境,所以你要為這個值按預(yù)定的用法選擇正確的環(huán)境。假設(shè)下面的模板需要把 ?javascript.alert(...)? 方法綁定到 URL。

<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 并禁止危險的代碼。為了防止這種行為,可以調(diào)用 ?bypassSecurityTrustUrl ?把這個 URL 值標(biāo)記為一個可信任的 URL:

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


如果需要把用戶輸入轉(zhuǎn)換為一個可信任的值,可以在組件方法中處理。下面的模板允許用戶輸入一個 YouTube 視頻的 ID,然后把相應(yīng)的視頻加載到 ?<iframe>? 中。?<iframe src>? 是一個“資源 URL”的安全環(huán)境,因為不可信的源碼可能作為文件下載到本地,被毫無防備的用戶執(zhí)行。所以要調(diào)用一個組件方法來構(gòu)造一個新的、可信任的視頻 URL,這樣 Angular 就會允許把它綁定到 ?<iframe src>?。

<h4>Resource URL:</h4>
<p>Showing: {{dangerousVideoUrl}}</p>
<p>Trusted:</p>
<iframe class="e2e-iframe-trusted-src" width="640" height="390" [src]="videoUrl"></iframe>
<p>Untrusted:</p>
<iframe class="e2e-iframe-untrusted-src" width="640" height="390" [src]="dangerousVideoUrl"></iframe>
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);
}

內(nèi)容安全政策

內(nèi)容安全策略(CSP)是防止 XSS 的深度防御技術(shù)。要啟用 CSP,請將你的 Web 服務(wù)器配置為返回適當(dāng)?shù)?nbsp;?Content-Security-Policy? HTTP 請求頭。在 Google Developers 網(wǎng)站上的《網(wǎng)絡(luò)基礎(chǔ)知識》指南中了解有關(guān)內(nèi)容安全政策的更多信息。

新版 Angular 所需的最小化策略是:

default-src 'self'; style-src 'self' 'unsafe-inline';

區(qū)段

詳情

default-src 'self';

允許此頁面加載所有來自同源的資源。

style-src 'self' 'unsafe-inline';

允許此頁面加載來自同源的全局樣式('self'),并允許組件加載它們的樣式('unsafe-inline' - 參見 angular/angular#6361)。

Angular 本身只需要這些設(shè)置即可正常運行。但是,隨著項目的增長,你可能需要將 CSP 設(shè)置擴(kuò)展到超乎此最小值,以支持應(yīng)用特有的一些其它特性。

強制執(zhí)行可信類型

我們建議使用可信類型來幫助保護(hù)你的應(yīng)用程序免受跨站腳本攻擊??尚蓬愋褪且豁?nbsp;Web 平臺功能,可通過實施更安全的編碼實踐來幫助你防范跨站腳本攻擊??尚蓬愋瓦€可以幫助簡化應(yīng)用程序代碼的審計。

可信類型可能尚未在你的應(yīng)用程序目標(biāo)的所有瀏覽器中可用。如果啟用了可信類型的應(yīng)用程序在不支持可信類型的瀏覽器中運行,應(yīng)用程序的功能將被保留,并且你的應(yīng)用程序?qū)⑼ㄟ^ Angular 的 DomSanitizer 防范 XSS。有關(guān)當(dāng)前瀏覽器支持,請參閱 caniuse.com/trusted-types。

要為你的應(yīng)用程序強制實施可信類型,你必須將應(yīng)用程序的 Web 服務(wù)器配置為使用以下 Angular 策略之一發(fā)出 HTTP 請求頭:

策略

詳情

angular

此策略用于 Angular 內(nèi)部經(jīng)過安全審查的代碼,并且當(dāng)強制執(zhí)行可信類型時,Angular 需要此策略才能正常運行。任何由 Angular 清理的內(nèi)聯(lián)模板值或內(nèi)容都被此政策視為安全的。

angular#unsafe-bypass

此策略用于要使用 Angular 的 ?DomSanitizer ?的各個方法來繞過安全性的應(yīng)用程序,比如 bypassSecurityTrustHtml。任何使用了這些方法的應(yīng)用程序都必須啟用此策略。

angular#unsafe-jit

JIT 編譯器使用此策略。如果你的應(yīng)用程序直接與 JIT 編譯器交互或使用 ?platformBrowserDynamic ?在 JIT 模式下運行,則必須啟用此策略。

你應(yīng)該在以下位置為可信類型配置 HTTP 請求頭:

  • 生產(chǎn)環(huán)境基礎(chǔ)設(shè)施服務(wù)器
  • Angular CLI ( ?ng serve? ),使用 ?angular.json? 文件中的 ?headers ?屬性,用于本地開發(fā)和端到端測試
  • Karma ( ?ng test? ),使用 ?karma.config.js? 文件中的 ?customHeaders ?屬性,進(jìn)行單元測試

以下是為可信類型和 Angular 配置的請求頭示例:

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

以下是為可信類型和 Angular 應(yīng)用程序?qū)iT配置的請求頭示例,這些應(yīng)用程序使用了 Angular ?DomSanitizer ?中那些可以繞過安全性的方法。

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

以下是使用 JIT,且專門為可信類型和 Angular 應(yīng)用程序配置的請求頭示例:

Content-Security-Policy: trusted-types angular angular#unsafe-jit; require-trusted-types-for 'script';
社區(qū)貢獻(xiàn)
要了解關(guān)于如何對可信類型配置進(jìn)行故障排除的更多信息,以下資源可能會有所幫助:
使用可信類型防范基于 DOM 的跨站腳本漏洞

使用 AOT 模板編譯器

AOT 模板編譯器可防止稱為模板注入的一整類漏洞,并大大提高了應(yīng)用程序性能。AOT 模板編譯器是 Angular CLI 應(yīng)用程序使用的默認(rèn)編譯器,你應(yīng)該在所有生產(chǎn)部署中使用它。

AOT 編譯器的替代方法是 JIT 編譯器,它可以在運行時將模板編譯為瀏覽器中的可執(zhí)行模板代碼。Angular 信任這些模板代碼,因此動態(tài)生成模板并進(jìn)行編譯(尤其是包含用戶數(shù)據(jù)的模板)可以規(guī)避 Angular 的內(nèi)置保護(hù),并且是一種安全性方面的反模式。

服務(wù)器端 XSS 保護(hù)

在服務(wù)器上構(gòu)造的 HTML 容易受到注入攻擊。將模板代碼注入到 Angular 應(yīng)用程序中與注入可執(zhí)行代碼是一樣的:它使攻擊者可以完全控制該應(yīng)用程序。為避免這種情況,請使用一種模板語言來自動轉(zhuǎn)義值以防止服務(wù)器上的 XSS 漏洞。不要在服務(wù)器端使用模板語言生成 Angular 模板;這樣做會帶來引入模板注入漏洞的高風(fēng)險。

HTTP 級漏洞

Angular 內(nèi)置了一些支持來防范兩個常見的 HTTP 漏洞:跨站請求偽造(XSRF)和跨站腳本包含(XSSI)。這兩個漏洞主要在服務(wù)器端防范,但是 Angular 也自帶了一些輔助特性,可以讓客戶端的集成變得更容易。

跨站請求偽造

在跨站請求偽造(XSRF 或 CSFR)中,攻擊者欺騙用戶,讓他們訪問一個假冒頁面(比如 ?evil.com?),該頁面帶有惡意代碼,秘密的向你的應(yīng)用程序服務(wù)器發(fā)送惡意請求(比如 ?example-bank.com?)。

假設(shè)用戶已經(jīng)在 ?example-bank.com? 登錄。用戶打開一個郵件,點擊里面的鏈接,在新頁面中打開 ?evil.com?。

該 ?evil.com? 頁面立刻發(fā)送惡意請求到 ?example-bank.com?。這個請求可能是從用戶賬戶轉(zhuǎn)賬到攻擊者的賬戶。與該請求一起,瀏覽器自動發(fā)出 ?example-bank.com? 的 cookie。

如果 ?example-bank.com? 服務(wù)器缺乏 XSRF 保護(hù),就無法辨識請求是從應(yīng)用程序發(fā)來的合法請求還是從 ?evil.com? 來的假請求。

為了防止這種情況,你必須確保每個用戶的請求都是從你自己的應(yīng)用中發(fā)出的,而不是從另一個網(wǎng)站發(fā)出的??蛻舳撕头?wù)器必須合作來抵擋這種攻擊。

常見的反 XSRF 技術(shù)是服務(wù)器隨機(jī)生成一個用戶認(rèn)證令牌到 cookie 中??蛻舳舜a獲取這個 cookie,并用它為接下來所有的請求添加自定義請求頁頭。服務(wù)器比較收到的 cookie 值與請求頁頭的值,如果它們不匹配,便拒絕請求。

這個技術(shù)之所以有效,是因為所有瀏覽器都實現(xiàn)了同源策略。只有設(shè)置 cookie 的網(wǎng)站的代碼可以訪問該站的 cookie,并為該站的請求設(shè)置自定義頁頭。這就是說,只有你的應(yīng)用程序可以獲取這個 cookie 令牌和設(shè)置自定義頁頭。?evil.com? 的惡意代碼不能。

Angular 的 ?HttpClient ?對這項技術(shù)的客戶端部分提供了內(nèi)置的支持。

跨站腳本包含(XSSI)

跨站腳本包含,也被稱為 Json 漏洞,它可以允許一個攻擊者的網(wǎng)站從 JSON API 讀取數(shù)據(jù)。這種攻擊發(fā)生在老的瀏覽器上,它重寫原生 JavaScript 對象的構(gòu)造函數(shù),然后使用 ?<script>? 標(biāo)簽包含一個 API 的 URL。

只有在返回的 JSON 能像 JavaScript 一樣可以被執(zhí)行時,這種攻擊才會生效。所以服務(wù)端會約定給所有 JSON 響應(yīng)體加上前綴 ?")]}',\n"?,來把它們標(biāo)記為不可執(zhí)行的,以防范這種攻擊。

Angular 的 ?HttpClient ?庫會識別這種約定,并在進(jìn)一步解析之前,自動把字符串 ?")]}',\n"? 從所有響應(yīng)中去掉。

要學(xué)習(xí)更多這方面的知識,請參閱谷歌 Web 安全博客文章的 XSSI 小節(jié)。

審計 Angular 應(yīng)用程序

Angular 應(yīng)用應(yīng)該遵循和常規(guī) Web 應(yīng)用一樣的安全原則并按照這些原則進(jìn)行審計。Angular 中某些應(yīng)該在安全評審中被審計的 API(比如?bypassSecurityTrust ?API)都在文檔中被明確標(biāo)記為安全性敏感的。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號