正文
测试过程详述
低高权限两个用户中菜单栏中销量上报功能栏下不同功能,用低权限Token替换掉高权限Token,低权限可访问高权限的功能列表,同样回显出高权限的回显数据
风险分析
由于后台应用没有做权限控制,或仅仅在菜单、按钮上做了权限控制,导致恶意用户只要猜测其他管理页面的URL或者敏感的参数信息,就可以访问或控制其他角色拥有的数据或页面,达到权限提升的目的。
解决思路
1、使用redis,key=sessionId,value=token
2、用户登录时,将信息存到缓存中
3、当用户请求接口时,在网关获取到sessionId,再根据传入的token去对比缓存的token。如果一致,就放行;如果不一致,就报错:不可越权,请求未授权
4、用户登出时,清出sessionId的缓存
代码
网关的代码
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
String path = exchange.getRequest().getURI().getPath();
if (isSkip(path)) {
return chain.filter(exchange);
}
if (Objects.nonNull(authProperties.getSkipAuth()) && authProperties.getSkipAuth()) {
return chain.filter(exchange);
}
ServerHttpResponse resp = exchange.getResponse();
String headerToken = exchange.getRequest().getHeaders().getFirst(AuthProvider.AUTH_KEY);
String paramToken = exchange.getRequest().getQueryParams().getFirst(AuthProvider.AUTH_KEY);
if (StringUtils.isBlank(headerToken) && StringUtils.isBlank(paramToken)) {
return unAuth(resp, "缺失令牌,鉴权失败");
}
String auth = StringUtils.isBlank(headerToken) ? paramToken : headerToken;
String token = JwtUtil.getToken(auth);
Claims claims = JwtUtil.parseJWT(token);
if (claims == null) {
return unAuth(resp, "请求未授权");
}
//是否开启纵向越权
if (authProperties.getUltraVires()) {
Mono<Void> resp1 = isUltraVires(headers, resp, token);
if (resp1 != null) return resp1;
}
return chain.filter(exchange);
}
private Mono<Void> isUltraVires(HttpHeaders headers, ServerHttpResponse resp, String token) {
String cookie = headers.getFirst(TokenConstant.COOKIE);
if(ObjectUtil.isEmpty(cookie)){
return unAuth(resp, "不可越权,请求未授权");
}
String sessionId =cookie.replace("JSESSIONID=","").trim();
RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
String sessionKey = StringUtil.format(CacheNames.SESSION_KEY,sessionId);
String redisToken = (String)redisUtil.get(sessionKey);
if(ObjectUtil.isEmpty(redisToken)){
return unAuth(resp, "不可越权,请求未授权");
}
if(!token.equals(redisToken)){
return unAuth(resp, "不可越权,请求未授权");
}
return null;
}
登陆器的代码
登录时:
/**
* 用于越权校验
* @param request
* @param authInfo
*/
private void setSessionRedis(HttpServletRequest request, AuthInfo authInfo) {
String accessToken = authInfo.getAccessToken();
String sessionId = request.getSession().getId();
String sessionKey = StringUtil.format(CacheNames.SESSION_KEY,sessionId);
redisUtil.set(sessionKey,accessToken);
}
登出时:
@GetMapping("/login/out")
@ApiOperation(value = "退出登录", notes = "退出登录")
public R loginOut() {
HttpServletRequest request = WebUtil.getRequest();
String sessionId = request.getSession().getId();
String sessionKey = StringUtil.format(CacheNames.SESSION_KEY,sessionId);
redisUtil.del(sessionKey);
return R.success();
}
注意
根据YAML语法规范,参数名不能以"is"开头。这是因为以"is"开头的参数名可能与YAML中的布尔值表示冲突,导致解析错误。如果您需要使用以"is"开头的参数名,可以考虑使用引号将其括起来,或者选择其他不以"is"开头的命名方式。请参考YAML规范和相关文档以获取更详细的信息。