防止 Javascript 中的 HTML 和脚本注入
Posted
技术标签:
【中文标题】防止 Javascript 中的 HTML 和脚本注入【英文标题】:Preventing HTML and Script injections in Javascript 【发布时间】:2014-01-18 06:42:46 【问题描述】:假设我有一个带有输入框的页面。用户在输入框中输入内容并点击按钮。该按钮触发一个函数,该函数获取输入到文本框中的值并将其输出到文本框下方的页面上,无论出于何种原因。
现在很难找到明确的答案,否则我不会问,但你会如何输出这个字符串:
<script>alert("hello")</script> <h1> Hello World </h1>
这样既不执行脚本也不显示 html 元素?
我在这里真正要问的是,是否有一种标准方法可以避免 Javascript 中的 HTML 和脚本注入。每个人似乎都有不同的方法(我正在使用 jQuery,所以我知道我可以简单地将字符串输出到 text 元素而不是 html 元素,但这不是重点)。
【问题讨论】:
How to prevent javascript injection attacks within user-generated HTML的可能重复 您要阻止 所有 HTML 注入,还是仅阻止 不安全 注入? 另外,如果用例真的如你所说,而且这只是客户端 JavaScript,你真的不需要防止“注入”。只有当输入没有显示给其他人时,用户才能攻击自己(如果它显示给其他用户,你会在服务器端清理输入)。 所有,这更多是关于防止此类事情发生的概念和方法的解释。什么是不安全的 html 注入而不是安全的?<h1>Hello World</h1>
是一种安全注入,因为它不会给用户带来安全风险。如果您想防止 HTML/JS 注入,您可以删除编码 HTML 标记。就这么简单。
【参考方案1】:
您可以将 <
和 >
编码为它们的 HTML 等效项。
html = html.replace(/</g, "<").replace(/>/g, ">");
How to display HTML tags as plain text
【讨论】:
王牌!正是我想要的,如果不清楚,请道歉。这是一个正则表达式呢?除了对其他代码稍作修改外,我没有特别使用它们。 这是一个非常简单的正则表达式——表达式基本上是<
。在 javascript 中,正则表达式在斜杠 (//
) 内定义,g
表示它应该搜索所有出现的事件(g = 全局)。
@dlampard 请注意,这正是 .text()
所做的,所以如果你有 jQuery,就不需要自定义正则表达式。
好的,我试图避免使用 jQuery,因为它在我试图理解概念和基础知识时掩盖了实际发生的事情。不过我确实喜欢它。
如果您只想允许一些标签,例如粗体或斜体,您知道该怎么做吗?【参考方案2】:
myDiv.textContent = arbitraryHtmlString
正如@Dan 指出的那样,不要使用innerHTML,即使在你不附加到文档的节点中,因为延迟的回调和脚本总是被执行。您可以查看此https://gomakethings.com/preventing-cross-site-scripting-attacks-when-using-innerhtml-in-vanilla-javascript/ 了解更多信息。
【讨论】:
请注意,该问题不包含jquery
标记,因此您应该尝试用纯javascript 回答。
如果 OP 要求,我将详细说明并提供无 jquery 的答案。这里的原理是一样的。
我不完全确定为什么,但如果 html 的值为 0,这似乎返回一个空字符串。
你必须给它一个字符串,所以"0"
可以工作。您可以检查源 github.com/jquery/jquery/blob/2.2-stable/src/core/parseHTML.js 并查看它丢弃非字符串输入
问题:您确定temp.innerHTML = arbitraryHtmlString;
不会开始预加载图像并运行任何在图像标签等中定义的onload
处理程序吗?【参考方案3】:
单行:
var encodedMsg = $('<div />').text(message).html();
看看效果:
https://jsfiddle.net/TimothyKanski/wnt8o12j/
【讨论】:
【参考方案4】:我使用这个函数 htmlentities($string):
$msg = "<script>alert("hello")</script> <h1> Hello World </h1>" $msg = htmlentities($msg); echo $msg;
【讨论】:
【参考方案5】:From here
var string="<script>...</script>";
string=encodeURIComponent(string); // %3Cscript%3E...%3C/script%3
【讨论】:
【参考方案6】:我使用打字稿+装饰器+正则表达式的解决方案
const removeTag = new RegExp("(<[a-zA-Z0-9]+>)|(</[a-zA-Z0-9]+>)", "g");
return value.replace(removeTag, "");
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc)
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
;
var __metadata = (this && this.__metadata) || function (k, v)
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
;
function filter(target)
return class extends target
constructor(...args)
super(...args);
setState(opts)
const state =
username: this.filter(opts.username),
password: this.filter(opts.password),
;
super.setState(state);
filter(value)
const removeTag = new RegExp("(<[a-zA-Z0-9]+>)|(</[a-zA-Z0-9]+>)", "g");
return value.replace(removeTag, "");
;
let Form = class Form
constructor()
this.state =
username: "",
password: "",
;
setState(opts)
this.state =
...this.state,
...opts,
;
getState()
return this.state;
;
Form = __decorate([
filter,
__metadata("design:paramtypes", [])
], Form);
function getElement(key)
return document.getElementById(key);
const button = getElement("btn");
const username = getElement("username");
const password = getElement("password");
const usernameOutput = getElement("username-output");
const passwordOutput = getElement("password-output");
function handleClick()
const form = new Form();
form.setState( username: username.value, password: password.value );
usernameOutput.innerHTML = `Username: $form.getState().username`;
passwordOutput.innerHTML = `Password: $form.getState().password`;
button.onclick = handleClick;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
:root
--bg: #1d1907;
--foreground: #e3e0cd;
--primary: #cfb53b;
--black: #333;
--white: #fafafa;
@keyframes borderColor
from
border-bottom: 1px solid var(--foreground);
to
border-bottom: 1px solid var(--primary);
*
outline: none;
border: none;
body
padding: 0.5rem;
font-family: "Fira Code";
background-color: var(--bg);
color: var(--foreground);
input
border-bottom: 1px solid var(--foreground);
background-color: var(--black);
color: var(--foreground);
padding: 0.5rem;
input:focus
animation-name: borderColor;
animation-duration: 3s;
animation-fill-mode: forwards;
button
padding: 0.5rem;
border-radius: 3px;
border: 1px solid var(--primary);
background-color: var(--primary);
color: var(--white);
button:hover,
button:active
background-color: var(--white);
color: var(--primary);
.form
margin-bottom: 2rem;
</style>
<title>Decorator</title>
</head>
<body>
<h1>Prevent Injection</h1>
<div class="form">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" placeholder="Type your username" />
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" placeholder="Type your password" />
</div>
<div class="form-group">
<button id="btn">Enviar</button>
</div>
</div>
<div class="form-result">
<p id="username-output">Username:</p>
<p id="password-output">Password:</p>
</div>
<script src="/dist/pratica1.js"></script>
</body>
</html>
打字稿代码如下:
type State =
username: string;
password: string;
;
function filter<T extends new (...args: any[]) => any>(target: T): T
return class extends target
constructor(...args: any[])
super(...args);
setState(opts: State)
const state =
username: this.filter(opts.username),
password: this.filter(opts.password),
;
super.setState(state);
filter(value: string)
const removeTag = new RegExp("(<[a-zA-Z0-9]+>)|(</[a-zA-Z0-9]+>)", "g");
return value.replace(removeTag, "");
;
@filter
class Form
private state: State;
constructor()
this.state =
username: "",
password: "",
;
setState(opts: State)
this.state =
...this.state,
...opts,
;
getState()
return this.state;
function getElement(key: string): HTMLElement | null
return document.getElementById(key);
const button = getElement("btn") as HTMLButtonElement;
const username = getElement("username") as HTMLInputElement;
const password = getElement("password") as HTMLInputElement;
const usernameOutput = getElement("username-output") as HTMLParagraphElement;
const passwordOutput = getElement("password-output") as HTMLParagraphElement;
function handleClick()
const form = new Form();
form.setState( username: username.value, password: password.value );
usernameOutput.innerHTML = `Username: $form.getState().username`;
passwordOutput.innerHTML = `Password: $form.getState().password`;
button.onclick = handleClick;
【讨论】:
【参考方案7】:试试这个方法将“可能包含 html 代码的字符串”转换为“文本格式”:
$msg = "<div></div>";
$safe_msg = htmlspecialchars($msg, ENT_QUOTES);
echo $safe_msg;
希望这会有所帮助!
【讨论】:
【参考方案8】:用这个,
function restrict(elem)
var tf = _(elem);
var rx = new RegExp;
if(elem == "email")
rx = /[ '"]/gi;
else if(elem == "search" || elem == "comment")
rx = /[^a-z 0-9.,?]/gi;
else
rx = /[^a-z0-9]/gi;
tf.value = tf.value.replace(rx , "" );
在后端,对于 java ,尝试使用 StringUtils 类或自定义脚本。
public static String HTMLEncode(String aTagFragment)
final StringBuffer result = new StringBuffer();
final StringCharacterIterator iterator = new
StringCharacterIterator(aTagFragment);
char character = iterator.current();
while (character != StringCharacterIterator.DONE )
if (character == '<')
result.append("<");
else if (character == '>')
result.append(">");
else if (character == '\"')
result.append(""");
else if (character == '\'')
result.append("'");
else if (character == '\\')
result.append("\");
else if (character == '&')
result.append("&");
else
//the char is not a special one
//add it to the result as is
result.append(character);
character = iterator.next();
return result.toString();
【讨论】:
以上是关于防止 Javascript 中的 HTML 和脚本注入的主要内容,如果未能解决你的问题,请参考以下文章
如何防止用户生成的 HTML 中的 Javascript 注入攻击
防止在 Asp.Net Core ViewComponents 中加载多个 JavaScript 脚本
[JavaWeb]_[初级]_[对Html特殊符号进行转义防止XSS攻击和反转义]
[JavaWeb]_[初级]_[对Html特殊符号进行转义防止XSS攻击和反转义]