docs(security): proofread prose, app now shows good and bad
- App now shows how Angular handles untrusted URLs and resources - E2e test covered new functionality - Copyedits to prose - Updated provider expressions to use latest syntax The original security feature tracker: https://github.com/angular/angular/issues/8511
This commit is contained in:
parent
909f230cc7
commit
705d8c50fd
|
@ -1,7 +1,8 @@
|
||||||
/// <reference path="../_protractor/e2e.d.ts" />
|
/// <reference path="../_protractor/e2e.d.ts" />
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
describe('Security E2E Tests', () => {
|
describe('Security E2E Tests', () => {
|
||||||
beforeAll(function() { browser.get(''); });
|
beforeAll(() => browser.get(''));
|
||||||
|
|
||||||
it('sanitizes innerHTML', () => {
|
it('sanitizes innerHTML', () => {
|
||||||
let interpolated = element(By.className('e2e-inner-html-interpolated'));
|
let interpolated = element(By.className('e2e-inner-html-interpolated'));
|
||||||
|
@ -13,13 +14,23 @@ describe('Security E2E Tests', () => {
|
||||||
expect(bold.getText()).toContain('Syntax');
|
expect(bold.getText()).toContain('Syntax');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('escapes untrusted URLs', () => {
|
||||||
|
let untrustedUrl = element(By.className('e2e-dangerous-url'));
|
||||||
|
expect(untrustedUrl.getAttribute('href')).toMatch(/^unsafe:javascript/);
|
||||||
|
});
|
||||||
|
|
||||||
it('binds trusted URLs', () => {
|
it('binds trusted URLs', () => {
|
||||||
let dangerousUrl = element(By.className('e2e-dangerous-url'));
|
let trustedUrl = element(By.className('e2e-trusted-url'));
|
||||||
expect(dangerousUrl.getAttribute('href')).toMatch(/^javascript:alert/);
|
expect(trustedUrl.getAttribute('href')).toMatch(/^javascript:alert/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('escapes untrusted resource URLs', () => {
|
||||||
|
let iframe = element(By.className('e2e-iframe-untrusted-src'));
|
||||||
|
expect(iframe.getAttribute('src')).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('binds trusted resource URLs', () => {
|
it('binds trusted resource URLs', () => {
|
||||||
let iframe = element(By.className('e2e-iframe'));
|
let iframe = element(By.className('e2e-iframe-trusted-src'));
|
||||||
expect(iframe.getAttribute('src')).toMatch(/^https:\/\/www.youtube.com\//);
|
expect(iframe.getAttribute('src')).toMatch(/^https:\/\/www.youtube.com\//);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { BypassSecurityComponent } from './bypass-security.component';
|
||||||
import { InnerHtmlBindingComponent } from './inner-html-binding.component';
|
import { InnerHtmlBindingComponent } from './inner-html-binding.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'my-app',
|
||||||
template: `
|
template: `
|
||||||
<h1>Security</h1>
|
<h1>Security</h1>
|
||||||
<inner-html-binding></inner-html-binding>
|
<inner-html-binding></inner-html-binding>
|
||||||
|
@ -14,7 +14,7 @@ import { InnerHtmlBindingComponent } from './inner-html-binding.component';
|
||||||
directives: [
|
directives: [
|
||||||
BypassSecurityComponent,
|
BypassSecurityComponent,
|
||||||
InnerHtmlBindingComponent,
|
InnerHtmlBindingComponent,
|
||||||
],
|
]
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,19 @@
|
||||||
<h3>Bypass Security Component</h3>
|
<h3>Bypass Security Component</h3>
|
||||||
|
|
||||||
<!--#docregion dangerous-url -->
|
<!--#docregion dangerous-url -->
|
||||||
<h4>A dangerous URL:</h4>
|
<h4>A untrusted URL:</h4>
|
||||||
<p><a class="e2e-dangerous-url" [href]="dangerousUrl">Click me.</a></p>
|
<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>
|
||||||
<!--#enddocregion dangerous-url -->
|
<!--#enddocregion dangerous-url -->
|
||||||
|
|
||||||
<!--#docregion iframe-videoid -->
|
<!--#docregion iframe-videoid -->
|
||||||
<h4>Resource URL:</h4>
|
<h4>Resource URL:</h4>
|
||||||
<p><label>Showing: <input (input)="updateVideoUrl($event.target.value)"></label></p>
|
<p><label>Showing: <input (input)="updateVideoUrl($event.target.value)"></label></p>
|
||||||
<iframe class="e2e-iframe" width="640" height="390" [src]="videoUrl"></iframe>
|
<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>
|
||||||
<!--#enddocregion iframe-videoid -->
|
<!--#enddocregion iframe-videoid -->
|
||||||
|
|
||||||
<!--#enddocregion -->
|
<!--#enddocregion -->
|
||||||
|
|
|
@ -8,14 +8,18 @@ import { DomSanitizationService, SafeResourceUrl, SafeUrl } from '@angular/platf
|
||||||
templateUrl: 'app/bypass-security.component.html',
|
templateUrl: 'app/bypass-security.component.html',
|
||||||
})
|
})
|
||||||
export class BypassSecurityComponent {
|
export class BypassSecurityComponent {
|
||||||
dangerousUrl: SafeUrl;
|
dangerousUrl: string;
|
||||||
|
trustedUrl: SafeUrl;
|
||||||
|
dangerousVideoUrl: string;
|
||||||
videoUrl: SafeResourceUrl;
|
videoUrl: SafeResourceUrl;
|
||||||
|
|
||||||
// #docregion trust-url
|
// #docregion trust-url
|
||||||
constructor(private sanitizer: DomSanitizationService) {
|
constructor(private sanitizer: DomSanitizationService) {
|
||||||
// javascript: URLs are dangerous if attacker controlled. Angular sanitizes them in data
|
// javascript: URLs are dangerous if attacker controlled.
|
||||||
// binding, but we can explicitly tell Angular to trust this value:
|
// Angular sanitizes them in data binding, but we can
|
||||||
this.dangerousUrl = sanitizer.bypassSecurityTrustUrl('javascript:alert("Hi there")');
|
// explicitly tell Angular to trust this value:
|
||||||
|
this.dangerousUrl = 'javascript:alert("Hi there")';
|
||||||
|
this.trustedUrl = sanitizer.bypassSecurityTrustUrl(this.dangerousUrl);
|
||||||
// #enddocregion trust-url
|
// #enddocregion trust-url
|
||||||
this.updateVideoUrl('PUBnlbjZFAI');
|
this.updateVideoUrl('PUBnlbjZFAI');
|
||||||
}
|
}
|
||||||
|
@ -23,11 +27,12 @@ export class BypassSecurityComponent {
|
||||||
// #docregion trust-video-url
|
// #docregion trust-video-url
|
||||||
updateVideoUrl(id: string) {
|
updateVideoUrl(id: string) {
|
||||||
// Appending an ID to a YouTube URL is safe.
|
// 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
|
// 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.
|
// that it's easier to check if the value is safe.
|
||||||
|
this.dangerousVideoUrl = 'https://www.youtube.com/embed/' + id;
|
||||||
this.videoUrl =
|
this.videoUrl =
|
||||||
this.sanitizer.bypassSecurityTrustResourceUrl('https://www.youtube.com/embed/' + id);
|
this.sanitizer.bypassSecurityTrustResourceUrl(this.dangerousVideoUrl);
|
||||||
}
|
}
|
||||||
// #enddocregion trust-video-url
|
// #enddocregion trust-video-url
|
||||||
}
|
}
|
||||||
// #enddocregion
|
|
||||||
|
|
|
@ -11,4 +11,3 @@ export class InnerHtmlBindingComponent {
|
||||||
// E.g. a user/attacker controlled value from a URL.
|
// E.g. a user/attacker controlled value from a URL.
|
||||||
htmlSnippet = 'Template <script>alert("0wned")</script> <b>Syntax</b>';
|
htmlSnippet = 'Template <script>alert("0wned")</script> <b>Syntax</b>';
|
||||||
}
|
}
|
||||||
// #enddocregion inner-html-controller
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<!-- #docregion -->
|
<!-- #docregion -->
|
||||||
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Angular Content Security</title>
|
<title>Angular Content Security</title>
|
||||||
|
@ -21,6 +21,6 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<app-root>Loading...</app-root>
|
<my-app>Loading...</my-app>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
block includes
|
block includes
|
||||||
include ../_util-fns
|
include ../_util-fns
|
||||||
:marked
|
:marked
|
||||||
Web application security has many aspects. This documentation describes Angular's built in
|
Web application security has many aspects. This chapter describes Angular's built in
|
||||||
protections against common web application vulnerabilities and attacks, such as Cross Site
|
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
|
Scripting Attacks. It does not cover application level security, such as authentication (_Who is
|
||||||
this user?_) or authorization (_What can this user do?_).
|
this user?_) or authorization (_What can this user do?_).
|
||||||
|
@ -50,7 +50,7 @@ h2#best-practices Best Practices
|
||||||
h2#xss Preventing Cross-Site Scripting (XSS)
|
h2#xss Preventing Cross-Site Scripting (XSS)
|
||||||
:marked
|
:marked
|
||||||
[Cross-Site Scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) enables attackers
|
[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
|
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
|
particular their login data), or perform actions impersonating the user. This is one of the most
|
||||||
common attacks on the web.
|
common attacks on the web.
|
||||||
|
|
||||||
|
@ -81,10 +81,10 @@ h2#xss Preventing Cross-Site Scripting (XSS)
|
||||||
|
|
||||||
Angular defines four security contexts: HTML, style, URL, and resource URL.
|
Angular defines four security contexts: HTML, style, URL, and resource URL.
|
||||||
|
|
||||||
* HTML is used when interpreting a value as HTML, e.g. when binding to `innerHtml`
|
* HTML is used when interpreting a value as HTML, e.g., when binding to `innerHtml`
|
||||||
* Style is used when binding CSS into the `style` property
|
* Style is used when binding CSS into the `style` property
|
||||||
* URL is used for URL properties such as `<a href>`
|
* URL is used for URL properties such as `<a href>`
|
||||||
* Resource URLs are URLs that will be loaded and executed as code, e.g. in `<script src>`
|
* Resource URLs are URLs that will be loaded and executed as code, e.g., in `<script src>`
|
||||||
|
|
||||||
Angular sanitizes untrusted values for the first three items; sanitizing resource URLs is not
|
Angular sanitizes untrusted values for the first three items; sanitizing resource URLs is not
|
||||||
possible as they contain arbitrary code. In development mode, Angular prints a console warning
|
possible as they contain arbitrary code. In development mode, Angular prints a console warning
|
||||||
|
@ -95,7 +95,8 @@ h2#xss Preventing Cross-Site Scripting (XSS)
|
||||||
The template below binds the value of `htmlSnippet`, once by interpolating it into an element's
|
The template below binds the value of `htmlSnippet`, once by interpolating it into an element's
|
||||||
content, and once by binding it to the `innerHTML` property of an element.
|
content, and once by binding it to the `innerHTML` property of an element.
|
||||||
|
|
||||||
+makeExample('security/ts/app/inner-html-binding.component.html')(format=".")
|
+makeExcerpt('app/inner-html-binding.component.html')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Interpolated content is always escaped - the HTML is not interpreted, and the browser displays
|
Interpolated content is always escaped - the HTML is not interpreted, and the browser displays
|
||||||
angle brackets in the elements text content.
|
angle brackets in the elements text content.
|
||||||
|
@ -121,8 +122,7 @@ figure.image-display
|
||||||
|
|
||||||
### Content Security Policy
|
### Content Security Policy
|
||||||
|
|
||||||
A [Content Security Policy (CSP)]
|
A [Content Security Policy (CSP)](http://www.html5rocks.com/en/tutorials/security/content-security-policy/) is a defense-in-depth
|
||||||
(http://www.html5rocks.com/en/tutorials/security/content-security-policy/) is a defense-in-depth
|
|
||||||
technique to prevent XSS. To enable CSP, configure your web server to return an appropriate
|
technique to prevent XSS. To enable CSP, configure your web server to return an appropriate
|
||||||
`Content-Security-Policy` HTTP header.
|
`Content-Security-Policy` HTTP header.
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ figure.image-display
|
||||||
### Server side XSS protection
|
### Server side XSS protection
|
||||||
|
|
||||||
HTML constructed on the server is vulnerable to injection attacks. Injecting template code into an
|
HTML constructed on the server is vulnerable to injection attacks. Injecting template code into an
|
||||||
Angular application is the same as injecting executable code (e.g. JavaScript) into the
|
Angular application is the same as injecting executable code (e.g., JavaScript) into the
|
||||||
application; it gives the attacker full control over the application. To prevent this, make sure
|
application; it gives the attacker full control over the application. To prevent this, make sure
|
||||||
to use a templating language that automatically escapes values to prevent XSS vulnerabilities on
|
to use a templating language that automatically escapes values to prevent XSS vulnerabilities on
|
||||||
the server. Do not generate Angular templates on the server side using a templating language, this
|
the server. Do not generate Angular templates on the server side using a templating language, this
|
||||||
|
@ -181,7 +181,7 @@ figure.image-display
|
||||||
If we need to convert user input into a trusted value, it can be convenient to do so in a
|
If we need to convert user input into a trusted value, it can be convenient to do so in a
|
||||||
controller method. The template below allows users to enter a YouTube video ID, and load the
|
controller method. The template below allows users to enter a YouTube video ID, and load the
|
||||||
corresponding video in an `<iframe>`. The `<iframe src>` attribute is a resource URL security
|
corresponding video in an `<iframe>`. The `<iframe src>` attribute is a resource URL security
|
||||||
context, because an untrusted source can e.g. smuggle in file downloads that unsuspecting users
|
context, because an untrusted source can, e.g., smuggle in file downloads that unsuspecting users
|
||||||
would execute. So we call a method on the controller to construct a trusted video URL, which
|
would execute. So we call a method on the controller to construct a trusted video URL, which
|
||||||
Angular then allows binding into `<iframe src>`.
|
Angular then allows binding into `<iframe src>`.
|
||||||
|
|
||||||
|
@ -198,12 +198,12 @@ h2#http HTTP-level Vulnerabilities
|
||||||
h3#xsrf Cross-site Request Forgery (XSRF)
|
h3#xsrf Cross-site Request Forgery (XSRF)
|
||||||
:marked
|
:marked
|
||||||
In a Cross-site Request Forgery (XSRF or CSRF), an attacker tricks the user into visiting a
|
In a Cross-site Request Forgery (XSRF or CSRF), an attacker tricks the user into visiting a
|
||||||
_different_ page, and has them e.g. submit a form that sends a request to your application's
|
_different_ page, and has them, e.g., submit a form that sends a request to your application's
|
||||||
web server. If the user is logged into your application, the browser will send authentication
|
web server. If the user is logged into your application, the browser will send authentication
|
||||||
cookies, and the attacker could - for example - cause a bank transfer in the user's name with
|
cookies, and the attacker could — for example — cause a bank transfer in the user's name with
|
||||||
the right request.
|
the right request.
|
||||||
|
|
||||||
To prevent this, your application must make sure that user requests originate in your own
|
To prevent this, your application must ensure that user requests originate in your own
|
||||||
application, not on a different site. A common technique is that the server sends a randomly
|
application, not on a different site. A common technique is that the server sends a randomly
|
||||||
generated authentication token in a cookie, often with the name `XSRF-TOKEN`. Cookies can only
|
generated authentication token in a cookie, often with the name `XSRF-TOKEN`. Cookies can only
|
||||||
be read by the website on which they are set, so only your own application can read this token. On
|
be read by the website on which they are set, so only your own application can read this token. On
|
||||||
|
@ -220,14 +220,17 @@ h3#xsrf Cross-site Request Forgery (XSRF)
|
||||||
|
|
||||||
Angular applications can customize cookie and header names by binding their own
|
Angular applications can customize cookie and header names by binding their own
|
||||||
`CookieXSRFStrategy` value, or implement an entirely custom `XSRFStrategy` by providing a custom
|
`CookieXSRFStrategy` value, or implement an entirely custom `XSRFStrategy` by providing a custom
|
||||||
binding for that type, by adding
|
binding for that type, by adding either of the following to your providers list:
|
||||||
`provide(XSRFStrategy, {useValue: new CookieXSRFStrategy('myCookieName', 'My-Header-Name')})` or
|
|
||||||
`provide(XSRFStrategy, {useClass: MyXSRFStrategy})` to your providers list.
|
|
||||||
|
|
||||||
|
code-example(language="typescript").
|
||||||
|
{ provide: XSRFStrategy, useValue: new CookieXSRFStrategy('myCookieName', 'My-Header-Name')}
|
||||||
|
{ provide: XSRFStrategy, useClass: MyXSRFStrategy}
|
||||||
|
|
||||||
|
:marked
|
||||||
Learn about Cross Site Request Forgery (XSRF) at the Open Web Application Security Project (OWASP)
|
Learn about Cross Site Request Forgery (XSRF) at the Open Web Application Security Project (OWASP)
|
||||||
[here](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29) and
|
[here](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29) and
|
||||||
[here](https://www.owasp.org/index.php/CSRF_Prevention_Cheat_Sheet). This [Stanford University
|
[here](https://www.owasp.org/index.php/CSRF_Prevention_Cheat_Sheet). This [Stanford University
|
||||||
paper](https://seclab.stanford.edu/websec/csrf/csrf.pdf) is a rich source of detail.
|
paper](https://seclab.stanford.edu/websec/csrf/csrf.pdf) is also a rich source of detail.
|
||||||
|
|
||||||
h3#xssi Cross-site Script Inclusion (XSSI)
|
h3#xssi Cross-site Script Inclusion (XSSI)
|
||||||
:marked
|
:marked
|
||||||
|
|
Loading…
Reference in New Issue