Java代码规范(安全问题) 修改案例

不安全的随机数

描述 :

标准的伪随机数值生成器不能抵挡各种加密攻击,因为其输出的随机数容易被计算预测。在对安全性要求较高的环境中,比如生成访问令牌(Token) ,生成密钥等,使用能够生成可预测值的函数作为随机数据源,会导致系统安全性降低。

修复建议:

如果程序的安全性要求较高,则应使用更安全的随机数生成器,如 java.security.SecureRandom 类。

示例:

一: INT类型示例
1. 不安全生产随机数
    new Random().nextInt(999999999)
2. 修改后安全随机数
   SecureRandom secureRandom = new SecureRandom();
   secureRandom.nextInt(999999999)
二: LONG类型示例
3. 不安全随机数示例
   RandomUtils.nextLong(starValue, endValue) 
4. 修改后安全随机数
    SecureRandom secureRandom = new SecureRandom();
    secureRandom.nextLong() % (starValue+endValue) +starValue;   
      

不安全的JSON反序列化

描述:

对用户控制的 Json 流进行反序列化,可能让攻击者有机会在服务器上执行任意代码 、滥用应用程序逻辑和/或导致拒绝服务将对象图转换为 Json 格式数据的 Json 序 列化库可能包括重新从 Json 流重构对象所需的元数据。如果攻击者可以指定要重构 的对象类,并能够强制应用程序运行具有用户控制数据的任意资源库,则在 Json 流 的反序列化期间他们也许能够执行任意代码

解决方案

如果可能,在没有验证 Json 流的内容的情况下,请勿对不受信任的数据进行反序列化。根据所用的 Json 库,也许可以将反序列化过程中构造的类列入白名单。
示例:
不安全示例:

           JSONObject jsonObject = JSONObject.parseObject(s); //直接转换为JSONOBjet对象
           if (jsonObject.getInteger("code") == 0) {
               JSONObject data = jsonObject.getJSONObject("data");
               userName = data.getString("name");
           }

方案一

JsonSerializationWhitelist whitelist = new JsonSerializationWhitelist();
whitelist.addToWhitelist(User.class);

ObjectMapper objectMapper = new ObjectMapper();
whitelist.configureObjectMapper(objectMapper);

User user = new User();
user.setName("John Doe");
user.setAge(30);

String json = objectMapper.writeValueAsString(user);
System.out.println(json); // 只会序列化 "name" 字段

方案二

用具体的类接收

          String s = HttpUtil.doGet(userInfoUrl, requestJwtToken);
          ResponseInfo<UserInfo> responseInfo = JSONObject.parseObject(s,ResponseInfo.class);
          if(responseInfo.getCode() == 0){
              userName = responseInfo.getData().getName();
          }

资源未释放:I/O流 (IO流未关闭)

描述

程序在使用系统I/O输入输出流后资源没有正确释放,尤其是多次创建资源未释放会占用系统开销,影响程序性能,攻击者可能会通过耗尽资源池的方式发起拒绝服务攻击。

解決方案

1 .请不要依赖 finalize() 回收资源 2. 在 finally 代码段中合理释放资源

不安全示例

        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URLConnection connection = new URL(url).openConnection();
            setURLConnection(connection);
            connection.setDoOutput(true);
            connection.setDoInput(true);
            // 此处为直接new 出来 OutputStreamWriter导致IO 资源为释放
            out = new PrintWriter(new OutputStreamWriter(connection.getOutputStream(), UTF8));
            out.print(param);
            out.flush();
            // 催出为直接new 出来 InputStreamReader  导致IO 资源为释放
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), UTF8)); 
            StringBuilder sb = new StringBuilder();
            for (String line; (line = in.readLine()) != null; ) {
                sb.append(line);
            }
            result = sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("发送POST请求时出现异常,异常原因[{}]", e);
        } finally {
            IOUtils.closeQuietly(out);
            IOUtils.closeQuietly(in);
        }  

解决方案

        PrintWriter out = null;
        BufferedReader in = null;
        InputStreamReader inputStreamReader =null;
        InputStream inputStream = null; 
        OutputStream outputStream = null;
        OutputStreamWriter outputStreamWriter = null;
        String result = "";
        try {
            URLConnection connection = new URL(url).openConnection();
            setURLConnection(connection);
            connection.setDoOutput(true);
            connection.setDoInput(true);
            //原来直接new 出来的输出流单独抽离了出来
            outputStream = connection.getOutputStream(); 
            outputStreamWriter = new OutputStreamWriter(outputStream, UTF8);
            out = new PrintWriter(outputStreamWriter);
            out.print(param);
            out.flush();
            //原来直接new 出来的输入流单独抽离了出来
            inputStream = connection.getInputStream();
            inputStreamReader = new InputStreamReader(inputStream, UTF8);
            in = new BufferedReader(inputStreamReader);
            StringBuilder sb = new StringBuilder();
            for (String line; (line = in.readLine()) != null; ) {
                sb.append(line);
            }
            result = sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("代码异常,异常原因[{}]", e);
        } finally {
           // 在此处对IO流进行关闭  OUtils.closeQuietly 会自判空
            IOUtils.closeQuietly(out);
            IOUtils.closeQuietly(in);
            IOUtils.closeQuietly(inputStreamReader);
            IOUtils.closeQuietly(inputStream);
            IOUtils.closeQuietly(outputStream);
            IOUtils.closeQuietly(outputStreamWriter);
        }

