In AngularJS, Strict Contextual Escaping (SCE) is a security feature that helps prevent Cross-Site Scripting (XSS) attacks by blocking potentially unsafe content. When AngularJS detects unsafe content, it throws the error:
[$sce:unsafe] Attempting to use an unsafe value in a safe context.
This happens when you try to bind HTML, URLs, or JavaScript that Angular considers unsafe. This guide will explain why this happens, how to fix it securely, and how to prevent XSS vulnerabilities.
1. Why Does AngularJS Block Unsafe Content?
AngularJS blocks unsafe content to prevent attackers from injecting malicious scripts into the application.
1️⃣ Example of Unsafe Content in AngularJS
Consider the following example where we use ng-bind-html
:
<div ng-bind-html="userInput"></div>
If userInput
contains:
$scope.userInput = "<script>alert('XSS Attack!');</script>";
The browser blocks this content due to AngularJS’s SCE protection.
2️⃣ Common Causes of [$sce:unsafe] Errors
- Using
ng-bind-html
with untrusted content - Displaying untrusted URLs in
src
orhref
attributes - Using JavaScript URLs (e.g.,
javascript:alert(1)
) - Trying to load iframe content with
ng-src
2. Fixing [$sce:unsafe] Error Securely
If you trust the content, you can mark it as safe using $sce.trustAsHtml()
.
1️⃣ Using $sce.trustAsHtml()
for Safe HTML
app.controller("MyCtrl", function ($scope, $sce) {
$scope.safeHtml = $sce.trustAsHtml("<b>This is safe HTML</b>");
});
<div ng-bind-html="safeHtml"></div>
This works, but should only be used for trusted content.
Warning: Do NOT use $sce.trustAsHtml()
on user-generated content, as it can introduce XSS vulnerabilities.
2️⃣ Using $sce.trustAsResourceUrl()
for URLs
If you need to use a dynamic URL (e.g., an iframe src
), Angular blocks it unless it is explicitly trusted.
$scope.trustedUrl = $sce.trustAsResourceUrl("https://example.com");
<iframe ng-src="{{trustedUrl}}"></iframe>
Use this only for trusted sources.
3️⃣ Using $sce.trustAsJs()
for JavaScript URLs (Not Recommended)
If you are trying to load a javascript:
URL (e.g., javascript:alert(1);
), Angular blocks it.
$scope.trustedJs = $sce.trustAsJs("javascript:alert('XSS');");
This is dangerous and should be avoided.
Instead, use event handlers in JavaScript.
3. Preventing XSS When Using $sce
To safely handle HTML content, follow best security practices:
1. Use a Sanitization Library
Instead of blindly trusting content, sanitize it before displaying:
app.controller("MyCtrl", function ($scope, $sanitize) {
$scope.safeContent = $sanitize(userInput);
});
<div ng-bind-html="safeContent"></div>
This removes malicious scripts while keeping safe HTML elements.
2. Validate and Escape User Input
If your application accepts user input, always validate and escape it before rendering.
Sanitize on the Backend (Example in Node.js)
const sanitizeHtml = require('sanitize-html');
let cleanInput = sanitizeHtml(userInput);
Use encodeURIComponent()
for URLs
let safeUrl = encodeURIComponent(userProvidedUrl);
3. Use a Content Security Policy (CSP)
Adding a CSP header can block inline scripts and unsafe URLs:
Content-Security-Policy: default-src 'self'; script-src 'self'
This prevents malicious scripts from executing.
4. Summary
Issue | Secure Fix |
---|---|
$sce:unsafe error when using ng-bind-html | Use $sce.trustAsHtml() only for trusted content |
Unsafe iframe ng-src | Use $sce.trustAsResourceUrl() |
XSS attack via user input | Sanitize input using $sanitize or backend validation |
Allowing JavaScript URLs | Avoid javascript: URLs and use event listeners instead |
Security Best Practices:
Always sanitize user input
Avoid $sce.trustAsHtml()
for untrusted content
Use CSP headers to block XSS attacks
Validate all URLs before rendering