java安全编码指南之:表达式规则说明

简介

在java编写过程中,我们会使用到各种各样的表达式,在使用表达式的过程中,有哪些安全问题需要我们注意的呢?一起来看看吧。

注意表达式的返回值

我们在使用JDK库的时候,一定要注意认真的读一下JDK中方法的含义和它的返回值。

有些返回值可能表示这个操作是否成功,有的返回值可能是方法操作的结果。我们看两个常见的例子:

 public void deleteFileWrong(){
  File file= new File("/tmp/www.jb51.net.txt");
  file.delete();
  System.out.println("File delete success!");
 }

 public void deleteFileRight(){
  File file= new File("/tmp/www.jb51.net.txt");
  if(file.delete()){
   System.out.println("File delete success!");
  }
 }

先看一个文件删除的例子,delete方法是有返回值的,所以我们在调用delete方法之后,一定要判断一下返回值,看是否删除成功。

再看一个常见的String中字符替换的例子:

 public void stringReplaceWrong(){
  String url="www.jb51.net";
  url.replace("www","WWW");
  System.out.println("replaced url..."+url);
 }
 public void stringReplaceRight(){
  String url="www.jb51.net";
  url=url.replace("www","WWW");
  System.out.println("replaced url..."+url);
 }

我们要记住,String是不可变的,所以它的replace方法,会返回一个替换过后的String,但是原String是不变的,所以我们需要将返回值重新赋值。

注意避免NullPointerException

NullPointerException应该是最最常见的运行时异常了。怎么避免这个异常呢?

我们要做的就是在调用object的方法时候,一定要判断这个object是不是为空。

在JDK8之后,我们引入了Stream操作:

 public void streamWrong(Collection<Object> collection){
  collection.stream().filter(obj->obj.equals("www.jb51.net"));
 }

Stream操作的过程中,我们需要注意stream中的元素是否为空。

有时候,我们可能觉得已经判断是为空了,但是条件判断不准确,导致未知的异常,看下面这个例子:

 public int countWrong(Collection<Object> collection, Object object){
  int count=0;
  if(collection ==null){
   return count;
  }
  for(Object element: collection){
   if((element ==null && object== null)
    || element.equals(object)){
    count++;
   }
  }
  return count;
 }

这个例子是用来查找collection中到底有多少元素和object相同,如果两者都为空,也记为相同。

但是上面的例子有一个漏洞,它没有考虑element ==null 而 object !=null的情况,所以会导致NullPointerException的生成。

我们需要这样修改:

 public int countRight(Collection<Object> collection, Object object){
  int count=0;
  if(collection ==null){
   return count;
  }
  for(Object element: collection){
   if((element ==null && object== null)
     || (element !=null && element.equals(object))){
    count++;
   }
  }
  return count;
 }

数组相等的判断

如果我们需要比较两个数组是否相等,其实我们想比较的是两个数组中的元素是否相等。

我们知道数组是一个特殊的Object,那么数组对象也有一个equals方法,考虑下面的例子:

 public boolean compareWrong(){
  int[] array1 = new int[10];
  int[] array2 = new int[10];
  return array1.equals(array2);
 }

返回的结果是false,因为数组直接使用了Object中定义的equals方法,我们看下该方法的定义:

 public boolean equals(Object obj) {
  return (this == obj);
 }

可以看到,该方法比较的是两个地址是否相等。所以我们的到了false结果。

其实,我们可以使用Arrays.equals工具类中的方法来进行两个数组的比较:

 public boolean compareRight(){
  int[] array1 = new int[10];
  int[] array2 = new int[10];
  return Arrays.equals(array1, array2);
 }

基础类型的封装类间的比较

在java中,我们知道有一些基础类型像boolean, byte,char, short, int他们会有相对应的封装类型:Boolean,Byte,Character,Short,Integer等。

我们可以直接将基础类型的值赋值给封装类型,封装类型会自行进行转换。

考虑下面的例子:

  Boolean boolA=true;
  Boolean boolB=true;
  System.out.println(boolA==boolB);

结果是多少呢?

答案是true。为什么两个不同对象的比较会是true呢?

在回答这个问题之前,我们看一下字符串的比较:

  String stringA="www.jb51.net";
  String stringB="www.jb51.net";
  System.out.println(stringA==stringB);

这个我们大家应该都知道,因为String有一个字符串常量池,直接从字符串常量构建的String对象,其实是同一个对象。

同样的对于Boolean和Byte来说,如果直接从基础类值构建的话,也是同一个对象。

而对于Character来说,如果值的范围在\u0000 to \u007f,则属于同一个对象,如果超出了这个范围,则是不同的对象。

对于Integer和Short来说,如果值的范围在-128 and 127,则属于同一个对象,如果超出了这个范围,则是不同的对象。

再考虑下面的例子:

Boolean boolA=true;
Boolean boolC=new Boolean(true);
System.out.println(boolA==boolC);

输出的结果是false,因为boolC使用了new关键字,构建了一个新的对象。

集合中类型不匹配

现在java集合可以通过指定类型,从而只存储特定类型的对象。考虑下面的一个例子:

 public void typeMismatch(){
  HashSet<Short> shortSet= new HashSet<>();
  for(int i=0;i<10;i++){
   shortSet.add((short)i);
   shortSet.remove(i);
  }
  System.out.println(shortSet.size());
 }

上面代码我们定义了一个Short的集合,然后将0-9添加进去,接着我们又调用了remove方法把i从集合删除。

但是最后输出结果是10,表明我们并没有删除成功。为什么呢?

看下HashSet的remove方法:

 public boolean remove(Object o) {
  return map.remove(o)==PRESENT;
 }

remove方法的参数是Object,我们传入的i是int类型的,跟short不匹配,所以导致删除失败。

我们需要这样修改:

 public void typeMatch(){
  HashSet<Short> shortSet= new HashSet<>();
  for(int i=0;i<10;i++){
   shortSet.add((short)i);
   shortSet.remove((short)i);
  }
  System.out.println(shortSet.size());
 }

Asset的副作用

我们会使用Asset语句在代码中做调试使用,在使用的过程中需要注意Asset语句不要对系统的业务逻辑产生副作用,也就是说即使Asset语句不运行,也不会修改代码的业务逻辑。

看下面的例子:

 public void assetWrong(ArrayList<Integer> list){
  assert list.remove(0)>0;
 }

上的代码我们从list中删除第一个元素,并判断删除的元素是否大于0.

上面的代码如果assert语句不执行的话,会导致业务逻辑也不执行,所以需要修改成下面这样:

 public void assetRight(ArrayList<Integer> list){
  int result=list.remove(0);
  assert result>0;
 }

本文的例子:

learn-java-base-9-to-20/tree/master/security

补充知识:Slow HTTP Denial of Service Attack 漏洞解决

解决漏洞需要修改tomcat conf 下 server.xml 文件

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="2000" redirectPort="8443" URIEncoding="UTF-8"/> connectionTimeout="20"

