整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

微信公众号爆出前端安全漏洞

日在公众号中挖掘到了一个 XSS 安全漏洞,具体复现流程如下:

  1. 发一篇公众号文章,标题中包含 <input onfocus="alert('1')">
  2. 用户打开文章后,在写留言页面中会发现标题没有被转义,正常被渲染成了 HTML
  3. 用户点击被渲染出来的输入框后执行代码

以下是复现漏洞的视频

视频链接

如果视频又打不开了,可以去我公众号的文章里看。

现在我们来分析下这个漏洞的产生过程。

首先标题中存在 HTML <input onfocus="alert('1')">,在网页中如果不对这部分文本做转义的话,就会正常渲染为 HTML。

在文章详情中其实我们并没有发现这个问题,也就说明了在该页面中开发者是做了文本转义的。

但是在留言页面中却出现了该问题,也就是说开发者并没有做标题的转义,因此导致了这个问题的发生。

虽然这个问题触发条件不是那么容易,但是对于微信这样亿级日活的产品出现这样低级的安全问题实属没想到。

我们把这样的安全问题称之为 XSS 攻击。根据攻击的来源,我们可以将此类攻击分为三种,分别为:

  • 反射型
  • 存储型
  • DOM 型

在这个案例中我们遇到的是存储型 XSS 攻击。此类攻击是攻击者将恶意代码提交至服务器并保存在数据库中,用户访问该页面触发攻击行为。这种类型的攻击常见于保存用户编辑数据的场景下,比如案例中的发表文章,亦或者评论场景等等。

防范存储型 XSS 攻击的策略就是不相信一切用户提交的信息,比如说用户的评论、发表的文章等等。对于这些信息一律进行字符串转义,主要是引号、尖括号、斜杠

function escape(str) {
 str = str.replace(/&/g, '&')
 str = str.replace(/</g, '<')
 str = str.replace(/>/g, '>')
 str = str.replace(/"/g, '&quto;')
 str = str.replace(/'/g, ''')
 str = str.replace(/`/g, '`')
 str = str.replace(/\//g, '/')
 return str
}
// "<script>alert(1)</script>"
escape('<script>alert(1)</script>')
复制代码

但是在显示富文本的场景下其实不能把所有的内容都转义了,因为这样会把需要的格式也过滤掉。对于这种情况,通常考虑采用白名单过滤的办法。

// 使用 js-xss 开源项目
const xss = require('xss')
let html = xss('<h1 id="title">XSS</h1><script>alert("xss");</script>')
// -> <h1>XSS</h1><script>alert("xss");</script>
console.log(html)
复制代码

在白名单的情况下,h1 标签不会被转义,但是 script 能被正常转义。

日常开发中,为了方便数据的输入和输出,JavaScript提供了一些常用的输入输出语句,具体如表1-3所示。
表1常用的输入输出语句

类型

语句

说明

输入

prompt()

用于在浏览器中弹出输入框,用户可以输入内容

输出

alert()

用于在浏览器中弹出警告框

document.write()

用于在网页中输出内容

console.log()

用于在控制台中输出信息

接下来将分别演示document.write0、console.log0和promptO的使用。

1. document.write()

document.write0的输出内容中如果含有HTML标签,会被浏览器解析。下面利用documenL.write0在页面中输出“我是document.write0语句!”,示例代码如下。

document.write('我是document.write()语句!');

2. console.log()

利用console.log0语句在控制台输出“我是console.log0语句!”,示例代码如下。

console.log('我是console.log()语句!');

console:.log0的输出结果需要在浏览器的控制台中查看。在Chrome 浏览器中按“F12”键(或在网页空白区域右击,在弹出的菜单中选择“检查”)启动开发者工具,然后切换到“Console”(控制台)面板,即可看到console.log0的输出结果。

3.prompt()

利用prompt0语句实现在页面中弹出一个带有提示信息的输入框,示例代码如下。

prompt(请输入姓名:');

上述示例代码运行后,将在页面中弹出一个输人框并提示用户“请输人姓名:”提示框。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>细说JavaScript中常用的几种对话框</title>
<script type="text/javascript">javascript>
//第一种 最简单的提示框alert(弹出对话框输出自定义的提示信息)
alert("此处填写自定义的提示信息!!");
//第二种 带确认和取消的询问提示框confirm 它的返回值为真true/假false
if(confirm("请问您确认提交吗"))
{
alert("您点击了确定按钮");
}else
{
alert("您点击了取消按钮");
}
//第三种带输入的提示对话框prompt,返回值为 对话框中输入的内容
//这里需要注意的是,prompt有两个参数,前面是提示的话,后面是当对话框出来后,在对话框里的默认值
var name = prompt("请输入您的名字", ""); //将输入的内容赋给变量 name ,
if (name)//如果返回的有内容
{
alert("欢迎您:" + name)
}
</script>
</head>
<body>
</body>
</html>