大家都知道shiro方法级,请求级保障是不错的,但是对于对于具体的行级数据安全控制是比较无力的,但是我们总是需要用一些手段来保证数据级的安全,举个比较简单的说明,
有合法登录A和B用户,A用户通过方法findById=A可以加载出自己的信息并修改密码,这个时候用户B可以通过findById=A也可以加载出A的信息,这样简单的入侵方式比较简单,
只要使用编辑器改动下表单的提交值即可,如果严谨是没事的,但是总有那么一些人不会那么严谨,所以经常都是通过id直出直入查询到数据,导致用户资料泄漏被修改,
那么问题来了,我们该如何防止这样的低级攻击呢?
下面我提供一个比较简单的方案,希望抛砖引玉,如果有更好的方案请分享出来,互相学习
一般来说,我们更新,删除或者查询单个记录都是需要一个唯一键,例如id?别人可以编辑这个id的值获取其他记录的信息,这个时候我们可以加一个认证值给请求路径,防止只修改了id
即可认为是合法请求
下面上些代码
<a href="#" onclick="removeById('/cms/user/removeById.do?id=${v.id}&rsv_=<@key id="${v.id}" />');return false;" style="cursor: pointer;">删除</a>
看到后面删除的参数有一个rsv_,这个是认证值,如果单独修改id的值,但是没有修改出相应的rsv_值那是认为是非法请求,因为id和rsv_是配套,通过一定算法出来的关联
而这里我的<@key id="${v.id}" /> 这个是我自定义的freemarker标签,相信看过前面的文章也知道该如何配置使用,下面我展示下生成的rsv_标签类
package com.silvery.core.freemarker; import java.io.IOException; import java.util.Map; import com.silvery.utils.ShortLinkUtils; import freemarker.core.Environment; import freemarker.template.TemplateDirectiveBody; import freemarker.template.TemplateDirectiveModel; import freemarker.template.TemplateException; import freemarker.template.TemplateModel; /** * * FreeMarker自定义标签,生成编号认证 * * @author shadow * */ public class KeyTag implements TemplateDirectiveModel { @SuppressWarnings("unchecked") public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody directiveBody) throws TemplateException, IOException { Object id = params.get("id"); validate(id, null); env.getOut().write(ShortLinkUtils.getSingleLink(id.toString())); } private void validate(Object id, Object body) throws TemplateException { if (id == null || id.toString().trim().equals("")) { throw new TemplateException("参数[id]不能为空", null); } } }
然后看我们的请求拦截器,如何判断是否合法请求
package com.silvery.core.spring.handler; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import com.silvery.utils.ShortLinkUtils; public class SystemHandler implements HandlerInterceptor { private final static Logger log = LoggerFactory.getLogger(SystemHandler.class); @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object obj, Exception e) throws Exception { } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object obj, ModelAndView view) throws Exception { } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception { return validateSafeRequest(request, response, obj); } /** 防止擅改数据提交 */ private boolean validateSafeRequest(HttpServletRequest request, HttpServletResponse response, Object obj) { String newObj = request.getParameter("id"); if (!isNull(newObj)) { String newToken = request.getParameter("rsv_"); if (!isNull(newToken)) { if (ShortLinkUtils.getSingleLink(newObj).equals(newToken)) { return true; } } log.error(new StringBuffer().append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append( " [").append(obj).append("] ").append(" appear insecure request ").toString()); return false; } return true; } private boolean isNull(String s) { if (s == null || s.length() <= 0) { return true; } return false; } }
大概流程就是这样,相信大家都能理解,比较简单,比较实用,请随意喷,最多我去蓝翔再深造下回来咯...