以上这篇java安全编码指南之:表达式规则说明就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java开发中常用的 Websocket 技术参考

    1. 前言 Websocket是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议.WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据,当然也支持客户端发送数据到服务端.通常用来社交聊天.弹幕.多玩家游戏.协同编辑.股票基金实时报价.资讯自动更新等场景,那么今天就简单聊一下在 Java 开发中对Websocket的技术选型. 技术选型是结合自身业务选择最适合的技术方案,并不存在褒贬. 2. 常用的 Websocket 技术 2.1

  • Java 基于AQS实现一个同步器

    前面说了这个多,我们可以自己尝试实现一个同步器,我们可以简单的参考一下ReentrantLock这个类的实现方式,我们就简单的实现一个不可重入的独占锁吧! 一.简单分析ReentrantLock的结构 下图所示,直接实现了Lock这个接口,然后定义了一个内部类继承AQS,暂时不考虑公平锁和非公平锁,前面说AQS的时候说过,留有tryAcquire,tryRelease这两个方法在具体子类中根据实际情况实现的,可想而知这个内部类主要的是实现tryAcquire,tryRelease: 我们看看Lo

  • java安全编码指南之:Number操作详解

    简介 java中可以被称为Number的有byte,short,int,long,float,double和char,我们在使用这些Nubmer的过程中,需要注意些什么内容呢?一起来看看吧. Number的范围 每种Number类型都有它的范围,我们看下java中Number类型的范围: 考虑到我们最常用的int操作,虽然int的范围够大,但是如果我们在做一些int操作的时候还是可能超出int的范围. 超出了int范围会发送什么事情呢?看下面的例子: public void testIntege

  • java安全编码指南之:声明和初始化说明

    简介 在java对象和字段的初始化过程中会遇到哪些安全性问题呢?一起来看看吧. 初始化顺序 根据JLS(Java Language Specification)中的定义,class在初始化过程中,需要同时初始化class中定义的静态初始化程序和在该类中声明的静态字段(类变量)的初始化程序. 而对于static变量来说,如果static变量被定义为final并且它值是编译时常量值,那么该static变量将会被优先初始化. 那么使用了final static变量,是不是就没有初始化问题了呢? 我们来

  • java安全编码指南之:Mutability可变性详解

    简介 mutable(可变)和immutable(不可变)对象是我们在java程序编写的过程中经常会使用到的. 可变类型对象就是说,对象在创建之后,其内部的数据可能会被修改.所以它的安全性没有保证. 而不可变类型对象就是说,对象一旦创建之后,其内部的数据就不能够被修改,我们可以完全相信这个对象. 虽然mutable对象安全性不够,但是因为其可以被修改,所以会有效的减少对该对象的拷贝. 而immutable对象因为不可改变,所以尝试对该对象的修改都会导致对象的拷贝,从而生成新的对象. 我们最常使用

  • java安全编码指南之:表达式规则说明

    简介 在java编写过程中,我们会使用到各种各样的表达式,在使用表达式的过程中,有哪些安全问题需要我们注意的呢?一起来看看吧. 注意表达式的返回值 我们在使用JDK库的时候,一定要注意认真的读一下JDK中方法的含义和它的返回值. 有些返回值可能表示这个操作是否成功,有的返回值可能是方法操作的结果.我们看两个常见的例子: public void deleteFileWrong(){ File file= new File("/tmp/www.jb51.net.txt"); file.de

  • 详解java安全编码指南之可见性和原子性

    不可变对象的可见性 不可变对象就是初始化之后不能够被修改的对象,那么是不是类中引入了不可变对象,所有对不可变对象的修改都立马对所有线程可见呢? 实际上,不可变对象只能保证在多线程环境中,对象使用的安全性,并不能够保证对象的可见性. 先来讨论一下可变性,我们考虑下面的一个例子: public final class ImmutableObject { private final int age; public ImmutableObject(int age){ this.age=age; } }

  • java安全编码指南之:对象构建操作

    简介 程序员肯定是不缺对象的,因为随时都可以构建一个,对象多了肯定会出现点安全问题,一起来看看在java的对象构建中怎么保证对象的安全性吧. 构造函数的异常 考虑下面的一个例子: public class SensitiveOperation { public SensitiveOperation(){ if(!doSecurityCheck()){ throw new SecurityException("Security check failed!"); } } //Securit

  • 浅谈java安全编码指南之堆污染

    产生堆污染的例子 有同学可能会问了,既然JDK5引入了泛型,为什么还会出现堆污染呢? 这是一个好问题,让我们看一个例子: public void heapPollution1(){ List normalList= Arrays.asList("www.flydean.com",100); List<Integer> integerList= normalList; } 上面的例子中,我们使用Arrays.asList创建了一个普通的List. 这个List中包含了int和

  • 浅谈java安全编码指南之死锁dead lock

    不同的加锁顺序 我们来看一个不同加锁顺序的例子: public class DiffLockOrder { private int amount; public DiffLockOrder(int amount){ this.amount=amount; } public void transfer(DiffLockOrder target,int transferAmount){ synchronized (this){ synchronized (target){ if(amount< tr

  • Java字符编码简介_动力节点Java学院整理

    1. 概述 本文主要包括以下几个方面:编码基本知识,Java,系统软件,url,工具软件等. 在下面的描述中,将以"中文"两个字为例,经查表可以知道其GB2312编码是"d6d0 cec4",Unicode编码为"4e2d 6587",UTF编码就是"e4b8ad e69687".注意,这两个字没有iso8859-1编码,但可以用iso8859-1编码来"表示". 2. 编码基本知识 最早的编码是iso88

  • 详解Java函数式编程和lambda表达式

    为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论.函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做.说白了,函数式编程是基于某种语法或调用API去进行编程.例如,我们现在需要从一组数字中,找出最小的那个数字,若使用用命令式编程实现这个需求的话,那么所编写的代码如下: public static void main(String[] args) { int[] nums = new int[]{1, 2, 3, 4, 5,

随机推荐