本主題會講述 Angular 為防范 Web 應用常見的安全漏洞和攻擊(比如跨站腳本攻擊)內置的保護措施,但不會涉及應用級安全,比如用戶認證(這個用戶是誰?)和授權(這個用戶能做什么?)。
你可以運行現場演練 / 下載范例,在 Stackblitz 中試用并下載本頁的代碼。
舉報漏洞
給我們(security@angular.io)發(fā)郵件,報告 Angular 本身的漏洞。
要了解關于“谷歌如何處理安全問題”的更多信息,參閱谷歌的安全哲學。
最佳實踐
實踐
詳情
及時把 Angular 包更新到最新版本
我們會頻繁的更新 Angular 庫,這些更新可能會修復之前版本中發(fā)現的安全漏洞。查看 Angular 的更新記錄,了解與安全有關的更新。
不要修改你的 Angular 副本
私有的、定制版的 Angular 往往跟不上最新版本,這可能導致你忽略重要的安全修復與增強。反之,應該在社區(qū)共享你對 Angular 所做的改進并創(chuàng)建 Pull Request。.
避免使用本文檔中帶“安全風險”標記的 Angular API
要了解更多信息,請參閱本章的信任安全值部分。
跨站腳本(XSS)允許攻擊者將惡意代碼注入到頁面中。這些代碼可以偷取用戶數據(特別是它們的登錄數據),還可以冒充用戶執(zhí)行操作。它是 Web 上最常見的攻擊方式之一。
為了防范 XSS 攻擊,你必須阻止惡意代碼進入 DOM。比如,如果某個攻擊者能騙你把 ?<script>
? 標簽插入到 DOM,就可以在你的網站上運行任何代碼。除了 ?<script>
?,攻擊者還可以使用很多 DOM 元素和屬性來執(zhí)行代碼,比如 ?<img onerror="...">
?、?<a href="javascript:...">
?。如果攻擊者所控制的數據混進了 DOM,就會導致安全漏洞。
為了系統(tǒng)性的防范 XSS 問題,Angular 默認把所有值都當做不可信任的。 當值從模板中以屬性(Property)、DOM 元素屬性(Attribte)、CSS 類綁定或插值等途徑插入到 DOM 中的時候, Angular 將對這些值進行無害化處理(Sanitize),對不可信的值進行編碼。如果某個值已經在 Angular 之外進行過無害化處理,可以確信是安全的,可以把這個值標記為安全的來把這一點通知 Angular。
與用于渲染的值不同,默認情況下,Angular 模板被認為是受信任的,應被視為可執(zhí)行代碼。切勿通過串聯用戶輸入和模板語法來生成模板。這樣做會使攻擊者能夠將任意代碼注入你的應用程序。為避免這些漏洞,請始終在生產部署中使用默認的 AOT 模板編譯器。
借助內容安全策略和可信類型,可以提供額外的保護層。這些 Web 平臺特性會在 DOM 級別運行,這是用來防范 XSS 問題的最有效位置,因為即使使用其它低級 API 也無法繞過它們。出于這個原因,我們強烈建議開發(fā)人員通過為其應用程序配置內容安全策略并啟用強制可信類型來利用這些特性。
無害化處理會審查不可信的值,并將它們轉換成可以安全插入到 DOM 的形式。多數情況下,這些值并不會在處理過程中發(fā)生任何變化。無害化處理的方式取決于所在的環(huán)境:一個在 CSS 里面無害的值,可能在 URL 里很危險。
Angular 定義了四個安全環(huán)境 - HTML,樣式,URL,和資源 URL:
安全上下文 |
詳情 |
---|---|
HTML |
值需要被解釋為 HTML 時使用,比如當綁定到 |
樣式 |
值需要作為 CSS 綁定到 |
URL |
值需要被用作 URL 屬性時使用,比如 |
資源 URL |
值需要作為代碼進行加載并執(zhí)行,比如 |
Angular 會對前三項中種不可信的值進行無害化處理,但不能對第四種資源 URL 進行無害化,因為它們可能包含任何代碼。在開發(fā)模式下,如果在進行無害化處理時需要被迫改變一個值,Angular 就會在控制臺上輸出一個警告。
下面的例子綁定了 ?htmlSnippet
?的值,一次把它放進插值里,另一次把它綁定到元素的 ?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>
插值的內容總會被編碼 - 其中的 HTML 不會被解釋,所以瀏覽器會在元素的文本內容中顯示尖括號。
如果希望這段 HTML 被正常解釋,就必須綁定到一個 HTML 屬性上,比如 ?innerHTML
?。但是如果把一個可能被攻擊者控制的值綁定到 ?innerHTML
?就會導致 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 認為這些值是不安全的,并自動進行無害化處理。它會移除 ?script
?元素,但保留安全的內容,比如該片段中的 ?<b>
? 元素。
除非你強制使用可信類型(Trusted Types),否則瀏覽器內置的 DOM API 不會自動保護你免受安全漏洞的侵害。比如 ?document
?、通過 ?ElementRef
?拿到的節(jié)點和很多第三方 API,都可能包含不安全的方法。如果你使用能操縱 DOM 的其它庫,也同樣無法借助像 Angular 插值那樣的自動清理功能。所以,要避免直接和 DOM 打交道,而是盡可能使用 Angular 模板。
瀏覽器內置的 DOM API 不會自動針對安全漏洞進行防護。比如,?document
?(它可以通過 ?ElementRef
?訪問)以及其它第三方 API 都可能包含不安全的方法。 要避免直接與 DOM 交互,只要可能,就盡量使用 Angular 模板。
有時候,應用程序確實需要包含可執(zhí)行的代碼,比如使用 URL 顯示 ?<iframe>
?,或者構造出有潛在危險的 URL。為了防止在這種情況下被自動無害化,可以告訴 Angular,你已經審查了這個值,檢查了它是怎么生成的,并確信它總是安全的。但是千萬要小心!如果你信任了一個可能是惡意的值,就會在應用中引入一個安全漏洞。如果你有疑問,請找一個安全專家復查下。
注入 ?DomSanitizer
?服務,然后調用下面的方法之一,你就可以把一個值標記為可信任的。
bypassSecurityTrustHtml
?bypassSecurityTrustScript
?bypassSecurityTrustStyle
?bypassSecurityTrustUrl
?bypassSecurityTrustResourceUrl
?記住,一個值是否安全取決于它所在的環(huán)境,所以你要為這個值按預定的用法選擇正確的環(huán)境。假設下面的模板需要把 ?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 并禁止危險的代碼。為了防止這種行為,可以調用 ?bypassSecurityTrustUrl
?把這個 URL 值標記為一個可信任的 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);
如果需要把用戶輸入轉換為一個可信任的值,可以在組件方法中處理。下面的模板允許用戶輸入一個 YouTube 視頻的 ID,然后把相應的視頻加載到 ?<iframe>
? 中。?<iframe src>
? 是一個“資源 URL”的安全環(huán)境,因為不可信的源碼可能作為文件下載到本地,被毫無防備的用戶執(zhí)行。所以要調用一個組件方法來構造一個新的、可信任的視頻 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);
}
內容安全策略(CSP)是防止 XSS 的深度防御技術。要啟用 CSP,請將你的 Web 服務器配置為返回適當的 ?Content-Security-Policy
? HTTP 請求頭。在 Google Developers 網站上的《網絡基礎知識》指南中了解有關內容安全政策的更多信息。
新版 Angular 所需的最小化策略是:
default-src 'self'; style-src 'self' 'unsafe-inline';
區(qū)段 |
詳情 |
---|---|
default-src 'self';
|
允許此頁面加載所有來自同源的資源。 |
style-src 'self' 'unsafe-inline';
|
允許此頁面加載來自同源的全局樣式( |
Angular 本身只需要這些設置即可正常運行。但是,隨著項目的增長,你可能需要將 CSP 設置擴展到超乎此最小值,以支持應用特有的一些其它特性。
我們建議使用可信類型來幫助保護你的應用程序免受跨站腳本攻擊??尚蓬愋褪且豁?nbsp;Web 平臺功能,可通過實施更安全的編碼實踐來幫助你防范跨站腳本攻擊??尚蓬愋瓦€可以幫助簡化應用程序代碼的審計。
可信類型可能尚未在你的應用程序目標的所有瀏覽器中可用。如果啟用了可信類型的應用程序在不支持可信類型的瀏覽器中運行,應用程序的功能將被保留,并且你的應用程序將通過 Angular 的 DomSanitizer 防范 XSS。有關當前瀏覽器支持,請參閱 caniuse.com/trusted-types。
要為你的應用程序強制實施可信類型,你必須將應用程序的 Web 服務器配置為使用以下 Angular 策略之一發(fā)出 HTTP 請求頭:
策略 |
詳情 |
---|---|
angular
|
此策略用于 Angular 內部經過安全審查的代碼,并且當強制執(zhí)行可信類型時,Angular 需要此策略才能正常運行。任何由 Angular 清理的內聯模板值或內容都被此政策視為安全的。 |
angular#unsafe-bypass
|
此策略用于要使用 Angular 的 ? |
angular#unsafe-jit
|
JIT 編譯器使用此策略。如果你的應用程序直接與 JIT 編譯器交互或使用 ? |
你應該在以下位置為可信類型配置 HTTP 請求頭:
ng serve
? ),使用 ?angular.json
? 文件中的 ?headers
?屬性,用于本地開發(fā)和端到端測試
ng test
? ),使用 ?karma.config.js
? 文件中的 ?customHeaders
?屬性,進行單元測試以下是為可信類型和 Angular 配置的請求頭示例:
Content-Security-Policy: trusted-types angular; require-trusted-types-for 'script';
以下是為可信類型和 Angular 應用程序專門配置的請求頭示例,這些應用程序使用了 Angular ?DomSanitizer
?中那些可以繞過安全性的方法。
Content-Security-Policy: trusted-types angular angular#unsafe-bypass; require-trusted-types-for 'script';
以下是使用 JIT,且專門為可信類型和 Angular 應用程序配置的請求頭示例:
Content-Security-Policy: trusted-types angular angular#unsafe-jit; require-trusted-types-for 'script';
社區(qū)貢獻
要了解關于如何對可信類型配置進行故障排除的更多信息,以下資源可能會有所幫助:
使用可信類型防范基于 DOM 的跨站腳本漏洞
AOT 模板編譯器可防止稱為模板注入的一整類漏洞,并大大提高了應用程序性能。AOT 模板編譯器是 Angular CLI 應用程序使用的默認編譯器,你應該在所有生產部署中使用它。
AOT 編譯器的替代方法是 JIT 編譯器,它可以在運行時將模板編譯為瀏覽器中的可執(zhí)行模板代碼。Angular 信任這些模板代碼,因此動態(tài)生成模板并進行編譯(尤其是包含用戶數據的模板)可以規(guī)避 Angular 的內置保護,并且是一種安全性方面的反模式。
在服務器上構造的 HTML 容易受到注入攻擊。將模板代碼注入到 Angular 應用程序中與注入可執(zhí)行代碼是一樣的:它使攻擊者可以完全控制該應用程序。為避免這種情況,請使用一種模板語言來自動轉義值以防止服務器上的 XSS 漏洞。不要在服務器端使用模板語言生成 Angular 模板;這樣做會帶來引入模板注入漏洞的高風險。
Angular 內置了一些支持來防范兩個常見的 HTTP 漏洞:跨站請求偽造(XSRF)和跨站腳本包含(XSSI)。這兩個漏洞主要在服務器端防范,但是 Angular 也自帶了一些輔助特性,可以讓客戶端的集成變得更容易。
在跨站請求偽造(XSRF 或 CSFR)中,攻擊者欺騙用戶,讓他們訪問一個假冒頁面(比如 ?evil.com
?),該頁面帶有惡意代碼,秘密的向你的應用程序服務器發(fā)送惡意請求(比如 ?example-bank.com
?)。
假設用戶已經在 ?example-bank.com
? 登錄。用戶打開一個郵件,點擊里面的鏈接,在新頁面中打開 ?evil.com
?。
該 ?evil.com
? 頁面立刻發(fā)送惡意請求到 ?example-bank.com
?。這個請求可能是從用戶賬戶轉賬到攻擊者的賬戶。與該請求一起,瀏覽器自動發(fā)出 ?example-bank.com
? 的 cookie。
如果 ?example-bank.com
? 服務器缺乏 XSRF 保護,就無法辨識請求是從應用程序發(fā)來的合法請求還是從 ?evil.com
? 來的假請求。
為了防止這種情況,你必須確保每個用戶的請求都是從你自己的應用中發(fā)出的,而不是從另一個網站發(fā)出的。客戶端和服務器必須合作來抵擋這種攻擊。
常見的反 XSRF 技術是服務器隨機生成一個用戶認證令牌到 cookie 中??蛻舳舜a獲取這個 cookie,并用它為接下來所有的請求添加自定義請求頁頭。服務器比較收到的 cookie 值與請求頁頭的值,如果它們不匹配,便拒絕請求。
這個技術之所以有效,是因為所有瀏覽器都實現了同源策略。只有設置 cookie 的網站的代碼可以訪問該站的 cookie,并為該站的請求設置自定義頁頭。這就是說,只有你的應用程序可以獲取這個 cookie 令牌和設置自定義頁頭。?evil.com
? 的惡意代碼不能。
Angular 的 ?HttpClient
?對這項技術的客戶端部分提供了內置的支持。
跨站腳本包含,也被稱為 Json 漏洞,它可以允許一個攻擊者的網站從 JSON API 讀取數據。這種攻擊發(fā)生在老的瀏覽器上,它重寫原生 JavaScript 對象的構造函數,然后使用 ?<script>
? 標簽包含一個 API 的 URL。
只有在返回的 JSON 能像 JavaScript 一樣可以被執(zhí)行時,這種攻擊才會生效。所以服務端會約定給所有 JSON 響應體加上前綴 ?")]}',\n"
?,來把它們標記為不可執(zhí)行的,以防范這種攻擊。
Angular 的 ?HttpClient
?庫會識別這種約定,并在進一步解析之前,自動把字符串 ?")]}',\n"
? 從所有響應中去掉。
要學習更多這方面的知識,請參閱谷歌 Web 安全博客文章的 XSSI 小節(jié)。
Angular 應用應該遵循和常規(guī) Web 應用一樣的安全原則并按照這些原則進行審計。Angular 中某些應該在安全評審中被審計的 API(比如?bypassSecurityTrust
?API)都在文檔中被明確標記為安全性敏感的。
更多建議: