@libs-ui/services-sanitizer
v0.2.357-28
Published
> Service xử lý XSS (Cross-Site Scripting) sử dụng DOMPurify, cung cấp API sanitize HTML an toàn cho Angular.
Readme
@libs-ui/services-sanitizer
Service xử lý XSS (Cross-Site Scripting) sử dụng DOMPurify, cung cấp API sanitize HTML an toàn cho Angular.
Giới thiệu
SanitizerService là service Angular tích hợp DOMPurify để loại bỏ các mối đe dọa XSS trong nội dung HTML. Service cung cấp nhiều chế độ sanitize từ strict (chỉ text formatting cơ bản) đến rich text (đầy đủ HTML features cho editor), đồng thời hỗ trợ trả về Angular SafeHtml để bind trực tiếp vào [innerHTML] mà không cần thêm bước xử lý.
Tính năng
- ✅ Sanitize HTML loại bỏ XSS threats (script, event handler, inline JS)
- ✅ Tích hợp Angular
DomSanitizer— trả vềSafeHtmldùng trực tiếp với[innerHTML] - ✅ Chế độ strict — chỉ cho phép text formatting cơ bản (
p,span,strong,em,br) - ✅ Chế độ rich text — giữ đầy đủ HTML structure cho editor (Quill, TinyMCE, CKEditor)
- ✅ Strip HTML — loại bỏ toàn bộ tags, chỉ giữ plain text
- ✅ Detect XSS — kiểm tra xem HTML có chứa nội dung độc hại không
- ✅ Custom config — tuỳ chỉnh allowed tags, attributes, styles theo nhu cầu
- ✅
providedIn: 'root'— không cần import vào module, inject trực tiếp
Khi nào sử dụng
- Hiển thị nội dung HTML từ user input (comment, mô tả, bài viết)
- Render output từ rich text editor (Quill, TinyMCE, CKEditor) an toàn
- Cần kiểm tra nhanh xem một đoạn HTML có chứa XSS threats không
- Lưu HTML vào database sau khi đã loại bỏ nội dung nguy hiểm
- Hiển thị nội dung i18n có chứa HTML tags cơ bản
Cài đặt
npm install @libs-ui/services-sanitizerĐảm bảo dompurify đã được cài đặt (peer dependency):
npm install dompurifyImport
import { SanitizerService, SanitizerConfig } from '@libs-ui/services-sanitizer';Inject vào component (không cần khai báo trong imports[] vì providedIn: 'root'):
import { Component, inject } from '@angular/core';
import { SanitizerService } from '@libs-ui/services-sanitizer';
@Component({
selector: 'app-example',
standalone: true,
templateUrl: './example.component.html',
})
export class ExampleComponent {
private readonly sanitizerService = inject(SanitizerService);
}Ví dụ sử dụng
Ví dụ 1 — Sanitize HTML cơ bản và bind với [innerHTML]
// example.component.ts
import { Component, inject, signal, computed } from '@angular/core';
import { SanitizerService } from '@libs-ui/services-sanitizer';
import { SafeHtml } from '@angular/platform-browser';
@Component({
selector: 'app-article-detail',
standalone: true,
templateUrl: './article-detail.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ArticleDetailComponent {
private readonly sanitizerService = inject(SanitizerService);
protected rawContent = signal('<p>Nội dung bài viết</p><script>alert("XSS")</script>');
protected safeContent = computed<SafeHtml>(() =>
this.sanitizerService.sanitizeToSafeHtml(this.rawContent())
);
}<!-- article-detail.component.html -->
<div class="article-body" [innerHTML]="safeContent()"></div>Ví dụ 2 — Strict mode cho comment người dùng
// comment-item.component.ts
import { Component, inject } from '@angular/core';
import { SanitizerService } from '@libs-ui/services-sanitizer';
@Component({
selector: 'app-comment-item',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="comment-text">{{ safeComment }}</div>
`,
})
export class CommentItemComponent {
private readonly sanitizerService = inject(SanitizerService);
// Chỉ cho phép p, span, strong, em, br — loại bỏ link, ảnh, và styles
safeComment = this.sanitizerService.sanitizeStrict(
'<p>Bình luận <strong>quan trọng</strong></p><a href="malicious.com">Click</a>'
);
// Result: '<p>Bình luận <strong>quan trọng</strong></p>Click'
}Ví dụ 3 — Rich text editor (Quill / TinyMCE)
// editor-preview.component.ts
import { Component, inject, signal, computed } from '@angular/core';
import { SanitizerService } from '@libs-ui/services-sanitizer';
import { SafeHtml } from '@angular/platform-browser';
@Component({
selector: 'app-editor-preview',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<div class="ql-editor" [innerHTML]="previewHtml()"></div>`,
})
export class EditorPreviewComponent {
private readonly sanitizerService = inject(SanitizerService);
protected editorOutput = signal('');
protected previewHtml = computed<SafeHtml>(() => {
const sanitized = this.sanitizerService.sanitizeRichText(this.editorOutput());
return this.sanitizerService.sanitizeToSafeHtml(sanitized);
});
protected handlerEditorChange(html: string): void {
this.editorOutput.set(html);
}
}Ví dụ 4 — Kiểm tra XSS trước khi lưu
// save-content.component.ts
import { Component, inject } from '@angular/core';
import { SanitizerService } from '@libs-ui/services-sanitizer';
import { NotificationService } from '@libs-ui/services-notification';
@Component({
selector: 'app-save-content',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SaveContentComponent {
private readonly sanitizerService = inject(SanitizerService);
private readonly notification = inject(NotificationService);
protected handlerSave(userInput: string): void {
if (this.sanitizerService.hasXSSThreats(userInput)) {
this.notification.showError('i18n_content_contains_unsafe_html');
return;
}
const cleanHtml = this.sanitizerService.sanitize(userInput);
// Tiến hành lưu cleanHtml vào API
}
}Ví dụ 5 — Custom config (chỉ cho phép danh sách tags cụ thể)
// custom-sanitize.component.ts
import { Component, inject } from '@angular/core';
import { SanitizerService, SanitizerConfig } from '@libs-ui/services-sanitizer';
@Component({
selector: 'app-custom-sanitize',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomSanitizeComponent {
private readonly sanitizerService = inject(SanitizerService);
private readonly strictTableConfig: SanitizerConfig = {
allowedTags: ['table', 'thead', 'tbody', 'tr', 'th', 'td', 'caption'],
allowedAttributes: ['class', 'colspan', 'rowspan', 'scope'],
allowStyles: false,
allowDataAttributes: false,
};
protected safeTable = this.sanitizerService.sanitize(
'<table><tr><td onclick="steal()">Data</td></tr></table>',
this.strictTableConfig
);
// Result: '<table><tr><td>Data</td></tr></table>'
}Methods
| Method | Signature | Trả về | Mô tả |
|---|---|---|---|
| sanitize | sanitize(html: string, config?: SanitizerConfig): string | string | Sanitize HTML, loại bỏ XSS threats, trả về string an toàn |
| sanitizeToSafeHtml | sanitizeToSafeHtml(html: string, config?: SanitizerConfig): SafeHtml | SafeHtml | Sanitize và wrap thành Angular SafeHtml dùng trực tiếp với [innerHTML] |
| sanitizeStrict | sanitizeStrict(html: string): string | string | Strict mode — chỉ cho phép p, br, span, strong, b, em, i, u. Loại bỏ link, ảnh, styles |
| sanitizeRichText | sanitizeRichText(html: string): string | string | Rich text mode — giữ đầy đủ HTML features kể cả iframe, phù hợp output từ editor |
| stripHtml | stripHtml(html: string): string | string | Xoá toàn bộ HTML tags, chỉ giữ plain text content |
| hasXSSThreats | hasXSSThreats(html: string): boolean | boolean | Trả về true nếu HTML gốc khác HTML đã sanitize (tức có nội dung độc hại bị loại bỏ) |
Types & Interfaces
import { SanitizerConfig } from '@libs-ui/services-sanitizer';SanitizerConfig
interface SanitizerConfig {
/** Danh sách HTML tags được phép. Khi set sẽ REPLACE (không merge) default config */
allowedTags?: string[];
/** Danh sách HTML attributes được phép. Khi set sẽ REPLACE (không merge) default config */
allowedAttributes?: string[];
/** Cho phép CSS inline styles. Mặc định: true */
allowStyles?: boolean;
/** Cho phép data-* attributes. Mặc định: true */
allowDataAttributes?: boolean;
/** Custom DOMPurify config — merge vào trên cùng của config đã build */
customConfig?: DOMPurify.Config;
}| Property | Type | Default | Mô tả |
|---|---|---|---|
| allowedTags | string[] | DOMPurify defaults (p, div, span, h1-h6, ul, ol, li, table, a, img...) | Danh sách HTML tags được phép. Set = REPLACE, không merge |
| allowedAttributes | string[] | class, id, style, href, src, data-, aria-... | Danh sách HTML attributes được phép. Set = REPLACE, không merge |
| allowStyles | boolean | true | false sẽ loại bỏ thuộc tính style khỏi tất cả elements |
| allowDataAttributes | boolean | true | false sẽ loại bỏ toàn bộ data-* attributes |
| customConfig | DOMPurify.Config | undefined | Merge thêm vào config đã build, ưu tiên cao nhất |
Default allowed tags (khi không truyền allowedTags)
Text formatting: p, br, span, div, strong, b, em, i, u, s, strike, del, ins, sub, sup, small, mark, code, pre, kbd, samp
Headings: h1, h2, h3, h4, h5, h6
Lists: ul, ol, li, dl, dt, dd
Tables: table, thead, tbody, tfoot, tr, th, td, caption, col, colgroup
Links & Media: a, img, figure, figcaption, picture, source
Semantic: blockquote, q, cite, abbr, address, time, article, section, nav, aside, header, footer, main, hr, details, summary
Lưu ý quan trọng
⚠️ Custom config REPLACE, không merge: Khi cung cấp allowedTags hoặc allowedAttributes trong SanitizerConfig, service sẽ THAY THẾ hoàn toàn default config, không gộp lại. Nếu muốn giữ default và thêm tag mới, hãy dùng customConfig.ADD_TAGS thay thế.
// ❌ SAI — sẽ mất toàn bộ default tags, chỉ còn 'p' và tag thêm
sanitize(html, { allowedTags: ['p'], customConfig: { ADD_TAGS: ['custom-tag'] } });
// ✅ ĐÚNG — giữ default, thêm tag mới qua customConfig
sanitize(html, { customConfig: { ADD_TAGS: ['custom-tag'], ADD_ATTR: ['custom-attr'] } });⚠️ sanitizeToSafeHtml dùng bypassSecurityTrustHtml: Method này gọi DomSanitizer.bypassSecurityTrustHtml() sau khi DOMPurify đã làm sạch. Không được truyền HTML chưa sanitize vào bypassSecurityTrustHtml trực tiếp trong component — luôn dùng qua service này.
⚠️ hasXSSThreats là heuristic: Method so sánh string gốc với string đã sanitize. Có thể trả về true kể cả khi HTML chỉ khác nhau về whitespace hoặc attribute order — không phải 100% chính xác cho mọi trường hợp.
⚠️ sanitizeRichText cho phép iframe: Mode rich text thêm iframe vào allowed tags. Chỉ dùng khi thực sự cần embed video/content từ trusted sources (YouTube, Vimeo). Không dùng để sanitize user-generated content không kiểm soát được.
