大家都知道shiro方法級,請求級保障是不錯的,但是對於對於具體的行級數據安全控制是比較無力的,但是我們總是需要用一些手段來保證數據級的安全,舉個比較簡單的說明,
有合法登錄A和B用戶,A用戶通過方法findById=A可以加載出自己的信息並修改密碼,這個時候用戶B可以通過findById=A也可以加載出A的信息,這樣簡單的入侵方式比較簡單,
只要使用編輯器改動下表單的提交值即可,如果嚴謹是沒事的,但是總有那麼一些人不會那麼嚴謹,所以經常都是通過id直出直入查詢到數據,導致用戶資料泄漏被修改,
那麼問題來了,我們該如何防止這樣的低級攻擊呢?
下面我提供一個比較簡單的方案,希望拋磚引玉,如果有更好的方案請分享出來,互相學習
一般來說,我們更新,刪除或者查詢單個記錄都是需要一個唯一鍵,例如id?別人可以編輯這個id的值獲取其他記錄的信息,這個時候我們可以加一個認證值給請求路徑,防止只修改了id
即可認爲是合法請求
下面上些代碼
<a href="#" οnclick="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;
}
}
這是spring-mvc的攔截器,當然你也可以用普通的filter,原理是一樣的
大概流程就是這樣,相信大家都能理解,比較簡單,比較實用,請隨意噴,最多我去藍翔再深造下回來咯...