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" />
|
||||
'use strict';
|
||||
|
||||
describe('Security E2E Tests', () => {
|
||||
beforeAll(function() { browser.get(''); });
|
||||
beforeAll(() => browser.get(''));
|
||||
|
||||
it('sanitizes innerHTML', () => {
|
||||
let interpolated = element(By.className('e2e-inner-html-interpolated'));
|
||||
|
@ -13,13 +14,23 @@ describe('Security E2E Tests', () => {
|
|||
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', () => {
|
||||
let dangerousUrl = element(By.className('e2e-dangerous-url'));
|
||||
expect(dangerousUrl.getAttribute('href')).toMatch(/^javascript:alert/);
|
||||
let trustedUrl = element(By.className('e2e-trusted-url'));
|
||||
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', () => {
|
||||
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\//);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ import { BypassSecurityComponent } from './bypass-security.component';
|
|||
import { InnerHtmlBindingComponent } from './inner-html-binding.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<h1>Security</h1>
|
||||
<inner-html-binding></inner-html-binding>
|
||||
|
@ -14,7 +14,7 @@ import { InnerHtmlBindingComponent } from './inner-html-binding.component';
|
|||
directives: [
|
||||
BypassSecurityComponent,
|
||||
InnerHtmlBindingComponent,
|
||||
],
|
||||
]
|
||||
})
|
||||
export class AppComponent {
|
||||
}
|
||||
|
|
|
@ -2,14 +2,19 @@
|
|||
<h3>Bypass Security Component</h3>
|
||||
|
||||
<!--#docregion dangerous-url -->
|
||||
<h4>A dangerous URL:</h4>
|
||||
<p><a class="e2e-dangerous-url" [href]="dangerousUrl">Click me.</a></p>
|
||||
<h4>A 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>
|
||||
<!--#enddocregion dangerous-url -->
|
||||
|
||||
<!--#docregion iframe-videoid -->
|
||||
<h4>Resource URL:</h4>
|
||||
<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 -->
|
||||
|
|
|
@ -8,14 +8,18 @@ import { DomSanitizationService, SafeResourceUrl, SafeUrl } from '@angular/platf
|
|||
templateUrl: 'app/bypass-security.component.html',
|
||||
})
|
||||
export class BypassSecurityComponent {
|
||||
dangerousUrl: SafeUrl;
|
||||
dangerousUrl: string;
|
||||
trustedUrl: SafeUrl;
|
||||
dangerousVideoUrl: string;
|
||||
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")');
|
||||
// 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 = 'javascript:alert("Hi there")';
|
||||
this.trustedUrl = sanitizer.bypassSecurityTrustUrl(this.dangerousUrl);
|
||||
// #enddocregion trust-url
|
||||
this.updateVideoUrl('PUBnlbjZFAI');
|
||||
}
|
||||
|
@ -23,11 +27,12 @@ export class BypassSecurityComponent {
|
|||
// #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
|
||||
// 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('https://www.youtube.com/embed/' + id);
|
||||
this.sanitizer.bypassSecurityTrustResourceUrl(this.dangerousVideoUrl);
|
||||
}
|
||||
// #enddocregion trust-video-url
|
||||
}
|
||||
// #enddocregion
|
||||
|
|
|
@ -11,4 +11,3 @@ export class InnerHtmlBindingComponent {
|
|||
// E.g. a user/attacker controlled value from a URL.
|
||||
htmlSnippet = 'Template <script>alert("0wned")</script> <b>Syntax</b>';
|
||||
}
|
||||
// #enddocregion inner-html-controller
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- #docregion -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular Content Security</title>
|
||||
|
@ -21,6 +21,6 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<app-root>Loading...</app-root>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
block includes
|
||||
include ../_util-fns
|
||||
: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
|
||||
Scripting Attacks. It does not cover application level security, such as authentication (_Who is
|
||||
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)
|
||||
: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
|
||||
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.
|
||||
|
||||
|
@ -81,10 +81,10 @@ h2#xss Preventing Cross-Site Scripting (XSS)
|
|||
|
||||
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
|
||||
* 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
|
||||
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
|
||||
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
|
||||
Interpolated content is always escaped - the HTML is not interpreted, and the browser displays
|
||||
angle brackets in the elements text content.
|
||||
|
@ -121,8 +122,7 @@ figure.image-display
|
|||
|
||||
### Content Security Policy
|
||||
|
||||
A [Content Security Policy (CSP)]
|
||||
(http://www.html5rocks.com/en/tutorials/security/content-security-policy/) is a defense-in-depth
|
||||
A [Content Security Policy (CSP)](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
|
||||
`Content-Security-Policy` HTTP header.
|
||||
|
||||
|
@ -139,7 +139,7 @@ figure.image-display
|
|||
### Server side XSS protection
|
||||
|
||||
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
|
||||
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
|
||||
|
@ -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
|
||||
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
|
||||
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
|
||||
Angular then allows binding into `<iframe src>`.
|
||||
|
||||
|
@ -198,12 +198,12 @@ h2#http HTTP-level Vulnerabilities
|
|||
h3#xsrf Cross-site Request Forgery (XSRF)
|
||||
:marked
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
|
@ -220,14 +220,17 @@ h3#xsrf Cross-site Request Forgery (XSRF)
|
|||
|
||||
Angular applications can customize cookie and header names by binding their own
|
||||
`CookieXSRFStrategy` value, or implement an entirely custom `XSRFStrategy` by providing a custom
|
||||
binding for that type, by adding
|
||||
`provide(XSRFStrategy, {useValue: new CookieXSRFStrategy('myCookieName', 'My-Header-Name')})` or
|
||||
`provide(XSRFStrategy, {useClass: MyXSRFStrategy})` to your providers list.
|
||||
binding for that type, by adding either of the following 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)
|
||||
[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
|
||||
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)
|
||||
:marked
|
||||
|
|
Loading…
Reference in New Issue