diff --git a/public/docs/_examples/security/e2e-spec.ts b/public/docs/_examples/security/e2e-spec.ts new file mode 100644 index 0000000000..410f1428e1 --- /dev/null +++ b/public/docs/_examples/security/e2e-spec.ts @@ -0,0 +1,25 @@ +/// +'use strict'; +describe('Security E2E Tests', () => { + beforeAll(function() { browser.get(''); }); + + it('sanitizes innerHTML', () => { + let interpolated = element(By.className('e2e-inner-html-interpolated')); + expect(interpolated.getText()) + .toContain('Template Syntax'); + let bound = element(By.className('e2e-inner-html-bound')); + expect(bound.getText()).toContain('Template alert("0wned") Syntax'); + let bold = element(By.css('.e2e-inner-html-bound b')); + expect(bold.getText()).toContain('Syntax'); + }); + + it('binds trusted URLs', () => { + let dangerousUrl = element(By.className('e2e-dangerous-url')); + expect(dangerousUrl.getAttribute('href')).toMatch(/^javascript:alert/); + }); + + it('binds trusted resource URLs', () => { + let iframe = element(By.className('e2e-iframe')); + expect(iframe.getAttribute('src')).toMatch(/^https:\/\/www.youtube.com\//); + }); +}); diff --git a/public/docs/_examples/security/ts/app/app.component.ts b/public/docs/_examples/security/ts/app/app.component.ts new file mode 100644 index 0000000000..153e6b9e49 --- /dev/null +++ b/public/docs/_examples/security/ts/app/app.component.ts @@ -0,0 +1,20 @@ +// #docregion +import { Component } from '@angular/core'; + +import { BypassSecurityComponent } from './bypass-security.component'; +import { InnerHtmlBindingComponent } from './inner-html-binding.component'; + +@Component({ + selector: 'app-root', + template: ` +

Security

