😱
Web vulnerabilities can cause risks to your assets: your application, data, end users, and reputation.
Not to mention the bottom line.
It all adds up to liability
2021 OWASP Top 10 https://owasp.org/Top10/
- Broken Access Control
- Cryptographic Failures
- Injection
- Insecure Design
- Security Misconfiguration
- Vulnerable and Outdated Components
- Identification and Authentication Failures
- Software and Data Integrity Failures
- Security Logging and Monitoring Failures
- Server-Side Request Forgery (SSRF)
2021 OWASP Top 10 https://owasp.org/Top10/
- Broken Access Control
- Cryptographic Failures
- Injection
- Insecure Design
- Security Misconfiguration
- Vulnerable and Outdated Components
- Identification and Authentication Failures
- Software and Data Integrity Failures
- Security Logging and Monitoring Failures
- Server-Side Request Forgery (SSRF)
Occurs when there's not good data and code boundaries in values, thus allowing the attacker to perform unauthorized actions
<a href="javascript:alert('Yikes!')"> Free prizes here! </a>
Once an attacker can successfully run their script in the app, they can
Consider what happens when vulnerabilities exist in critical applications
Consider what happens when the attacked user has elevated access within the application
😰
Angular treats all values as untrusted
Security without effort
Angular automatically escapes values when using interpolation
{{comment}}
<img src=1 onerror="alert('Boo!')" />
<img src=1 onerror="alert('Boo!')" />
Angular automatically sanitizes values when property binding to sinks
Web API functions that allow us to create dynamic content such as
innerHTML
src
, href
, url
<img src=1 onerror="alert('Boo!')" />
A peek inside packages/core/src/sanitization
Angular maintains lists of safe elements and attributes
private startElement(element: Element): boolean {
const tagName = element.nodeName.toLowerCase();
if (!VALID_ELEMENTS.hasOwnProperty(tagName)) {
this.sanitizedSomething = true;
return !SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS.hasOwnProperty(tagName);
}
this.buf.push('<');
this.buf.push(tagName);
const elAttrs = element.attributes;
for (let i = 0; i < elAttrs.length; i++) {
const elAttr = elAttrs.item(i);
const attrName = elAttr!.name;
const lower = attrName.toLowerCase();
if (!VALID_ATTRS.hasOwnProperty(lower)) {
this.sanitizedSomething = true;
continue;
}
let value = elAttr!.value;
if (URI_ATTRS[lower]) value = _sanitizeUrl(value);
this.buf.push(' ', attrName, '="', encodeEntities(value), '"');
}
this.buf.push('>');
return true;
}
Source from packages/core/src/sanitization/html_sanitizer.ts
private startElement(element: Element): boolean {
if (!VALID_ELEMENTS.hasOwnProperty(tagName)) {
// yikes!
}
// tedious work here
foreach attribute {
if (!VALID_ATTRS.hasOwnProperty(lower)) {
// make note, move on
}
if (URI_ATTRS[lower]) value = _sanitizeUrl(value);
}
// finalize tedious work
}
Original source from packages/core/src/sanitization/html_sanitizer.ts
. My interpretation of what the code does.
<img
src=1
onerror="alert('Boo!')" />
// Safe Void Elements - HTML5
// https://html.spec.whatwg.org/#void-elements
const VOID_ELEMENTS = tagSet('area,br,col,hr,img,wbr');
// Attributes that have href and hence need to be sanitized
export const URI_ATTRS = tagSet(
'background,cite,href,itemtype,longdesc,poster,src,xlink:href'
);
The onerror
attribute is not a safe attribute and is removed.
Source from packages/core/src/sanitization/html_sanitizer.ts
Let's change our exploit to a previous example
<a href="javascript:alert('Yikes!')"> Free prizes here! </a>
const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:\/?#]*(?:[\/?#]|$))/i;
export function _sanitizeUrl(url: string): string {
url = String(url);
if (url.match(SAFE_URL_PATTERN)) return url;
if (typeof ngDevMode === 'undefined' || ngDevMode) {
console.warn(`WARNING: sanitizing unsafe URL value ${url} (see https://g.co/ng/security#xss)`);
}
return 'unsafe:' + url;
}
Source from packages/core/src/sanitization/url_sanitizer.ts
Allow safe markup
<strong>It's a wonderful drama! The best!</strong>
strong
is a valid inline element
// Inline Elements - HTML5
const INLINE_ELEMENTS = merge(
OPTIONAL_END_TAG_INLINE_ELEMENTS,
tagSet(
'a,abbr,acronym,audio,b,' +
'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,picture,q,ruby,rp,rt,s,' +
'samp,small,source,span,strike,strong,sub,sup,time,track,tt,u,var,video'
)
);
Source from packages/core/src/sanitization/html_sanitizer.ts
Angular protects us against mutation XSS (mXSS) 🔍
let mXSSAttempts = 5;
let parsedHtml = unsafeHtml;
do {
if (mXSSAttempts === 0) {
throw new Error('Failed to sanitize html because the input is unstable');
}
mXSSAttempts--;
unsafeHtml = parsedHtml;
parsedHtml = inertBodyElement!.innerHTML;
inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeHtml);
} while (unsafeHtml !== parsedHtml);
Source is packages/core/src/sanitization/html_sanitizer.ts
Use Angular constructs
Bypassing security
export enum SecurityContext {
NONE = 0,
HTML = 1,
STYLE = 2,
SCRIPT = 3,
URL = 4,
RESOURCE_URL = 5,
}
Source from packages/core/src/sanitization/security.ts
export abstract class DomSanitizer implements Sanitizer {
abstract sanitize(context: SecurityContext, value: SafeValue|string|null): string|null;
abstract bypassSecurityTrustHtml(value: string): SafeHtml;
abstract bypassSecurityTrustStyle(value: string): SafeStyle;
abstract bypassSecurityTrustScript(value: string): SafeScript;
abstract bypassSecurityTrustUrl(value: string): SafeUrl;
abstract bypassSecurityTrustResourceUrl(value: string): SafeResourceUrl;
}
Source from angular/packages/platform-browser/src/security/dom_sanitizer_service.ts
Angular tracks which security context to use based on the attribute you are binding
registerContext(SecurityContext.HTML, [
'iframe|srcdoc',
'*|innerHTML',
'*|outerHTML',
]);
Source from packages/compiler/src/schema/dom_security_schema.ts
With great power comes great responsibility
Occurs when the application shares session cookies to untrusted sources
yikesbank.com/transfer?amt=100&acctNum=12345
Use built-in CSRF protection to automatically send a CSRF token value in the API header that the backend can verify
From packages/common/http/src/module.ts
Angular gives you the building blocks to implement the access controls you need
Angular is our soft cozy security blanket
SPA Web Security posts
bit.ly/SPAWebSecurity
Code
github.com/alisaduncan/angular-security-code
Secure Coding in Angular
Pluralsight course
Okta Developer resources
Okta Developer blog