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