+ + + `, + directives: [ + BypassSecurityComponent, + InnerHtmlBindingComponent, + ], +}) +export class AppComponent { +} diff --git a/public/docs/_examples/security/ts/app/bypass-security.component.html b/public/docs/_examples/security/ts/app/bypass-security.component.html new file mode 100644 index 0000000000..75b7734dd4 --- /dev/null +++ b/public/docs/_examples/security/ts/app/bypass-security.component.html @@ -0,0 +1,15 @@ + +

Bypass Security Component

+ + +

A dangerous URL:

+

Click me.

+ + + +

Resource URL:

+

+ + + + diff --git a/public/docs/_examples/security/ts/app/bypass-security.component.ts b/public/docs/_examples/security/ts/app/bypass-security.component.ts new file mode 100644 index 0000000000..6a5f350d28 --- /dev/null +++ b/public/docs/_examples/security/ts/app/bypass-security.component.ts @@ -0,0 +1,33 @@ +// #docplaster +// #docregion +import { Component } from '@angular/core'; +import { DomSanitizationService, SafeResourceUrl, SafeUrl } from '@angular/platform-browser'; + +@Component({ + selector: 'bypass-security', + templateUrl: 'app/bypass-security.component.html', +}) +export class BypassSecurityComponent { + dangerousUrl: SafeUrl; + videoUrl: SafeResourceUrl; + + // #docregion trust-url + constructor(private sanitizer: DomSanitizationService) { + // javascript: URLs are dangerous if attacker controlled. Angular sanitizes them in data + // binding, but we can explicitly tell Angular to trust this value: + this.dangerousUrl = sanitizer.bypassSecurityTrustUrl('javascript:alert("Hi there")'); + // #enddocregion trust-url + this.updateVideoUrl('PUBnlbjZFAI'); + } + + // #docregion trust-video-url + 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.videoUrl = + this.sanitizer.bypassSecurityTrustResourceUrl('https://www.youtube.com/embed/' + id); + } + // #enddocregion trust-video-url +} +// #enddocregion diff --git a/public/docs/_examples/security/ts/app/inner-html-binding.component.html b/public/docs/_examples/security/ts/app/inner-html-binding.component.html new file mode 100644 index 0000000000..fe540d25fe --- /dev/null +++ b/public/docs/_examples/security/ts/app/inner-html-binding.component.html @@ -0,0 +1,6 @@ + +

Binding innerHTML

+

Bound value:

+

{{htmlSnippet}}

+

Result of binding to innerHTML:

+

diff --git a/public/docs/_examples/security/ts/app/inner-html-binding.component.ts b/public/docs/_examples/security/ts/app/inner-html-binding.component.ts new file mode 100644 index 0000000000..95a9f55979 --- /dev/null +++ b/public/docs/_examples/security/ts/app/inner-html-binding.component.ts @@ -0,0 +1,14 @@ +// #docregion +import { Component } from '@angular/core'; + +@Component({ + moduleId: module.id, + selector: 'inner-html-binding', + templateUrl: 'inner-html-binding.component.html', +}) +// #docregion inner-html-controller +export class InnerHtmlBindingComponent { + // E.g. a user/attacker controlled value from a URL. + htmlSnippet = 'Template Syntax'; +} +// #enddocregion inner-html-controller diff --git a/public/docs/_examples/security/ts/app/main.ts b/public/docs/_examples/security/ts/app/main.ts new file mode 100644 index 0000000000..3e1476beac --- /dev/null +++ b/public/docs/_examples/security/ts/app/main.ts @@ -0,0 +1,8 @@ +// #docregion +import { bootstrap } from '@angular/platform-browser-dynamic'; + +// #docregion import +import { AppComponent } from './app.component'; +// #enddocregion import + +bootstrap(AppComponent); diff --git a/public/docs/_examples/security/ts/example-config.json b/public/docs/_examples/security/ts/example-config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/public/docs/_examples/security/ts/index.html b/public/docs/_examples/security/ts/index.html new file mode 100644 index 0000000000..b665a8bc6f --- /dev/null +++ b/public/docs/_examples/security/ts/index.html @@ -0,0 +1,26 @@ + + + + + Angular Content Security + + + + + + + + + + + + + + + + + Loading... + + diff --git a/public/docs/_examples/security/ts/plnkr.json b/public/docs/_examples/security/ts/plnkr.json new file mode 100644 index 0000000000..e66527cb6b --- /dev/null +++ b/public/docs/_examples/security/ts/plnkr.json @@ -0,0 +1,8 @@ +{ + "description": "Content Security", + "files": [ + "!**/*.d.ts", + "!**/*.js" + ], + "tags": ["security"] +} diff --git a/public/docs/dart/latest/guide/_data.json b/public/docs/dart/latest/guide/_data.json index eed8cf36a4..7d25438019 100644 --- a/public/docs/dart/latest/guide/_data.json +++ b/public/docs/dart/latest/guide/_data.json @@ -73,6 +73,11 @@ "intro": "Learn how to apply CSS styles to components." }, + "security": { + "title": "Security", + "intro": "Prevent security vulnerabilities" + }, + "hierarchical-dependency-injection": { "title": "Hierarchical Dependency Injectors", "navTitle": "Hierarchical Injectors", diff --git a/public/docs/dart/latest/guide/security.jade b/public/docs/dart/latest/guide/security.jade new file mode 100644 index 0000000000..f8df2a84a6 --- /dev/null +++ b/public/docs/dart/latest/guide/security.jade @@ -0,0 +1 @@ +!= partial("../../../_includes/_ts-temp") \ No newline at end of file diff --git a/public/docs/js/latest/guide/_data.json b/public/docs/js/latest/guide/_data.json index 939d7c5c4a..437e7df624 100644 --- a/public/docs/js/latest/guide/_data.json +++ b/public/docs/js/latest/guide/_data.json @@ -73,6 +73,11 @@ "intro": "Learn how to apply CSS styles to components." }, + "security": { + "title": "Security", + "intro": "Prevent security vulnerabilities" + }, + "hierarchical-dependency-injection": { "title": "Hierarchical Dependency Injectors", "navTitle": "Hierarchical Injectors", diff --git a/public/docs/js/latest/guide/security.jade b/public/docs/js/latest/guide/security.jade new file mode 100644 index 0000000000..f8df2a84a6 --- /dev/null +++ b/public/docs/js/latest/guide/security.jade @@ -0,0 +1 @@ +!= partial("../../../_includes/_ts-temp") \ No newline at end of file diff --git a/public/docs/ts/latest/guide/_data.json b/public/docs/ts/latest/guide/_data.json index 19d65f49b2..ca414862e5 100644 --- a/public/docs/ts/latest/guide/_data.json +++ b/public/docs/ts/latest/guide/_data.json @@ -78,6 +78,11 @@ "intro": "Learn how to apply CSS styles to components." }, + "security": { + "title": "Security", + "intro": "Prevent security vulnerabilities" + }, + "hierarchical-dependency-injection": { "title": "Hierarchical Dependency Injectors", "navTitle": "Hierarchical Injectors", diff --git a/public/docs/ts/latest/guide/security.jade b/public/docs/ts/latest/guide/security.jade new file mode 100644 index 0000000000..b937f6214b --- /dev/null +++ b/public/docs/ts/latest/guide/security.jade @@ -0,0 +1,246 @@ +block includes + include ../_util-fns +:marked + # Security + Web application security has many aspects. This documentation describes Angular's built in + protections against common web application vulnerabilities and attacks, such as Cross Site + Scripting Attacks. It does not cover application level security, such as authentication (_Who is + this user?_) or authorization (_What can this user do?_). + + The [Open Web Application Security Project (OWASP)](https://www.owasp.org/index.php/Category:OWASP_Guide_Project) + has further information on the attacks and mitigations described below. + +.l-main-section +:marked + # Table Of Contents + + * [Reporting Vulnerabilities](#report-issues) + * [Best Practices](#best-practices) + * [Preventing Cross-Site Scripting (XSS)](#xss) + * [Trusting Safe Values](#bypass-security-apis) + * [HTTP-level Vulnerabilities](#http) + * [Auditing Angular Applications](#code-review) + +p Try the #[+liveExampleLink2()] of the code shown in this chapter. + +.l-main-section +h2#report-issues Reporting Vulnerabilities +:marked + Email us at [security@angular.io](mailto:security@angular.io) to report vulnerabilities in + Angular itself. + + For further details on how Google handles security issues please refer to [Google's security + philosophy](https://www.google.com/about/appsecurity/). + +.l-main-section +h2#best-practices Best Practices +:marked + * **Keep current with the latest Angular library releases.** + We regularly update our Angular libraries and these updates may fix security defects discovered in + previous version. Check the Angular [change + log](https://github.com/angular/angular/blob/master/CHANGELOG.md) for security-related updates. + + * **Don't modify your copy of Angular.** + Private, customized versions of Angular tend to fall behind the current version and may neglect + important security fixes and enhancements. Instead, share your Angular improvements with the + community and make a pull request. + + * **Avoid Angular APIs marked in the documentation as “[_Security Risk_](#bypass-security-apis)”.** + +.l-main-section +h2#xss Preventing Cross-Site Scripting (XSS) +:marked + [Cross-Site Scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) enables attackers + to inject malicious code into web pages. Such code can then for example steal user's data (in + particular their login data), or perform actions impersonating the user. This is one of the most + common attacks on the web. + + To block XSS attacks, we must prevent malicious code from entering the DOM. For example, if an + attacker can trick us into inserting a `