文章目录

  • 1.健康监控Actuator暴露端口
  • 2.SSRF漏洞攻击
  • 3.富文本XSS攻击
  • 4.暴力破解短信验证码登录
  • 5.恶意短信轰炸骚扰用户
  • 6.低版本Fastjson导致RCE漏洞
  • 7.SQL注入漏洞
  • 8.水平越权信息泄露
  • 9.权限绕过漏洞

1.健康监控Actuator暴露端口

​ Actuator是Springboot提供的用来对应用系统进行自省和监控的功能模块,借助于Actuator开发者可以很方便的对应用系统的某些监控指标进行查看,统计等。

​ 在springboot项目中引入Actuator:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

漏洞问题:

​ 系统启动后,我们通过访问Actuator暴露的一些端口,可以获取到信息,这样也就存在信息暴露的风险,,以下是一些暴露的端口。

(1)http://ip+port/actuator

​ 通过http://ip+port/actuator可以访问到系统暴露的端点集合,每一个端点都可以进行调用,例http://127.0.0.1:9876/api/actuator,查询结果如下:

(2)http://ip+port/actuator/env

​ 通过http://ip+port/actuator/env可以访问到系统配置环境信息,包含properties中数据库用户名、密码等敏感信息,例http://127.0.0.1:9876/api/actuator/env,查询结果如下:

(3)http://ip+port/actuator/heapdump

​ 通过http://ip+port/actuator/heapdump可以下载堆转储文件,在浏览器中执行此请求,会下载一份文件:

​ 然后利用heapdump_tool工具可以从此headdump文件中提取出数据库等敏感信息:

解决方案

1.在properties中禁止某些端口的访问,例如:

management:endpoints:web:exposure:#禁止env、heapdump、threaddump接口的访问exclude:- env- heapdump- threaddump

此时再访问禁止的端口,显示404:

2.在properties中完全禁用actuator,例如:

management:server:port: -1

此时再访问actuator的端口,都显示404:

2.SSRF漏洞攻击

​ 在项目开发中,有些需求是前端配置ip或者url信息,服务端通过此配置信息进行远程调用。例如系统提供前端页面配置数据库的链接信息:

系统支持前端页面配置获取第三方token的链接信息,通过服务端去调用获取的场景:

漏洞问题:

​ 可以对外网服务器所在的内网、本地进行端口扫描,获取一些服务的banner信息;攻击运行在内网或本地的应用程序(比如溢出);利用ftp、file协议读取本地文件等。

(1)扫描内网服务器开放的端口

​ 数据库链接配置:通过配置内网服务器的ip、端口信息,点击测试连接数据库,当此ip不存在或者端口不存在时,接口返回的报错速度会非常快;当访问到存活ip的存活端口,那么返回的时间会有非常大的差别。

不存在的端口,执行时间:

存活ip、存活端口,执行时间:

(2)访问内网文件

​ 服务器调用第三方系统配置链接信息:当接口地址配置ftp或file时,可以访问到服务器的文件信息。

解决方案:

1.禁止host配置为内网地址;

2.统一错误信息,避免用户可以根据错误信息来判断远端服务端的端口状态;

3.禁用不需要的协议,仅允许http和https请求,防止ftp、telnet、file、ldap等协议引起的问题;

4.对url进行非法特殊字符校验,防止CRLF(换行符\r\n)攻击;

5.对确实需要访问的内网ip、端口进行白名单的配置。

(1)校验url方法:

