
掌握ChatGPT插件与自定义GPT
TypeScript 已经走过了漫长的道路,成为现代 Web 开发技术堆栈的一部分,无论是前端还是后端。转向 TypeScript 的开发人员几乎从不后悔!然而,即使你掌握了一门强大的编程语言,你也可能会遇到最基本的安全警告。
TypeScript 是 React 和 Angular 等现代前端框架的强力补充。但是,它不会自动保护您的代码免受 DOM 漏洞的侵害。事实上,很多时候,开发人员没有充分利用 TypeScript,导致他们的代码库容易受到 DOM XSS 攻击。其他时候,他们只是不知道什么是 XSS,并成为恶意攻击者向他们的 DOM 注入恶意 JavaScript 的牺牲品。
因此,在这篇文章中,我将讨论什么是 XSS 以及 XSS 攻击是如何发生的。然后,我将向您展示如何在 TypeScript 项目中防止 XSS 攻击。
XSS 代表“跨站点脚本”。这是一种将外部 JavaScript 注入网站的技术。我们网站的大部分逻辑(包括动画和用户交互)都归属于我们网站的 JavaScript。即使您在运行时使用 TypeScript 来构建 React 或 Angular 项目,最终,所有这些 TypeScript 都会被编译为 JavaScript。
当您的应用程序留下漏洞,允许攻击者运行一些您未授权的 JavaScript 时,您的网站就会受到 XSS 攻击。该漏洞本身就是应用程序的 XSS 漏洞。但 XSS 漏洞是什么样子的?XSS 攻击究竟是如何发生的?
让我们举一个简单的网站示例,您在其中填写表单。为简洁起见,我有一个简单的CodeSandbox 示例,它在页面上呈现一个输入字段和一个提交按钮。页面显示如下:
页面是什么样子的。
它的 HTML 内容如下:
<!DOCTYPE html>
<html>
<head>
<title>XSS Attack Example</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="app">
<h1>Enter your name</h1>
<input type="text" />
<button>Greet Me!</button>
<div id="greeting"></div>
</div>
<script src="src/index.js"></script>
</body>
</html>
当用户输入姓名并按下“提交”按钮时,我们会运行一个简单的 JavaScript 函数。此函数会捕获输入字段中的值。然后它会在输入字段下方为用户生成问候语。因此,如果您输入姓名并点击“提交”,您将获得以下内容:
您将看到的问候语示例。
现在,这看起来似乎很安全,对吧?让我们看一下执行所有这些操作的 JavaScript 代码:
import "./styles.css";
const input = document.querySelector("input");
const submitButton = document.querySelector("button");
const greetingBox = document.querySelector("#greeting");
function onClick() {
const name = input.value;
greetingBox.append(`Hey ${name}! A very good morning to you`);
}
submitButton.addEventListener("click", onClick);
如果仔细观察我们操作 DOM 的位置,就会发现我们正在使用append方法在 DOM 元素上附加一些新 HTML。这听起来安全吗?
其实不然!它是 XSS 漏洞的滋生地。所有这些 JavaScript 代码都可以通过浏览器供任何人使用,因此我可以继续修改该行,如下所示:
greetingBox.append(alert("you are hacked! 🐱💻"));
现在,如果有人按下“提交”按钮,就会执行警报功能:
您将看到的警报。
前面的示例是用 JavaScript 编写的,但其 TypeScript 对应部分也不会有太大不同。为了演示,我们以 React with TypeScript 项目为例,我们可以在其中复制上述场景。
以下是供您参考的示例。我们只有一个简单的 React 应用程序,其中的App.ts文件中包含以下代码:
import "./styles.css";
export default function App() {
const handleSubmit = () => {
const inputDOM: HTMLElement | null = document.querySelector<
HTMLInputElement
>("input");
const inputVal: string = inputDOM?.value;
const greetingBox = document.getElementById<HTMLElement>("greeting");
greetingBox?.append(inputVal);
};
return (
<div className="App">
<h2>XSS in Typescript + React</h2>
<input />
<button onClick={handleSubmit}>Submit</button>
<div id="greeting"></div>
</div>
);
}
如果您查看我们所有的函数和 DOM 操作,就会发现我们正在使用完整的 TypeScript,而不是普通的旧 JavaScript!但是,我们操作 DOM 的方式仍然不正确。它仍然暴露了 DOM XSS 漏洞。这意味着仅使用 TypeScript 不会有帮助。
在上面的代码库中,假设攻击者设法注入一个脚本并以编程方式执行该脚本:
...
greetingBox?.append(alert("you are hacked! 🐱💻"));
...
XSS 攻击很可能这样执行:
您将看到的警报。
但上述问题的解决方案很简单:有一些糟糕的代码需要重构。现在让我们看看它。
在原始 JavaScript 示例中,您需要进行以下更改。不要使用append方法将一些文本附加到 DOM,而是使用textContent ,如下所示:
greetingBox.textContent = `Hey ${name}! A very good morning to you`;
上述代码会产生相同的结果。如果攻击者试图向其中添加一些 JavaScript:
greetingBox.textContent = `alert('you are hacked!')`;
JavaScript 将在 HTML 页面上呈现为字符串,而不是被执行:
攻击者将看到的文本。
现在让我们回到我们的 React 和 TypeScript 项目。有大量的错误代码。我们直接操作 DOM 而不使用state和ref,这在这里确实很有用。
import "./styles.css";
import { useState } from "react";
export default function App() {
const [name, setName] = useState<String>("");
const handleSubmit = () => {
const inputDOM: HTMLElement | null = document.querySelector<
HTMLInputElement
>("input");
const inputVal: string = inputDOM?.value;
const greetingBox = document.getElementById<HTMLElement>("greeting");
setName(inputVal);
// greetingBox?.append(inputVal);
// greetingBox?.append(alert("you are hacked! 🐱💻"));
};
return (
<div className="App">
<h2>XSS in Typescript + React</h2>
<input />
<button onClick={handleSubmit}>Submit</button>
<div id="greeting">{name}</div>
</div>
);
}
我们不使用append方法,而是简单地将输入字段的值设置为状态的值。然后我们使用此状态在 DOM 上输出值。
我们已经看到了一种解决基于 DOM 的 XSS 漏洞的方法。但是,如果您绝对需要在容器内设置 HTML,该怎么办?例如,您可以有一个富文本编辑器,它会将格式化的文本保存在数据库中。但是,当此文本返回时,它会附带相关的 HTML 标签。
或者在其他情况下,您的服务器可能会返回 TypeScript 应用程序需要呈现的某些动态链接的 href。在这种情况下,您也需要以编程方式操作 DOM。
为简洁起见,我们考虑这样一种场景:您的后端返回 Angular + TypeScript 应用程序呈现的链接的 href。以下是该组件的 HTML 部分:
<div>
<h3>
Welcome to XSS with Typescript in Angular!
</h3>
<a class="link">Click</a>
</div>
在其component.ts文件中,我们可以设置<a>标签的href属性。操作如下:
import { Component, Renderer2, ElementRef } from "@angular/core";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
constructor(private renderer: Renderer2, private el: ElementRef) {}
url: string = "https://google.com";
private get host(): HTMLElement {
return this.el.nativeElement;
}
ngOnInit() {
const link = this.getLink(".link");
this.renderer.setAttribute(link, "href", this.url);
}
private getLink(selector: string): HTMLAnchorElement {
return this.host.querySelector(selector);
}
}
如果您点击上述链接,您应该会被重定向到 google.com。但在这种情况下,想象一下 href 来自 API 请求。在这种情况下,攻击者可以轻松地将 URL 的 href 操纵为:
url: string = 'javascript:alert("you are hacked 🐱💻")';
现在我们又回到了原点!我们的 Angular + TypeScript 应用程序存在 XSS 漏洞。那么我们现在该怎么办?
好了,我们应该清理一下 HTML!Angular 在其 platform-browser 模块中提供了一个名为 DomSanitizer 的库。我们可以像这样 将其导入到我们的component.ts文件中:
import { DomSanitizer } from "@angular/platform-browser";
在我们的构造函数中创建它的引用:
constructor(
...
private DomSanitizer: DomSanitizer,
...
)
{}
现在,我们可以使用 DomSanitizer 根据特定规则清理任何 HTML。由于我们想要清理 HTML URL,因此可以使用 Angular 核心模块中的 SecurityContext 模块。
import { SecurityContext } from '@angular/core';
最后,我们现在可以像这样清理我们的 URL:
url: string =this.DomSanitizer.sanitize(SecurityContext.URL, 'javascript:alert("you are hacked 🐱💻")';
现在,如果您尝试单击该链接,则不会出现警报。如果您转到控制台,您将看到类似以下内容:
消毒后您将看到的警告。
这就是如何在 Angular + TypeScript 应用程序中清理未清理的 HTML,以保护此类用例免受致命的 XSS 攻击。您可以在我们关于该主题的指南中了解有关如何在 Angular 应用程序中防止 XSS 攻击的更多信息。
XSS 漏洞最常出现在 DOM 操作中。如果您在操作 DOM 时非常谨慎,那么避免它们将轻而易举。在页面上直接输出 HTML 时也必须小心谨慎。开发人员经常会忘记清理这些 HTML,从而导致 XSS 漏洞,攻击者可能会利用这些漏洞对您的应用程序发起致命攻击。