不安全的组件:Log4j2

描述

Apache Log4j2 是一款开源的 Java 日志记录工具,大量的业务框架都使用了该组件。此次漏洞是用于 Log4j2 提供的 lookup 功能造成的,该功能允许开发者通过一些协议去读取相应环境中的配置。但在实现的过程中,并未对输入进行严格的判断,从而造成漏洞的发生

解决方案

        将依赖版本升级为对应的安全版本 具体可以查看官方版本

SQL注入攻击

描述

SQL注入是指web应用程序对用户输入数据的合法性没有判断或过滤不严,数据在服务器端被接收后用于参与数据库操作,最终达到欺骗服务器执行恶意的 SQL 命令的效果。理论上来讲,应用程序中只要是与数据库有数据交互的地方,无论是增删改查,如果数据完全受用户控制,而应用程序又处理不当,那么这些地方都是可能存在 SQL 注入的。 恶意攻击者除了可以利用 SQL 注入漏洞获取数据库中的信息(例如,管理员后台密码、站点的用户个人信息)之外,甚至在数据库权限足够的情况下可以向服务器中写入一句话木马,从而获取 webshell 或进一步获取服务器系统权限

修复方案

1. 使用预编译处理输入参数:要防御 SQL 注入,用户的输入就不能直接嵌套在 SQL 语句当中。使用参数化的语句,用户的输入就被限制于一个参数当中。2. 输入验证:检查用户输入的合法性,以确保输入的内容为正常的数据。数据检查应当在客户端和服务器端都执行,之所以要执行服务器端验证,是因为客户端的校验往往只是减轻服务器的压力和提高对用户的友好度,攻击者完全有可能通过抓包修改参数或者是获得网页的源代码后,修改验证合法性的脚本(或者直接删除脚本),然后将非法内容通过修改后的表单提交给服务器等等手段绕过客户端的校验。因此,要保证验证操作确实已经执行,唯一的办法就是在服务器端也执行验证。但是这些方法很容易出现由于过滤不严导致恶意攻击者可能绕过这些过滤的现象,需要慎重使用。3. 错误消息处理:防范 SQL 注入,还要避免出现一些详细的错误消息,恶意攻击者往往会利用这些报错信息来判断后台 SQL 的拼接形式,甚至是直接利用这些报错注入将数据库中的数据通过报错信息显示出来。4.加密处理:将用户登录名称、密码等数据加密保存。加密用户输入的数据,然后再将它与数据库中保存的数据比较,这相当于对用户输入的数据进行了“消毒”处理,用户输入的数据不再对数据库有任何特殊的意义,从而也就防止了攻击者注入 SQL 命令。

方案一

使用ORM框架 如Mybatis 如已使用则避免使用 ${}方式取值 换为 #{}

示例
   1. 不安全示例
  <select id="getSqlCount" parameterType="String" resultType="java.lang.Integer">
        SELECT COUNT(*) AS total FROM t where  col1= ${xxx}
    </select>
    

    2. 安全示例1
     <select id="getSqlCount" parameterType="String" resultType="java.lang.Integer">
        SELECT COUNT(*) AS total FROM t where  col1= #{xxx}
    </select>

     2. 安全示例2
     <select id="getSqlCount" parameterType="String" resultType="java.lang.Integer">
        SELECT COUNT(*) AS total FROM t where  col1= <![CDDATA[#{xxx}]]>
    </select>

示例二(原始JDBC方式)

不安全示例
  				String colValue = 'xxxx';
                String countSql = "select count(*) as total from t where col="+colValue;
            	connection = JdbcUtil.getConnection(Integer.parseInt(providerId), "url", "userName", "Password");
            	// prepareStatement() 方法进行预编译组装SQL 并不能完全阻止SQL注入
       		    ps = connection.prepareStatement(countSql);
              resultSet = ps.executeQuery();
              while (resultSet.next()) {
                total = resultSet.getLong("total");
              }       
安全示例
            String colValue = 'xxxx';
            String countSql = "select count(*) as total from t where col= ? ";
            connection = JdbcUtil.getConnection(Integer.parseInt(providerId), "url", "userName", "Password");
            ps = connection.prepareStatement(countSql);
           ps .setString(1, colValue );// 预编译后在进行赋值
           resultSet = ps.executeQuery();
           while (resultSet.next()) {
            total = resultSet.getLong("total");
          }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值