//校验url信息
private void checkUrl(String url) {try {URL url = new URL(url);//ip或者域名String host = url.getHost();//协议,http/httpsString protocol = url.getProtocol();//判断是否存在非法特殊字符,防止CRLF攻击String docode = URLDecoder.decode(tokenUrl,"utf-8");if(docode.contains("\r\n")){throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "url包含特殊字符,请确保参数的准确性");}//只允许http、https协议if(!(protocol.equals("https") || protocol.equals("http"))){throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "协议只支持http/https,请确保参数的准确性");}//获取端口int port = url.getPort();if(-1 == port){if(protocol.equals("https")){port = 443;} else {port = 80;}}//获取系统配置的白名单ip集合Map<String, String> ignoredMap = apiWhiteListConfig.getIgnoredMap();//校验host和port是否合法IpUtil.checkHostAndPort(host, String.valueOf(port),ignoredMap);} catch (MalformedURLException e) {throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "url解析异常,请确保参数的准确性");} catch (UnsupportedEncodingException e){throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "url编码异常,请确保参数的准确性");}}

(2)配置内网白名单

properties配置文件中配置白名单集合:

#出于屏蔽SSRF攻击,给出放开的内网白名单及端口号
inner:ignored:url-list:- 10.233.144.x:6777- 10.111.144.x:8888- 10.234.100.x:9088

加载配置的白名单集合:

@Configuration
@ConfigurationProperties(prefix="inner.ignored")
@Data
public class ApiWhiteListConfig {//忽略的内网访问集合,key:ip,value:portprivate Map<String,String> ignoredMap = new HashMap<String,String>();private List<String> urlList;@PostConstructpublic void init(){if(null != urlList && urlList.size() > 0){for(int i = 0;i < urlList.size();i++){String str = urlList.get(i);String[] arr = str.split(":");if(null != arr && arr.length == 2){ignoredMap.put(arr[0],arr[1]);}}}}
}

(3)校验host和port是否合法

//ip工具
public class IpUtil {static List<Pattern> ipFilterRegexList = new ArrayList<>();//指定内网ip的匹配正则集合static {Set<String> ipFilter = new HashSet<String>();ipFilter.add("^10\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])"+ "\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])" + "\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])$");ipFilter.add("^172\\.(1[6789]|2[0-9]|3[01])\\" + ".(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])\\"+ ".(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])$");ipFilter.add("^192\\.168\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])\\"+ ".(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])$");ipFilter.add("127.0.0.1");ipFilter.add("0.0.0.0");ipFilter.add("localhost");for (String reg : ipFilter) {ipFilterRegexList.add(Pattern.compile(reg));}}/*** @Description: 判断ip是否属于内网* @Author: qingyun* @Param: [ip]* @Return: boolean* @Date: 2023/1/6 15:50*/public static boolean ipIsInner(String ip) {for (Pattern reg : ipFilterRegexList) {Matcher matcher = reg.matcher(ip);if (matcher.find()){return true;}}return false;}/*** @Description: 根据主机host,获取对应的ip* @Author: qingyun* @Param: [hostName]* @Return: java.lang.String[]* @Date: 2023/1/6 15:44*/public static String[] getAllIPByHostName(String hostName){try {InetAddress[] addrs=InetAddress.getAllByName(hostName);//根据域名创建主机地址对象String[] ips=new String[addrs.length];for (int i = 0; i < addrs.length; i++) {ips[i] = addrs[i].getHostAddress();//获取主机IP地址}return ips;} catch (UnknownHostException e) {return null;}}/*** @Description: 校验host和port是否合法* @Author: qingyun* @Param: [host, port, ignoredMap]* @Return: void* @Date: 2023/1/6 17:32*/public static void checkHostAndPort(String host, String port, Map<String, String> ignoredMap) {//获取到host解析后的真实ip地址String[] allIPByHostName = getAllIPByHostName(host);if(null == allIPByHostName || allIPByHostName.length == 0){throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "host:"+host+"解析异常,请确保您输入的地址是真实有效的");}if(null != ignoredMap && !ignoredMap.isEmpty()){boolean flag = true;for(int i = 0;i < allIPByHostName.length;i++){if(!ignoredMap.containsKey(allIPByHostName[i])){   //host配置的ip不存在与白名单内flag = false;break;} else {  //当包含此ip时,还需要比对下放开的端口if(!port.equals(ignoredMap.get(allIPByHostName[i]))){  //放开的端口不相同flag = false;break;}}}if(flag){   //host和port存在于配置的白名单内,可以直接放行return;}}//校验此host解析出来的ip是否是内网,内网则直接返回,防止ssrf攻击for(int j = 0;j < allIPByHostName.length;j++){if(ipIsInner(allIPByHostName[j])){  //是内网地址,则直接抛出异常throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "host:"+host+"不是外网地址,请确保参数的准确性");}}}
}

(4)统一错误处理,不返回具体报错信息

 try {    //校验地址是否符合要求checkUrl(String url)//调用远端服务器String result = HttpClientUtils.doPost(url, param.toString(), null, null);      } catch (Exception e) {e.printStackTrace();throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "接口请求调用失败,请确保参数的准确性");}

3.富文本XSS攻击

​ 在项目中,有些输入框的文本内容需要带有样式、引入图片等需求,传统的input输入框没法满足,这时候就需要引入富文本编辑器,前端显示富文本的时候使用html标签。例如:

漏洞问题:

​ 当富文本内容包含一些前端js的alert、script等字符时,在进行文本回显的时候,浏览器会执行上述脚本,从而劫持用户会话、危害网站、插入恶意内容、重定向用户、使用恶意软件劫持用户浏览器等。

(1)恶意弹窗

​ 在富文本中包含以下恶意js脚本:

<img src=1 onerror="alert(1)">
<script>alert(1);</script>
<img src=1 onerror=alert(document.cookie);>

​ 在富文本回显的时候,出现弹框:

服务端解决方案:

1.引入jsoup过滤前端提交过来的富文本

​ jsoup是一款java的HTML解析器,可直接解析某个URL地址、HTML文本内容。java引入jsoup的方式:在pom.xml中引入jar包:

 <dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.15.3</version>
</dependency>

​ 调用jsoup提供的API进行字符过滤:

  /*** @Description: 获取经过json处理后的数据,防止xss攻击* @Author: qingyun* @Return: java.lang.String* @Date: 2023/1/5 14:36*/private String getJsonCleanValue(String msg) {Safelist safelist = Safelist.relaxed();String res = Jsoup.clean(msg,safelist);return res;}

2.当Safelist.relaxed()提供的白名单不够用时,进行扩展

​ 首先来看下Safelist.relaxed()定义的白名单集合:

 public static Safelist relaxed() {return new Safelist().addTags( "a", "b", "blockquote", "br", "caption", "cite", "code", "col", "colgroup", "dd", "div", "dl", "dt", "em", "h1", "h2", "h3", "h4", "h5", "h6","i", "img", "li", "ol", "p", "pre", "q", "small", "span", "strike", "strong","sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u", "ul").addAttributes("a", "href", "title").addAttributes("blockquote", "cite").addAttributes("col", "span", "width").addAttributes("colgroup", "span", "width").addAttributes("img", "align", "alt", "height", "src", "title", "width").addAttributes("ol", "start", "type").addAttributes("q", "cite").addAttributes("table", "summary", "width").addAttributes("td", "abbr", "axis", "colspan", "rowspan", "width").addAttributes( "th", "abbr", "axis", "colspan", "rowspan", "scope","width").addAttributes("ul", "type").addProtocols("a", "href", "ftp", "http", "https", "mailto").addProtocols("blockquote", "cite", "http", "https").addProtocols("cite", "cite", "http", "https").addProtocols("img", "src", "http", "https").addProtocols("q", "cite", "http", "https");}

addTags(String … tags):参数的内容表示html标签(元素)的白名单;

addAttributes(String tag,String … attributes):表示指定标签允许配置哪些属性;

addProtocols(String tag,String attribute,String … protocols):表示指定标签的指定属性允许使用哪些协议。

​ 当默认的白名单不满足需求时,可以在既有的基础上添加新的白名单,例如:

 Safelist safelist = Safelist.relaxed().addTags("test").addAttributes("test","src","href").addProtocols("test","src","http");

​ 测试结果如下:

 //输入String str = "<test src='http://xxxxxx'>";//输出<test src="http://xxxxxx"></test>//输入,不支持的https协议String str = "<test src='https://xxxxxx'>";//输出<test></test>

前端解决方案

1.引入js-xss过滤待显示富文本

​ 前端将数据渲染到页面呈现之前,先对内容进行过滤,推荐使用js-xss,官网:http://jsxss.com。

(1)在Node.js上使用

安装:

$ npm install xss --save

使用:

var xss = require('xss');
console.log(xss('<a href="#" onclick="alert(/xss/)">click me</a>'));

(2)在浏览器器上使用

引入文件:

https://raw.github.com/leizongmin/js-xss/master/dist/xss.js

使用:

console.log(filterXSS('<a href="#" onclick="alert(/xss/)">click me</a>'));

4.暴力破解短信验证码登录

​ 现在越来越多的网站支持通过手机号+验证码的方式登录系统,验证码一般4位或者6位数字。

漏洞问题:

​ 当不法用户输入某个已知的其他用户的手机号,点击获取验证码,然后使用程序生成符合位数的验证码进行暴力登录,破解成功后即可获得合法用户的权限,甚至可以破解管理员的密码进而控制整个站点。

解决方案:

1.使用验证码进行验证登录;

2.生成短信验证码时,设置失效时间、可以验证的剩余次数(每次验证数量减1,数量小于0则删除);

(1)生成验证码

​ 验证码存放到redis中,设置失效时间、设置可以验证的剩余次数。

//组织存在redis中的key值
String key = RedisKeyPrefixConstants.PHONE_NUM+phone;
//使用hash方式来存,因为每次验证需要单独更新可验证的次数值
redisTemplate.opsForHash().put(key,"code",verCode);
//设置可以验证的剩余次数
redisTemplate.opsForHash().put(key,"num",5);
//设置过期时间
redisTemplate.expire(key,300,TimeUnit.SECONDS);

(2)校验验证码

​ 判断是否有给此手机号发送过验证码,没发送过直接验证失败;有发送过验证码,判断输入的验证码是否等于redis中存放的,若是相等,则校验通过,删除redis中的记录;若是不相等,则剩余次数减1,然后判断剩余次数是否小于0,小于0,则删除redis中的记录,验证失败,若是大于0则提示验证码错误。

//传递用户手机号、输入的验证码
public boolean verifyCaptcha(String phone, String captcha){String key = RedisKeyPrefixConstants.PHONE_NUM+phone;//获取此手机号的验证码Object object = redisTemplate.opsForHash().get(key, "code");if(null == object){//没有给此手机号发送的验证码记录throw WebErrorUtils.commonError(null, HttpStatus.PROCESSING, "未请求验证码或验证码已失效,请重新登录!");}String res= object.toString();//比较输入的验证码是否正确if(res.equals(captcha)){//验证通过,删除redis的记录redisTemplate.delete(key);return true;}else{//校验次数减1double num = redisTemplate.opsForHash().increment(key,"num",-1);if(num < 0 ){//剩余可以校验的次数小于0,已经用完,则删除redis的记录redisTemplate.delete(key);throw WebErrorUtils.commonError(null, HttpStatus.PROCESSING, "未请求验证码或验证码已失效,请重新登录!");} else {//还有剩余可以校验的次数,返回验证码错误throw WebErrorUtils.commonError(null, HttpStatus.PROCESSING, "验证码错误!");}}}

5.恶意短信轰炸骚扰用户

​ 现在越来越多的网站支持通过手机号+验证码的方式登录系统,验证码一般4位或者6位数字。

漏洞问题:

​ 当不法用户输入某个已知的其他用户的手机号,点击获取验证码,或者用程序循环去跑发送短信验证码的接口,导致此手机号的用户被短信轰炸,受到骚扰。

解决方案:

1.前端进行控制

​ 在点击了获取验证码后,前端隐藏此按钮或者让此按钮灰显不能点击,然后出现一个倒计时的标签,等倒计时结束后才允许点击获取验证码按钮。

2.服务端进行控制

​ 只是前端进行控制,也会存在绕过倒计时限制进行接口调用的情况,例如使用其他api工具直接调用接口。服务端需要对发送短信的接口进行限制,这里使用redis来存已发送的验证码。当某个手机号请求发送验证码,先从redis中查看是否在有效期内给此号码发送过验证码,若发送过,则提示已经发送过,不再重复发送;若不存在,则新生成一个验证码,发送到此手机号上,并存到redis中。

public void sendCaptcha(String phone){String verCode;String key = RedisKeyPrefixConstants.PHONE_NUM+phone;Object object = redisTemplate.opsForHash().get(key, "code");if(null != object){throw WebErrorUtils.commonError(null, HttpStatus.PROCESSING, "该用户验证码已发送,且未过期,请输入验证码登录或注册!");}else {//生成一个6位数的验证码Random r = new Random(System.currentTimeMillis());int low = 100000;int high = 999999;int code = (r.nextInt(high - low) + low);verCode = String.valueOf(code);//使用hash方式来存,因为每次验证需要单独更新可验证的次数值redisTemplate.opsForHash().put(key,"code",verCode);//设置可以验证的剩余次数redisTemplate.opsForHash().put(key,"num",5);//设置过期时间redisTemplate.expire(key,300,TimeUnit.SECONDS);}try {//调用发送短信的方法sendMsg(phone, verCode);}catch (Throwable throwable){//短信发送失败,删除redis记录redisTemplate.delete(key);throw WebErrorUtils.commonError(throwable, HttpStatus.PROCESSING, "短信发送失败!");}}

6.低版本Fastjson导致RCE漏洞

​ Fastjson是java的一个库,可以将java对象通过toJSONString()转化为json格式的字符串,也可以将json格式的字符串通过parseObject()转化为java对象。

​ 在springboot中引入fastjson:

 <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency>

漏洞问题:

​ 前端使用json格式把参数传递到服务端,服务端在进行反序列化(json格式转为java对象)的时候,会进入parseField方法,进入该方法后,会调用setValue(object,value)方法,这里会执行构造的恶意代码,最终造成代码执行。Fastjson采用黑白名单的方法来防御反序列化漏洞,导致当不法用户不断发掘新的反序列化类时,就算在autoType关闭的情况下仍然可以绕过黑白名单防御机制,造成远程命令执行漏洞。

解决方案:

1.更新Fastjson到最新版本

​ 在<=1.2.68版本中,攻击者可以通过精心构造的json请求,远程执行恶意代码,所以把Fastjson版本更新到最新版解决此问题。

7.SQL注入漏洞

​ 在开发的接口中,存在需要前端输入参数,服务端根据参数组织sql查询数据的情况。

漏洞问题:

​ 若是服务端对用户输入数据的合法性没有判断或过滤不严,攻击者可以在事先定义好的查询语句结尾添加上额外的sql语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务执行非授权的任意查询,从而进一步得到响应的数据信息。

(1)sql拼接参数传递问题

​ 对于mybatis,组织参数到sql中,可以使用拼接符号进行参数的拼接,也可以使用#{}进行参数的占位,当进行sql预编译的时候,${}修饰的会把参数值原样拼接到sql中;#{}修饰的会使用占位符?占位,并把参数值作为参数传递进去,当参数为字符串类型时,会默认给参数值加上单引号。

​ 例如以下查询sql:

select * from user where id = ${id}

若参数值id=1 or 1=1,则经过编译后的sql为:

select * from user where id = 1 or 1=1

此时会查询出所有的用户数据,导致信息泄露。

(2)没法使用#{}设置参数的order by排序字段

​ 当sql的查询结果支持多种方式排序,且排序字段和排序方式由前端进行传递时,若是我们使用#{}的方式来占位参数,编译出来的sql是错误的,编译之后会给此排序字段加上单引号。

​ 例如以下查询sql:

select * from user order by #{orderFiled} #{order}

若参数值orderFiled = ‘created_time’,order=‘desc’,经过编译后的sql为:

select * from user order by  'created_time' 'desc'

此sql是有问题的,排序字段和排序方式不能加单引号。那只能使用${}进行sql拼接的方式,此时若参数值orderFiled = ‘created_time’,order=‘desc,(SELECT*FROM(SELECT+SLEEP(3)UNION/**/SELECT+1)a)’,经过编译后的的sql为:

select * from user order by  created_time desc,(SELECT*FROM(SELECT+SLEEP(3)UNION/**/SELECT+1)a)

此时mysql会被恶意查询拖延时间,甚至拖垮mysql服务器。

解决方案:

1.sql组织时尽量使用#{}占位符代替${}拼接符;

2.动态字段排序order by这样没法使用#{}的情况,可以使用以下两种方式处理:

①代码中判断:服务端收到参数后,对参数的值进行校验,判断是否属于允许的值,允许才放行;校验通过后拼接sql即可使用${}的方式。

//校验排序字段,也可以定义枚举值的方式判断
if(!("created_time".equalsIgnoreCase(orderFiled) || "updated_time".equalsIgnoreCase(orderFiled))){throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "排序字段非法");}//校验排序方式,也可以定义枚举值的方式判断
if(!("desc".equalsIgnoreCase(order) || "asc".equalsIgnoreCase(order))){throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "排序方式非法");
}

②组织sql时判断:判断参数的值是否等于系统约定的值,等于才进行拼接,不等于使用默认的排序方式。

SELECT * FROM USER
ORDER BY
<choose> <WHEN  test="(orderFiled=='created_time' or orderFiled=='updated_time') and (order=='desc' or order=='asc')">${orderFiled}  ${order} </WHEN > <otherwise>updated_time DESC,created_time DESC </otherwise>
</choose>

8.水平越权信息泄露

​ 服务端给出的接口,基本都是根据参数查询、删除、更新、添加数据,用得比较多的是根据某个id去操作数据,而出于性能考虑,id一般都采用整数自增的方式,这样就会存在id被枚举的情况。

漏洞问题:

​ 若是对某条记录的操作不加权限的验证,直接返回此参数对应的记录或者修改记录,则会越权操作本来不属于此用户的记录,导致信息会被恶意用户盗取甚至篡改其他合法用户的信息。

(1)通过枚举访问资源

​ 有些场景下,用户发布一个对外的链接地址(不需要登录直接访问),此链接对应的资源只想让他熟悉的人知道,用户会把此链接单独发给他信赖的人。若是此链接地址的结尾是一个自增型的整数,则用户通过枚举结尾,就能轻松访问到其他用户发布的链接资源。

​ 例如以下的链接地址:

(2)越权查询记录

​ 用户使用系统时,可以通过浏览器查看调用了哪些接口,以及各个接口传递的参数。若是对访问的记录不加权限验证,则可能出现越权查看其他用户记录的情况。

​ 例如以下的接口访问数据:

当枚举id的值进行访问,没加权限验证,则可以越权访问记录。

解决方案:

1.对外公开的链接资源,结尾地址使用uuid,避免被枚举;

2.对于系统资源权限的验证,可以结合着项目当中引入的框架来定,例如使用spring security,在接口上使用注解的方式加上权限的标识,在管理后台对用户进行权限的分配,一般都是用户与角色挂钩,角色与菜单和权限挂钩;

3.对于比较细粒度的权限校验,可以结合着redis来实现,判断此记录是否属于此人的资源,属于才让访问,不属于则抛出异常。可以在记录产生的时候直接加到redis中,并且与某个用户进行关联,当操作此记录的时候,再从redis中取出,看此用户是否有权限操作。

//判断登录用户是否有权操作此记录
private boolean checkXXXById(Integer id) {//获取到当前用户String currentUserId = xxx.getLoginIdAsString();//组织redis的keyString key = RedisKeyPrefixConstants.USER_XXX_ID_SET+currentUserId;Boolean aBoolean = redisTemplate.hasKey(key);if(aBoolean) {   //存在此key的缓存值Boolean member = redisTemplate.opsForSet().isMember(key, id);if(member) {    //set集合中存在此值return true;}}else {   //不存在此缓存的值,则查询数据库List<Integer> xxxIdList = xxxkDao.getxxxIdList(currentUserId);  //if(null != xxxIdList && xxxIdList.size() > 0){//把查询结果放到redis中redisTemplate.opsForSet().add(key,xxxIdList.toArray());//判断从数据库中查询的记录是否包含此id,包含则放行if(xxxIdList.contains(id)){return true;}}}return false;}

9.权限绕过漏洞

​ 在项目开发中,会有一些需求要等第一步校验通过,返回成功状态码之后,才进行第二步的操作。例如访问一个带密码的链接,需要先等密码验证通过后才加载资源。

漏洞问题:

​ 当第一步校验后,抓包修改返回值,然后放包,即可绕过权限进行第二步的加载,导致攻击者访问未经授权的资源。

(1)抓包修改校验结果访问资源

​ 对于需要密码访问的链接,随便输入密码,对校验结果进行抓包,修改状态值为通过, 然后放包,即可查看到资源。

解决方案:

1.校验密码是否正确的时候,生成一个uuid的会话状态码,以此uuid+这个链接id作为redis的key值,把校验结果作为value存放到redis中,并设置过期时间为3秒,然后把校验结果和uuid状态码返回给前端;

2.执行第二步的时候,需要把第一步的uuid值、链接id作为参数传到服务端,服务端根据这两个参数组织redis的key,查询redis是否有此记录,若是没有,直接返回,说明是伪造参数或者已经过期;若是存在,则判断第一步校验的结果状态值,通过则放行,否则返回。

(1)第一步校验密码逻辑

 public XXX checkVisitPassword(String id, String visitPassword) {if(StringUtils.isEmpty(id)) {throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "id参数为空,请确保参数的准确性");}if(StringUtils.isBlank(visitPassword)) {throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "密码为空,请确保参数的准确性");}//查询是否存在此id与密码的值YYY yyy = yyyDao.getYyyByIdAndVisitPassword(id,visitPassword);XXX xxx = new XXX();xxx.setId(id);//生成uuid会话值String sessionId = IdUtil.simpleUUID();xxx.setSessionId(sessionId);//校验结果状态值String state = "ok";if(null == yyy) {  //不通过xxx.setCode(1);state = "error";} else {       //通过xxx.setCode(0);}//校验结果状态值存到redis中,过期时间3秒redisTemplate.opsForValue().set(RedisKeyPrefixConstants.VIEW_REQUESTID_STATE+id+":"+sessionId,state,3, TimeUnit.SECONDS);return xxx;}

(2)第二步获取资源逻辑

public XXX getMessage(String id,String sessionId) {if(StringUtils.isEmpty(id)) {throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "id参数为空,请确保参数的准确性");}if(StringUtils.isEmpty(sessionId)){         //会话id为空throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "sessionId参数为空,请确保参数的准确性");}String key = RedisKeyPrefixConstants.VIEW_REQUESTID_STATE+id+":"+sessionId;Object object = redisTemplate.opsForValue().get(key);if(null == object) {   //redis缓存中不存在此记录throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "输入密码的会话已失效,请重新加载访问");} else {redisTemplate.delete(key);String state = object.toString();if(StringUtils.isEmpty(state)){         //回话状态为空throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "输入密码的会话已失效,请重新加载访问");}if("error".equals(state)){throw WebErrorUtils.commonError(null, HttpStatus.UNPROCESSABLE_ENTITY, "输入的密码错误,请重新加载访问");}}//校验通过,查询数据返回XXX xxx = xxxService.findById(id);return xxx;}

Java开发常见漏洞及解决方案相关推荐

  1. java开发常见异常_最常见的Java异常及其对Java开发人员的评价

    java开发常见异常 我知道我说过在完成视频之前我不会发帖,但这一直困扰着我. 至于视频的更新,我有些懒散,有些忙碌,但是我已经准备好记录第一集,这是我第一次有机会,而且我不需要太多的编辑. 无论如何 ...

  2. Java开发常见英语词汇汇总

    小扎汇总了常用的Java常用名词中英对照,闲来无事可以看看自己认识多少?需要补充哪些常识? A. array数组   add 增加   accessible 可存取的   area面积   audio ...

  3. Java开发常见关键词集绵

    一.关键词1: (1)RPC:远程过程调用(Remote Procedure Call)的缩写形式.远程调用的时候让人们觉得是本地调用. (2)HTTP:超文本传输协议(Hyper Text Tran ...

  4. 50 个 Java 开发常见错误及规避技巧 (Part 2)

    转载至:https://www.oschina.net/translate/50-common-java-errors-and-how-to-avoid-them-part-2 编译器错误 编译器错误 ...

  5. java代码审计常见漏洞_java代码审计基础教程之V2会议系统多个漏洞集合/无需登录...

    因为学习java并不是很长时间,也没有做深入的研究.但是在学习之后,发现可以审计出一些简单的javaweb漏洞,所以想这这里和大家分享一下.0x01审计之初首先,我拿到了源码之后,大概看了一下这个系统 ...

  6. 微信小程序开发常见warnings警告解决方案

    在小程序开发过程中,只要稍不注意代码细节,就会出现很多 warnings 警告,虽然在对小程序的运行并没有什么影响,但是作为一名严谨的程序猿,是不允许它们存在的. 下面我将从我的实际项目开发过程中碰到 ...

  7. HTML转pdf几种解决方案,java开发html转pdf 解决方案

    之前在工作中遇到过一个需求,是将用户填写的申报,由固定模板的来生成pdf,后来想到先把内容生成html, 然后再把html格式的内容转换成pdf. 之前在网上找了很多,有很多内容都是转载的,后来折腾了 ...

  8. java开发常见的热词奇解

    2019独角兽企业重金招聘Python工程师标准>>> 1.分布式系统:一个厨房里有负责炒菜的.有负责洗菜的 2.集群:一个厨房里有两个负责炒菜的 3.分布式计算:两个厨师各自炒同一 ...

  9. Hadoop中的MapReduce框架原理、数据清洗(ETL)、MapReduce开发总结、常见错误及解决方案

    文章目录 13.MapReduce框架原理 13.7 数据清洗(ETL) 13.7.1 需求 13.7.1.1 输入数据 13.7.1.2 期望输出数据 13.7.2 需求分析 13.7.3实现代码 ...

最新文章

  1. macos下使用aria2_用Aria2代替Firefox内置的下载器
  2. 基于Picture Library创建的图片文档库中的上传多个文件功能(upload multiple files)报错怎么解决?...
  3. 编写XML作为配置文件的高级操作库
  4. byte 8位有符号 与 char 16位无符号
  5. java动态加载jar文件并执行方法
  6. c# 读hex_在C#中将string转换为hexstring
  7. 2017-2018-1 20155338 加分项目——PWD的实现
  8. MongoDB(window安装及服务配置)
  9. sql 基础增删改查语句
  10. 头条+腾讯 NLP 面经
  11. 《JavaScript启示录》——1.7 原始值(或简单值)
  12. mysql 查看tomcat日志_查看tomcat日志
  13. Vs code 设置缩进两个字符
  14. 问题 B: 零基础学C/C++25——判断某整数是正整数、负整数还是零
  15. 全国计算机等级考试有java吗,请问全国计算机等级考试有没有二级Java?
  16. PDCCH-based WUS signal
  17. 牛掰!SSH端口嗅探方法及其防御技术
  18. 人工智能-一种现代的方法-chapter3-通过搜索进行问题求解
  19. python测试开发django(8)--django连接mysql
  20. 在Linux系统中输入“ls /“,查看系统根目录结构:

热门文章

  1. 域名与服务器负载均衡技术学习总结
  2. 5G驱动光缆市场发展,MPO光缆将成需求热门
  3. 计算机毕业设计ssm软件项目Bug管理系统612ed系统+程序+源码+lw+远程部署
  4. Mysql数据迁移到达梦数据库
  5. 如何获取图片的倒映及剪切、放缩图片?
  6. Verdi的启动和设置
  7. 七大成功微信运营案例盘点
  8. 现代摩比斯对Velodyne进行战略投资并签署合作伙伴关系
  9. 关于Sublime text 2中Emmet的安装
  10. 编译Android 9.0内核源码并刷入手机