前言
最近在安恒的漏洞扫描器发现报了一个低危漏洞——表单域隐藏。
该漏洞描述将前端HTML中包含“hidden”属性的字段均视为有低风险存在,并给出建议:检查网页表单中类型为hidden的元素,在服务器端加强校验。
日常认知里,“hidden”属性不是很常用吗(比如前端密码字段),为何存在风险?为此学习并记录一下。
语法简介
首先来了解下HTML hidden 属性:
- hidden 属性规定对元素进行隐藏。
- 隐藏的元素不会被显示。
- 如果使用该属性,则会隐藏元素。
- 可以对 hidden 属性进行设置,使用户在满足某些条件时才能看到某个元素(比如选中复选框,等等)。然后,可使用 JavaScript来删除 hidden 属性,使该元素变得可见。
来看看一个语法入门案例:
将hidden属性删除,效果如下:
风险案例
如图,用户可以通过浏览器F12开发者工具,让隐藏的区块显示出来,并很方便的修改表单的值……
有时候前端会使用隐藏域记录一些关键值(比如修改一条信息时隐藏域记录该信息的ID,提交后根据这个ID做数据更新),可用户一旦修改这些值,再提交,那不就出乱子了,如何解决?
修改UID
前端页面如下:
<form>
<input type=“hidden” name="id" value=1 />
<input type="text" name="userName" />
<input type="text" name="phone" />
<button type="submit" value="保存" />
</form>
Controller 方法如下:
@Controller
public class UserController {
@Autowried
private UserService userService;
@RequestMapping("/update")
public String update(User u) {
userService.update(u);
...
}
}
SQL语句如下:
update user set
userName=#{userName},
phone=#{phone}
where id=#{id}
修改用户信息时,故意将隐藏的id改成其他人的,然后提交,这样的问题怎么解决?
【解决方案】
- 方案一 可以将UID避免通过前端传参,并将登录人的基本信息存到Session里;
- 方案二 将要修改的主键ID加密存储在隐藏域,后端解密获取主键ID;
- 方案三 在表单中,不得不使用隐藏域的时候,需要添加JS校验文件,对隐藏域的值进行校验,检验通过后才允许提交表单。
在数据库进行update数据更新的时候,where后面除了id=id,还要加个UserID=UserID,这样就能保证自己改自己的信息了。即:
update user set
userName=#{userName},
phone=#{phone}
where id=#{id}
and UserID=UserID
修改价格
公共漏洞和披露网站CVE(Common Vulnerabilities and Exposures)公布了Element InstantShop中的Web网页add_2_basket.asp的一个漏洞项,允许远程攻击者通过隐藏的表单变量“price”来修改价格信息。这个表单的形式如下所示:
<INPUT TYPE = HIDDEN NAME = "id" VALUE = "AUTO0034">
<INPUT TYPE = HIDDEN NAME = "product" VALUE = "BMW545">
<INPUT TYPE = HIDDEN NAME = "name" VALUE = "Expensive Car">
<INPUT TYPE = HIDDEN NAME = "price" VALUE = "100">
利用这个漏洞,不怀好意者可以任意设定price字段的值,然后提交给InstantShop网站的后台服务器,从而可能用100美元就可以获得一部BMW545。
修复方案
- 如果表单中隐藏域的值在整个项目中都有使用,可以考虑通过session,在action后台处理界面中,直接读取session的值,而不是通过前台的隐藏域读取。
- 在表单中,不得不使用隐藏域的时候,我们需要添加js校验文件,对隐藏域的值进行校验,检验通过后才允许提交表单。
虽然该漏洞风险很简单,但也算意识到了常用的hidden属性存在的风险。滴水穿石非一日之功,安全路上慢慢积累吧。