标签: JavaScript安全 XSS防护 2025-11-10 次
JavaScript 是整个互联网的前端。无论你是否将 TypeScript 转译成 JavaScript,创建快速的小 Node.js 脚本,或者构建一个美观但愚蠢的前端调用一个更有趣的 API 集合,JavaScript 都无处不在。因为 JavaScript 非常广泛,所以它是攻击者的主要目标。在本文中,我们将涵盖十个编写更安全的 JavaScript 的提示。

1. 跨站脚本
当讨论JavaScript安全时,讨论的第一项总是跨站脚本(XSS)。跨站脚本是一种注入形式;它意味着攻击者已经使你的应用程序要么解释或执行他们的恶意代码,而不是将其视为数据。用户输入应始终被视为数据,但不幸的是,如果我们不注意,计算机可能会被欺骗。
XSS 是唯一一种只能在 JavaScript 中工作的注入类型。它也是唯一一种直接攻击用户的方式,通过控制浏览器并利用它来攻击受害者。所有其他类型的注入并不攻击用户;例如,SQL 注入攻击数据库服务器,命令注入攻击系统正在运行的主机操作系统,而 LDAP 注入攻击 LDAP 服务器。你应该明白我的意思了。
通过XSS,攻击者可以利用浏览器访问你的Cookies(如果你以不安全的方式将会话信息存储在Cookie中),外部脚本(如果你没有使用内容安全策略来限制它),安装键盘记录器,破坏你的网站等等。JavaScript能做的任何事情,XSS攻击也能做;唯一的限制是攻击者的想象力。
尽管 years 来 XSS 的实例有所减少,这要归功于各种形式的意识和教育(例如 The OWASP Top Ten Risks to Web Apps),新的 JavaScript 框架自动进行输出编码,以及团队比以往更认真地对待安全问题,不幸的是,这仍然是一个高风险问题。
为了消除XSS影响你应用程序的可能性,请执行以下操作:
1. 对所有用户提供的或可修改的数据进行输入验证。在进行输入验证之后,如果您必须接受可能有害的字符(例如 <, >, ‘, “, -, 等等),则应通过转义(在前面添加反斜杠)或清理(用另一个字符替换它们或完全删除它们)来保护您的应用程序。
2. 对将要显示到屏幕上的任何内容进行输出编码,包括可能显示的任何内容(例如,如果您从 API 返回的内容知道它将由您的前端显示)。如果您可以让框架为您完成这项工作,那是确保正确执行此操作的最简单和最有效的方法。输出编码可能会变得相当复杂,特别是如果您在进行内联JavaScript。没有人愿意进行嵌套编码!
3. 使用内容安全策略头 (CSP) 列出您将在应用中允许的所有第三方组件,特别是脚本。恶意 XSS 攻击的第一件事就是尝试调用互联网上另一个更大的恶意脚本。大多数字段只允许 50 或 100 个字符,这并不是写攻击代码的很多空间。如果他们能够调用互联网上的恶意站点,然后调用一个更长的脚本,那么您的风险将成倍增加。
4. 添加httpsOnly标志到你的cookies中,以确保即使你确实错过了某些东西,攻击者也永远无法通过你的cookies访问你的会话信息。尝试访问你的cookies是XSS攻击通常会尝试的第二件事,所以不要让他们访问这些敏感信息。
5. 进行人工代码审查,使用静态(SAST)或动态(DAST)分析工具,或进行渗透测试,以确保万无一失,没有遗漏任何内容!
2. 使用输出编码的JavaScript框架为你编码
React, Angular, 和 Vue.js 自动执行输出编码,并且还具有许多其他令人惊叹的功能。在使用任何框架时,请小心检查是否有危险函数需要避免或谨慎使用,例如 React 的 dangerouslySetInnerHTML 和 Angular 的 bypassSecurityTRustAs* 函数。
3. 避免内联脚本
如果你只是“想快速做点什么”,直接在HTML中包含JavaScript可能会很有吸引力,但这大大增加了XSS的可能性(并为以后的维护创造问题)。除此之外,这还会使代码混乱。将JavaScript放在单独的外部文件中,以保持额外的安全层和组织。和组织。你可以在CSP头中定义你的脚本,以保持一切井然有序。这类似于我们将用户输入结合起来,然后直接传递给数据库执行……危险的情况会发生。
4. 使用严格模式
我们不是穴居人,这意味着我们当然希望编写整洁、语法正确的代码。严格模式通过收紧语言的规则来帮助你编写更安全、更整洁、更可预测的代码。你应该在所有提供严格模式的语言中使用严格模式,而不仅仅是JavaScript。
严格模式防止 silent 错误,禁止不安全的操作,阻止你将保留字(例如 let)用于它们原本以外的用途,防止意外的全局变量,并使 this 的使用更安全、更可预测。此外,严格模式还应该提高性能,帮助你更早地发现错误,引导你采用最佳实践,并提高安全性。
5. 使用开源工具
许多这些风险是众所周知的,开源社区的开发者已经创建了减轻这些风险的工具。使用有助于你编写更安全代码的免费、开源软件(FOSS)库和工具,例如:
Ø DomPurify 用于对输入进行XSS清理。
Ø Retire.js 用于查找过时、不受支持或其他危险的JavaScript库。
Ø Npm Audit,Yarn Audit,Snyk CLI(免费版)来检查你的依赖项是否是最新的。
Ø DOM Snitch 是一个 Google 项目,它创建了一个实验性的 Chrome 扩展,使开发人员和测试人员能够识别客户端代码中常见的不安全实践。
Ø Nodejsscan 一个免费的用于 node.js 应用的静态分析工具。
Ø Semgrep 社区版,Bearer CLI(免费版),Horusec等进行免费静态分析。它们都支持多种语言,包括 JavaScript。
Ø Zap 进行免费动态分析 (DAST) 注意:在工作场所运行前请获得老板的许可 Zap!这是一个黑客工具,未经书面许可对任何网站或应用程序进行操作是非法的。如果使用不当,该工具可能会造成损害。请在使用之前先阅读相关资料。其他工具使用起来永远不会危险。
6. 明确标识代码中的文本
在你的代码中明确区分文本和代码,确保变量中的内容不是 应该被执行或解释的代码。我们通过将用户输入的内容放入安全的数据元素中,来确保文本是文本,这样可以用于你的JavaScript或CSS。例如,不要使用innerHtml(会被渲染);而是使用innerText或textContent,这些 clearly 是仅用于显示的文本,不会被解释。始终明确某物是文本;不要让DOM(文档对象模型)来决定,因为有时它会出错。
7. 将变量应用于安全属性
如果必须设置element.setAttribute使用变量中的数据(这通常是用户提供的值,因此是潜在危险的),请只使用安全的静态属性,而不是动态(可更改的)属性。安全属性的示例包括:align,alink,alt,bgcolor,border,cellpadding,cellspacing,class,color,cols,colspan,coords,dir,face,height,hspace,ismap,lang,marginheight,marginwidth,multiple,nohref,noresize,noshade,nowrap,ref,rel,rev,rows,rowspan,scrolling,shape,span,summary,tabindex,title,usemap,valign,value,vlink, vspace, width). 不要使用动态、不安全的属性element.setAttribute,例如onclick或onblur。那些“发生”事情或改变的属性是不安全的,你不应该使用它们来处理用户提供的数据(例如保存到变量中的任何数据)。
当我们讨论使用变量(用户数据)时……它们应该只放在CSS属性值中,而不是放在上下文中。再次强调,我们不希望它们有可能使我们的软件以不可预测的方式行事。
示例:<style> selector { property : $varUnsafe; } </style>
8. 在后端验证输入
为了安全原因进行输入验证(而不是为了可用性、速度或其他任何原因),这些检查必须在后端进行,而不是在前端。任何拥有拦截代理(如Zap或Burp Suite)的人都可以“拦截”你的前端请求,对其修改,然后将其传递到后端,仿佛没有被拦截。如果做得好,后端不会知道在前端之后是否有人对请求进行了任何更改。这种类型的攻击或测试会在使用应用程序的同一台机器上进行,这意味着他们不需要担心加密,他们可以以明文形式直接访问你的整个请求。
为什么这不好?想象一下,你在JavaScript中进行了输入验证,规定你可以接受a-z,大小写,以及所有数字,但不接受其他任何内容。它接受这些数据,然后将其反射回屏幕上供用户阅读。应用程序在理论上会拒绝以下输入“‘ or 1=1 --” ,因为那些字符中有一些不在你的allowlist上。然而,想象一下,一个攻击者正在使用你的应用程序,并且使用了拦截代理。攻击者在字段中输入“Hello”,并通过了输入验证。然后他们拦截你的请求,将字段更改为“ ‘ or 1=1 --”,然后将其发送到你的应用程序。如果后端没有输入验证,应用程序将像通常那样使用这些数据,在这种情况下可能会将其附加到SQL语句中,例如‘select * from table users where username like %’ & your_now_malicious_variable_here & ‘ and password = ‘ & password_variable输入:.
虽然这是一个简化的例子,但希望你能看到,出于安全原因,输入验证必须在后端进行。你总是可以在前端进行,以提高速度和可用性。你只需要确保最终的决定始终在服务器上。
9. 避免使用有问题的函数
避免使用以下这些经常有问题的JavaScript函数。特别是如果你要使用这些函数处理用户提供的或可修改的数据时。如果你必须使用它们,请极其谨慎。eval(),innerHTML,outerHTML,Function(),escape(),unescaped(),document.write(),document.writeln(),unescapeHTML(),decodeURI()和encodeURI(),with(),使用字符串参数(意味着变量)的构造函数setTimeout(),setInterval(),new function,和setAttribute()(回忆一下:如果你使用setAttribute,请只使用静态值)。
10. 将其像其他应用程序一样进行保护
执行所有其他安全编码策略,例如进行输入验证/清理/转义,使用参数化查询而不是内联/动态查询构建,加密传输和静止状态下的数据,并使用可靠的系统进行身份验证、授权、会话管理、身份和秘密管理等。
在大多数人认为是“安全编码”一部分的标准项目中,确保你的团队遵循安全的系统开发生命周期(S-SDLC)是最重要的。当我说一个安全的SDLC,我的意思是,你的团队需要增加额外的活动和流程,以确保你正在开发和维护的软件是安全、可靠和坚固的。你可以添加到任何方法论中的活动包括威胁建模、安全代码审查、自动动态测试、软件成分分析或对你第三方依赖的其他验证、软件供应链强化、创建安全用户故事,或在所有其他项目要求旁边包含安全要求。你添加到SDLC的任何安全活动都将有助于确保我们创建更强大、更值得信赖的系统,供你的用户依赖。你添加的越多越好!
编写更安全的JavaScript的旅程始于小而一致的改变。选择一两个最佳实践并立即付诸行动。当你开始看到它们的影响时,花点时间与朋友或同事分享你所学到的知识。通过知识共享和逐步改进,我们可以使JavaScript不仅更加美丽和强大,而且更加安全。