当前位置:   article > 正文

阿里巴巴规约扫描、奇安信扫描bug修改建议_硬编码文件分隔符漏洞

硬编码文件分隔符漏洞

目录

命名规范和注释

1.Exceptional return value of java.io.File.delete() ignored

2.Result of integer multiplication cast to long

3.Return value of Throwable.getMessage() ignored, but method has no side effect

4.Dead store to local variable

5.Redundant nullcheck of lineB, which is known to be non-null

6.Exception is caught when Exception is not thrown

7.循环体内,字符串的联接方式,使用StringBuilder的append方法进行扩展。

8.关于基本数据类型与包装数据类型的使用标准如下:

9.Found reliance on default encoding: String.getBytes()(发现对默认编码的依赖:String.getBytes())

10.Map遍历时候entryset比keyset效率高

11.未使用的局部变量

12.May expose internal representation by returning reference to mutable object

13.Possible null pointer dereference of List

14.使用浮点数进行精确计算

15.解析Double类型数据的漏洞

16.路径遍历

17.比较Locale相关的数据未指定适当的Locale

18.使用==或!=比较基本数据类型的包装类

19.日志伪造

20.配置文件中的明文密码

21,资源未释放:流

22.敏感信息泄漏

23.XSS漏洞

24.直接使用线程类

25.系统信息泄露:标准错误流

26.拒绝服务:StringBuilder或者StringBuffer*

27.密码管理:硬编码加密密钥

28.在JAVA EE程序中直接使用Socket进行通信容易出错。

29.二级类:有风险的资源使用

30.一级类:代码质量 二级类:使用equals()来判断字符串是否为空

31.硬编码文件分隔符

32.有风险的SQL查询:MyBatis

33.byte数组转String时未指定编码

34.非同步方法覆盖同步方法

35.Optional没有在调用isPresent()后访问值

36.http响应

37.服务器端请求伪造

38.过于宽松的CORS策略

39.请使用System.currentTimeMillis()代替new Date().getTime()

40.事务场景中,抛出异常被catch后,如果需要回滚,一定要手动回滚事务。

41.使用DNS名称作为安全性的依据

42.输入验证:重定向

43.代码注入:HTTP响应截断


命名规范和注释

类、类属性、类方法的注释必须使用javadoc规范,使用/*内容/格式,不得使用//xxx方式和/xxx/方式

,除常用方法(如getXxx/isXxx)等外,不要在条件判断中执行复杂的语句,

方法内部单行注释,在被注释语句上方另起一行,所有的抽象方法(包括接口中的方法)必须要用javadoc注释、除了返回值、参数、异常说明外

1.Exceptional return value of java.io.File.delete() ignored

问题原因:java.io.File.delete()返回值未接受

规范写法:接受返回值并判断是否操作成功

2.Result of integer multiplication cast to long

问题原因:上面那行代码存在溢出风险在java中,int * int的结果还是int类型,不会自动转换为long类型,而long * int的结果是long类型,结果不会溢出

规范写法:将int * int换成long * int结果不会溢出

long size = quotaSize * 1024 * 1024;
换成
long size = quotaSize * 1024L * 1024;

3.Return value of Throwable.getMessage() ignored, but method has no side effect

问题原因:抛异常不合规范

规范写法:改为logger.error

4.Dead store to local variable

问题原因:局部变量的死存储,调用的方法有返回值并接受但是没有使用

规范写法:改为只调用方法不接受返回值

5.Redundant nullcheck of lineB, which is known to be non-null

问题原因:行C的冗余空检查,已知为非空

规范写法:多余的空判断去掉

 

数据库查询返回的list和set时查询结果为空也会不返回null
所以无需进行list != null 或 set != null 判断

6.Exception is caught when Exception is not thrown

问题原因:try{}catch(){}代码块中没有需要catch的Exception 类

规范写法:根据需要将代码的逻辑中不需要的try{}catch(){}去掉

7.循环体内,字符串的联接方式,使用StringBuilder的append方法进行扩展。

原因:下面代码,反编译出得字节码文件显示每次循环都会new出一个StringBuilder对象,然后进行append操作最后通过toString()方法返回String对象,造成资源的浪费

String str = "string"
for(int i = 0; i < 10; i++){
    str = str + "string";
}
//反编译之后大致这样子
String str = "string"
for(int i = 0; i < 10; i++){
    str = (new StringBuilder()).append(str).append("string").toString();
}
//string对象不可变,这里是在不断地创建StringBuilder对象,然后又调用tostring方法转成string对象。也就是在StringBuilder和string当中不断转化,这个过程是一个消耗。主要是当循环次数比较多的时候,就会在内存中创建很多StringBuilder对象,虽然会自动gc,但是这也增加了GC次数,然后就会有stop the world。减少吞吐量,增加了延时。

规范写法:使用StringBuild的append拼接字符串

String str = "string"
StringBuilder strb = new StringBuilder(str);
for(int i = 0; i < 10; i++){
    strb.append("string");
}
str = strb.toString();

8.关于基本数据类型与包装数据类型的使用标准如下:

 1) 所有的POJO类属性必须使用包装数据类型。  2) RPC方法的返回值和参数必须使用包装数据类型。

原因:POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何 NPE 问题,或者入库检查,都由使用者来保证。数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。

 3) 所有的局部变量推荐使用基本数据类型。

原因:Java中共有三种变量,分别是类变量、成员变量和局部变量。他们分别存放在JVM的方法区、堆内存和栈内存中。 其中局部变量的作用域只在方法内部。那么,既然是一个作用域只在方法内的变量,不需要考虑那么多,怎么性能高就怎么定义。

在Java语言中,new一个对象是存储在堆里的,我们通过栈中的引用来使用这些对象。所以,对象本身来说是比较消耗资源的。

对于经常用到的类型,如int等,如果我们每次使用这种变量的时候都需要new一个Java对象的话,就会比较笨重。而基本数据类型的变量不需要使用new创建,它们不会在堆上创建,而是直接在栈内存中存储,因此会更加高效。

9.Found reliance on default encoding: String.getBytes()(发现对默认编码的依赖:String.getBytes())

原因:对默认编码依赖会因为对系统的默认编码不可预期而出现问题

改正:避免出现问题最好还是还是将默认编码指定好,如

String.getBytes("GBK");

10.Map遍历时候entryset比keyset效率高

原因:因为使用keySet遍历时,当使用key值取得Map中的Value时,Map又遍历了一遍。而EntrySet时,由于EntrySet本身就是key-value结构,所以直接将Map的key-value给取出来了,自然速度就要快上一些,使用迭代器会更快一些。

改正:使用entryset替换keyset

//keyset使用for循环遍历
for(String key : map.keySet()){
    map.get(key);
}
//keyset使用迭代器遍历
Iterator<String> ito = mapInfo.keySet().iterator();
while(ito.hasNext()){
    String key = it,next();
    map.get(key);
}
//entryset替换后的for循环遍历
for(Map.Entry<String, String> entry : map.entrySet()){
    entry.getValue();
}
//entryset替换后的迭代器遍历
Iterator<Map.Entry<String, String>> ito = map.entrySet();
while(ito.hasNext()){
    Map.Entry<String, String> entry = ito.next();
    entry.getValue();
}

11.未使用的局部变量

原因:局部遍历未使用,资源浪费

改正:删除

public int test(){
    //变量a未使用对功能无影响可以直接删除
    int a = 1;
    int b = 3, c= 2;
    return b + c;
}

12.May expose internal representation by returning reference to mutable object

原因:返回对存储在对象字段中的可变对象值的引用,将公开对象的内部表示形式。如果实例由不受信任的代码访问,并且对可变对象的未经检查的更改将损害安全性或其他重要属性,则需要采取不同的措施。在许多情况下,返回对象的新副本是更好的方法。(引用类型的可变对象都会有这种风险)

改正:方法如下

//修改前的实体类(lombok的@Data也是这种get和set的写法)
public class clonecc {
    private Date date;
    private List list;
    public clonecc(Date date, List list) {
        this.date = date;
        this.list = list;
    }
    public List getList() {
        return list;
    }
​
    public void setList(List list) {
        this.list = list;
    }
​
    public Date getDate() {
        return date;
    }
​
    public void setDate(Date date) {
        this.date = date;
    }
​
}
//修改前的测试类
public class testr {
    public static void main(String[] args) throws Exception {
        Date date = new Date();
        String string = "aaaa";
        ArrayList<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        clonecc clonecc = new clonecc(date,list,string);
        clonecc.setDate(date);
        System.out.println(clonecc.getDate());
        date.setTime(6);
        System.out.println(clonecc.getDate());
        clonecc.setList(list);
        System.out.println(clonecc.getList());
        list.add("3");
        System.out.println(clonecc.getList());
​
    }
}
结果:修改list和date影响实体类中属性的值了在代码中是存在风险的
Thu Dec 08 17:34:22 CST 2022
Thu Jan 01 08:00:00 CST 1970
[1, 2]
[1, 2, 3]
//修改后的实体类
public class clonecc {
    private Date date;
    private List list;
    public clonecc(Date date, List list) {
        if(date != null) {
            this.date = (Date) date.clone();
        }else {
            this.date = null;
        }
        this.list = list;
    }
    public List getList() {
        return list;
    }
​
    public void setList(List list) {
        this.list = list;
    }
​
    public Date getDate() {
        if(this.date != null) {
            return (Date) date.clone();
        }else {
            return null;
        }
​
    }
​
    public void setDate(Date date) {
        if(date != null) {
            this.date = (Date) date.clone();
        }else {
            this.date = null;
        }
    }
//修改后的测试方法
public class testr {
    public static void main(String[] args) throws Exception {
        Date date = new Date();
        String string = "aaaa";
        ArrayList<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        clonecc clonecc = new clonecc(date,list,string);
        clonecc.setDate(date);
        System.out.println(clonecc.getDate());
        date.setTime(6);
        System.out.println(clonecc.getDate());
        clonecc.setList((List) list.clone());
        System.out.println(clonecc.getList());
        list.add("3");
        System.out.println(clonecc.getList());
​
    }
}
结果:修改list和date没有影响实体类的属性
Thu Dec 08 17:28:54 CST 2022
Thu Dec 08 17:28:54 CST 2022
[1, 2]
[1, 2]

13.Possible null pointer dereference of List

问题原因:在编程中使用方法返回的list或者声明的list进行流处理等操作时要对其判空处理否则会报NEP异常(MyBatis返回的list除外,因为其未查到数据也会返回一个ArrayList)

改正:对有返回为空的能的list使用前进行空判断

public static void main(String[] args) throws Exception {
        List list1 = lists();
        list1.stream();
    }
public static List lists (){
        return null;
    }
结果:Exception in thread "main" java.lang.NullPointerException

14.使用浮点数进行精确计算

问题原因:java中浮点数采用IEEE 754标准,在进行精度计算中使用浮点类型会导致精度缺失从而产生不正确的数值

改正:避免使用浮点数进行精确计算,可以考虑采用整数类型或者用于精确表达小数的BifDecimal替代

//使用细节
//double传值换成String,因为传入0.12后由于精度原因实际值和0.12会有偏差,使用String可以让传值和实际值一样
BigDecimal num1=new BigDecimal("0.12");
/*
使用除法divide除不尽会报错,通过指定小数位数和四舍五入规则解决。
传入第二个参数,指定小数位个数,并传入第三个参数,指定四舍五入规则。
*/
BigDecimal a=new BigDecimal("17");
BigDecimal b=new BigDecimal("8.30");        
System.out.println(a.divide(b,2,BigDecimal.ROUND_HALF_UP));
//使用演示
public static void main(String[] args) throws Exception {
        double doub1 = 21.3d;
        double doub2 = 3.0d;
        // sum = doub1 * doub2 / 7;
        double sum = new BigDecimal(String.valueOf(doub1)).multiply(new BigDecimal(String.valueOf(doub2))).divide(new BigDecimal("7"), 5, ROUND_HALF_UP).doubleValue();
        System.out.println(sum);
    }
结果:9.12857;

15.解析Double类型数据的漏洞

问题原因:在实施 java.lang.Double.parseDouble() 及相关方法时出现漏洞,会导致在解析 [2^(-1022) - 2^(-1075) : 2^(-1022) - 2^(-1076)] 范围内的任意数字时挂起线程。此缺陷可被攻击者用于执行拒绝服务 (DoS) 攻击。攻击者可以发送参数值位于该范围(例如 0.0222507385850720119e-00306)内的请求,致使程序在处理该请求时被挂起。

改正:升级JDK至1.6 Update 24 以上,若JDK已经是1.6 Update 24以上,该类型漏洞可以关闭,项目jdk已经是1.8漏洞可以关闭

16.路径遍历

问题原因:应用程序对用户可控制的输入未经合理校验,就传送给一个文件API。攻击者可能会使用一些特殊的字符(如../)摆脱受保护的限制,访问一些受保护的文件或目录。 例如:用户可以通过输入特殊字符(…/file.text)获取系统资源,获取resources下的file.test文件

改正:对文件名进行过滤

# 引入文件名过滤/处理方法
import org.apache.commons.io.FilenameUtils;
​
@GET
@Path("/images/{image}")
@Produces("images/*")
public Response getImage(@javax.ws.rs.PathParam("image") String image, MultipartFile file) {
    # 读取文件之前,对文件名进行过滤处理
    File fileTest = new File("resources/images/", FilenameUtils.getName(FilenameUtils.getName())); 
    String fileName = FilenameUtils.getName(file.getOriginalFilename(image));
​
    if (!fileTest.exists()) {
        return Response.status(Status.NOT_FOUND).build();
    }
​
    return Response.ok().entity(new FileInputStream(fileTest)).build();
}
//或者对文件名字过路径判断是否存在..
if(image.indexOf("..") == -1)

17.比较Locale相关的数据未指定适当的Locale

原因:不指定locale会导致相同的String转化结果不一致如将字符转换为大写字符。在英文Locale中,会将title转换为TITLE;在土耳其Locale中,会将title转换为T?TLE,其中的?是拉丁字母的I

更正:指定locale

"locale".toUpperCase(Locale.ENGLISH)

18.使用==或!=比较基本数据类型的包装类

问题原因:不能直接使用==!=操作符来比较的两个基本数据类型的包装类型的值,因为这些操作符比较的是对象的引用而不是对象的值。

更正:由于Java的缓存机制,所以如果基本类型的包装类是一个整数且在-128和127之间,或是布尔类型true或false,或者是'\u0000'和'\u007f'之间的字符文本,可以使用==!=进行比较。也就是说,如果使用了基本类型的包装类型(除去Boolean或Byte),则会缓存或记住一个值区间。对于值区间内的值,使用==!=会返回正确的值,而对于值区间外的值,将返回对象地址的比较结果。

19.日志伪造

问题原因:允许日志记录未经验证的用户输入,会导致日志伪造攻击。一般日志伪造出现的原因是,日志文件中存在一些修改linux系统的敏感操作的指令。这些指令一般都需要另起一行,所以一般包含换行符。所以对输出日志进行编码,即可防止此种情况发生。

20.配置文件中的明文密码

问题原因:配置文件中采用明文存储密码,所有能够访问该文件的人都能访问该密码,将会降低系统安全性。

更正:使用jasypt加密工具将配置文件中的敏感信息加密。

<dependency> 
    <groupId>org.jasypt</groupId> 
    <artifactId>jasypt</artifactId>
    <version>需要依赖的版本</version> 
</dependency>

21,资源未释放:流

问题原因:程序创建或分配流资源后,不进行合理释放,将会降低系统性能。攻击者可能会通过耗尽资源池的方式发起拒绝服务攻击。

//jdk1.8之后提供,try(){}可以自动关闭资源,防止忘记关闭资源,或者使用常规的try{}catch(){}finally{关闭流} 
try (InputStream inputStream = new FileInputStream("file")){
 
}catch(){ 
 
}

22.敏感信息泄漏

问题原因:常量的参数名尽量不要使用username、password、pwd、addressee、name等敏感字段,会被漏扫工具监测到敏感信息泄漏。如果存在的话,需要对字段的值进行加密,在使用的时候进行解密。

23.XSS漏洞

问题原因:

XSS跨站脚本攻击,指的是恶意攻击者往Web页面里插入恶意html代码。分为反射型XSS和存储型XSS。

实质的区别是反射型的风险来源是web请求的数据,存储型是数据库查出来的数据包含风险。

由此看来,如果从每个接口的入参出手,对每个参数进行校验、过滤、编码,那么就能从实质上解决反射型和存储型数据所附带的XSS风险操作。

实际拦截器并不能完全防止XSS,因为存储型的XSS数据中存储的数据不仅仅通过web接口增加,所以其实想要完全避免存储型的漏洞,还需要对接口响应数据进行过滤。可以通过实现ResponseBodyAdvice<T>接口实现。

更正:

在过滤器中,进行如下操作:

对特殊字符(如<、>、'、"以及<script>、javascript等进行过滤。

根据数据将要置于HTML上下文中的不同位置(HTML标签、HTML属性、JavaScript脚本、CSS、URL),对所有不可信数据进行恰当的输出编码。

设置HttpOnly属性,避免攻击者利用跨站脚本漏洞进行Cookie劫持攻击.

具体实现原理:

重写HttpServletRequestWrapper,重写getParameter(),getParameterValues(),getHeader(),getHeaders()在这几个方法中,对参数进行过滤。

创建过滤器覆盖原来的HttpServletRequest

Response设置HttpOnly属性,避免攻击者利用跨站脚本漏洞进行Cookie劫持攻击

/**
 * 过滤器
 */
@Slf4j
public class HttpRequestFilter implements Filter {
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
 
    }
 
    @SneakyThrows
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
 
        response.setHeader("Set-Cookie","cookiename=cookievalue; path=/; Domain=domainvaule; Max-age=seconds; HttpOnly");
        ParamHttpServletRequestWrapper request = new ParamHttpServletRequestWrapper(httpServletRequest);
 
        chain.doFilter(request, response);
    }
 
    @Override
    public void destroy() {
 
    }
}
/**
 * HttpServletRequestWrapper装饰类
 */
@Slf4j
public class ParamHttpServletRequestWrapper extends HttpServletRequestWrapper {
    HttpServletRequest orgRequest = null;
    private String currentUrl;
    private Map<String, String[]> parameterMap;
    private Map<String, String> headerMap;
 
    public ParamHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        orgRequest = request;
        this.setCurrentUrl(request.getRequestURI());
        parameterMap = request.getParameterMap();
        headerMap = new HashMap<>();
    }
 
    private void setCurrentUrl(String url){
        this.currentUrl = url;
    }
 
    public String getCurrentUrl(){
        return this.currentUrl;
    }
 
    /**
     * 获取原始的request
     * @return
     */
    public HttpServletRequest getOrgRequest() {
        return orgRequest;
    }
 
    /**
     * 获取最原始的request的静态方法
     * @return
     */
    public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
        if (request instanceof ParamHttpServletRequestWrapper) {
            return ((ParamHttpServletRequestWrapper) request).getOrgRequest();
        }
        return request;
    }
 
    /**
     * 获取所有参数名
     * @return 返回所有参数名
     */
    @Override
    public Enumeration<String> getParameterNames() {
        Vector<String> vector = new Vector<>(parameterMap.keySet());
        return vector.elements();
    }
 
 
    /**
     * 覆盖getParameter方法,将参数名和参数值都做xss & sql过滤。<br/>
     * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/>
     * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
     */
    @Override
    public String getParameter(String name) {
        String[] results = parameterMap.get(name);
        if (results == null || results.length <= 0)
            return null;
        else {
            String value = results[0];
            return XssEncodeUtil.stripXSS(value);
        }
    }
 
    /**
     * 获取指定参数名的所有值的数组,如:checkbox的所有数据 接收数组变量 ,如checkobx类型
     */
    @Override
    public String[] getParameterValues(String name) {
        String[] results = parameterMap.get(name);
        if (results == null || results.length <= 0)
            return null;
        else {
            int length = results.length;
            for (int i = 0; i < length; i++) {
                results[i] = XssEncodeUtil.stripXSS(results[i]);
            }
            return results;
        }
    }
 
    public void addHeader(String name, String value) {
        this.headerMap.put(name, value);
    }
 
    /**
     * 覆盖getHeader方法,将参数名和参数值都做xss & sql过滤。<br/>
     * 如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/>
     * getHeaderNames 也可能需要覆盖
     */
    @Override
    public String getHeader(String name) {
        String headerValue = headerMap.get(name);
        if (headerValue != null) {
            return XssEncodeUtil.stripXSS(headerValue);
        }
        return XssEncodeUtil.stripXSS(super.getHeader(name));
    }
 
    @Override
    public Enumeration<String> getHeaders(String name) {
        ArrayList<String> values = Collections.list(super.getHeaders(name));
        if (headerMap.containsKey(name)) {
            values.add(headerMap.get(name));
        }
        return Collections.enumeration(values);
    }
 
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
 
    @Override
    public ServletInputStream getInputStream() throws IOException {
        return super.getInputStream();
    }
 
 
 
}
/**
 * ESAPI工具类,防止xss,SQL注入等漏洞
 */
public class XssEncodeUtil {
 
    public static String encodeForHTML(String str){
        return StringEscapeUtils.escapeHtml(str);
    }
 
    public static String encodeForJavaScript(String str){
        return StringEscapeUtils.escapeJavaScript(str);
    }
 
    public static String encodeForSQLParam(String param){
        return StringEscapeUtils.escapeSql(param);
    }
 
    /**
     * 防止xss跨脚本攻击(ESAPI编码)
     */
    public static String stripXSS(String value) {
        if (StringUtils.isNotEmpty(value)) {
            Pattern scriptPattern = Pattern.compile(
                    "<[\r\n| | ]*script[\r\n| | ]*>(.*?)</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
            if(scriptPattern.matcher(value).find()){
                return encodeForHTML(value);
            }
            scriptPattern = Pattern.compile("src[\r\n| | ]*=[\r\n| | ]*[\\\"|\\\'](.*?)[\\\"|\\\']",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            if(scriptPattern.matcher(value).find()){
                return encodeForHTML(value);
            }
            // Remove any lonesome </script> tag
            scriptPattern = Pattern.compile("</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
            if(scriptPattern.matcher(value).find()){
                return encodeForHTML(value);
            }
            // Remove any lonesome <script ...> tag
            scriptPattern = Pattern.compile("<[\r\n| | ]*script(.*?)>",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            if(scriptPattern.matcher(value).find()){
                return encodeForHTML(value);
            }
            // Avoid eval(...) expressions
            scriptPattern = Pattern.compile("eval\\((.*?)\\)",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            if(scriptPattern.matcher(value).find()){
                return encodeForHTML(value);
            }
            // Avoid e-xpression(...) expressions
            scriptPattern = Pattern.compile("e-xpression\\((.*?)\\)",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            if(scriptPattern.matcher(value).find()){
                return encodeForHTML(value);
            }
            // Avoid javascript:... expressions
            scriptPattern = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
            if(scriptPattern.matcher(value).find()){
                return encodeForJavaScript(value);
            }
            // Avoid vbscript:... expressions
            scriptPattern = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
            if(scriptPattern.matcher(value).find()){
                return encodeForJavaScript(value);
            }
            // Avoid οnlοad= expressions
            scriptPattern = Pattern.compile("onload(.*?)=",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            if(scriptPattern.matcher(value).find()){
                return encodeForHTML(value);
            }
 
        }
        return value;
    }
​
}

24.直接使用线程类

原因:JAVA EE标准禁止在某些环境下使用Web应用程序中的线程管理,因为此时使用线程管理非常容易出错。线程管理起来很困难,可能还会以不可预知的方式干扰应用程序容器。即使容器没有受到干扰,线程管理通常还会导致各种难以发现的错误,如死锁、竞争条件及其他同步错误等。

更正:使用线程池

//使用项目里公用的线程池
    @Autowired
    @Qualifier("taskExecutor")
    private AsyncTaskExecutor taskExecutor;
    taskExecutor.submit(thread);
    //或者
    taskExecutor.submit(()->{
        run方法内容
    });

25.系统信息泄露:标准错误流

问题原因:通过系统输出流(标准错误流)打印或日志功能将系统数据或调试信息输出到本地文 件或屏幕时,在一些类似Eclipse的程序,为了让错误信息更加显眼,会将错误信息以红色 文本的形式通过System.err输出到控制台上,更容易发生内部信息泄露。例如

try{
 ...
}catch(Exception e){
 e.printStackTrace();
}

更正:

    try{
        ...
        }catch (NoSuchAlgorithmException){
            logger.error("NoSuchAlgorithmException ===" ,e.getMessage());
        }

26.拒绝服务:StringBuilder或者StringBuffer*

问题原因:当StringBuilder或者StringBuffer使用append添加数据时,如果StringBuilder或者StringBuffer对象内部数组不足以存入所有数据时,会重新调整调整基础数组的大小来存放新的数据。而旧数组将继续留在堆中,一直等待系统回收。当旧数组过多时,可能导致堆栈溢出,从而拒绝服务。

更正: 对于外界获取的数据进行内容过滤和长度校验,有效的使用JVM堆内存空间,防止资源浪费或溢出。

27.密码管理:硬编码加密密钥

问题原因:常量属性名避免带key,使用的话最好将值进行加密。

28.在JAVA EE程序中直接使用Socket进行通信容易出错。

问题原因:

在JAVA EE程序中直接使用Socket进行通信容易出错。

只有在与比较陈旧的系统进行通信时,JAVA EE标准才允许使用Socket,因为那时没有较高级别的协议可用。

自己编写通信协议将需要考虑许多安全上的问题,其中包括:输入信号与输出信号的比较、协议版本间的兼容性、通道安全、错误处理、网络限制、会话管理。

因此,自定义的通信协议将面临诸多安全隐患。

在自定义标准协议的应用过程中,也会碰到许多同样的问题。通常有很多可用的资源都可以解决与标准协议相关的各种安全问题,然而,攻击者同样也能获取这些源。

更正: 建议使用一些框架提供的方法代替直接使用Socket。

29.二级类:有风险的资源使用

问题原因:未对传输文件进行大小校验

    //允许上传的文件最大大小(100M,单位为byte)
    int maxSize = 1024*1024*100;
    //判断文件大小是否超限
        if (item.getSize() > maxSize)
        {
            item.delete();
            JOptionPane.showMessageDialog(null, "文件大小超过限制!应小于" + maxSize
                                                / 1024 / 1024 + "M");
            return "文件大小超过限制!应小于" + maxSize;
        }

30.一级类:代码质量 二级类:使用equals()来判断字符串是否为空

问题原因:使用string.length()!=0效率比"".equals()高

更正:根据代码逻辑使得修改为string.length()!=0

31.硬编码文件分隔符

问题原因:代码中分隔符写死了,在win系统中/或者\都可以但是在linux中识别不了\

更改:而 File.separator 是系统默认的文件分隔符号,在 UNIX 系统上,此字段的值为 ' / '在 Microsoft Windows 系统上,它为 ' \ ' 屏蔽了这些系统的区别。所以用 File.separator 保证了在任何系统下不会出错。

File file = new File(dirName + "\\" + fileName);
//换成
File file = new File(dirName + File.separator + fileName);

32.有风险的SQL查询:MyBatis

问题原因:在排序的时候使用$传递了排序字段

更正:采用#,对于排序字段采用<if> 的方法进行判断

33.byte数组转String时未指定编码

问题原因:String是字符集byte数组存的的是字节,字符到字节,是一个编码的过程; 字节到字符,是一个解码的过程; 同样的一个字符,在不同的字符集和编码方式下,实际存储的值,将是不同的; 比如前面说的Unicode字符集,UTF8 和UTF16编码后的数据是不同的。 这个编码后的数据,也就是字节,他们是不一样的 。 同样的一个编码值,在不同的字符集中,可能代表着不同的字符。 所以字符与字节之间,必然有编码参与其中。 这个编码环节是必然存在的,否则,你就没办法把字节与字符联系起来。

因此如果在字符和字节之间转化未指定编码后会导致转化结果出错

更正:转化时指定编码

    public static void main(String[] args) throws Exception {
        byte[] bytes = "春".getBytes("GBK");
        String s = new String(bytes);
        System.out.println("未指定编码使用默认编码转换=====   "+ s);
        String s1 = new String(bytes, "GBK");
        System.out.println("指定编码=====      "+s1);
    }
结果:
    未指定编码使用默认编码转换=====   ��
指定编码=====      春

34.非同步方法覆盖同步方法

问题原因:

更正:

35.Optional没有在调用isPresent()后访问值

问题原因:

更正:

36.http响应

问题原因:HTTP响应截断是由于应用程序未对用户提交的数据进行严格过滤,当用户恶意提交包含 CR(回车,即URL编码%0d或r)和 LF(换行符,即URL编码%0a或n)的HTTP请求,服务器可能会创建两个 HTTP 响应,攻击者可以控制第二个响应并加载攻击。攻击者可控制响应的内容构造 XSS 攻击,其中响应中包含恶意的 JavaScript 或其它代码在用户的浏览器中执行,也有可能让用户重定向到攻击者控制的Web内容或在用户的主机上执行恶意操作。

37.服务器端请求伪造

问题原因:很多Web应用提供了从其他的服务器上获取数据的功能,例如用户指定URL让Web应用加载图片,下载文件等。如果恶意利用这个功能,可以让存在缺陷的Web应用作为代理攻击远程和本地的服务器。这种形式的攻击称为服务端请求伪造攻击(Server-side Request Forgery, SSRF)。攻击者利用SSRF可以实现的攻击主要有5种:

  1. 可以对外网、服务器所在内网、本地进行端口扫描,获取一些服务器的banner信息;

  2. 攻击运行在内网或本地的应用程序(比如溢出);

  3. 对内网web应用进行指纹识别,通过访问默认文件实现;

  4. 攻击内外网的web应用,主要是使用get参数就可以实现的攻击(比如struts2,sqli等);

  5. 利用file协议读取本地文件等。

更正:1. 过滤返回信息,验证远程服务器对请求的响应是比较容易的方法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。

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

  2. 限制请求的端口为http常用的端口,比如80,443,8080,8090。

  3. 禁用不需要的协议。仅仅允许http和https请求。可以防止类似于file:///, gopher:// , ftp:// 等引起的问题。

  4. 过滤内网ip,限制访问内网

38.过于宽松的CORS策略

问题原因:

更正:

39.请使用System.currentTimeMillis()代替new Date().getTime()

问题原因:Date和System.currentTimeMillis()都是获得一个13位数字,Date类中有很多方法处理时间,如果你只需要时间戳的话可以直接调用System.currentTimeMillis(),因为Date在初始化也是调用System.currentTimeMillis(),System.currentTimeMillis()也是直接调用本地方法,使用new Date().getTime()还需要创建对象效率低浪费空间

更正:在获得时间戳时使用使用System.currentTimeMillis()代替new Date().getTime()

40.事务场景中,抛出异常被catch后,如果需要回滚,一定要手动回滚事务。

问题原因:Spring使用声明式事务处理,默认情况下,如果被注解的数据库操作方法中发生了unchecked异常,所有的数据库操作将rollback;如果发生的异常是checked异常,默认情况下数据库操作还是会提交的。

checked异常:表示无效,不是程序中可以预测的。比如无效的用户输入,文件不存在,网络或者数据库链接错误。这些都是外在的原因,都不是程序内部可以控制的。

必须在代码中显式地处理。比如try-catch块处理,或者给所在的方法加上throws说明,将异常抛到调用栈的上一层。

更正:@Transactional(rollbackFor = Exception.class)

41.使用DNS名称作为安全性的依据

问题原因:程序中采用DNS名称进行安全认证,但DNS名称是容易被攻击者进行欺骗的

更正:不要依赖DNS名称来做安全认证

42.输入验证:重定向

问题原因:应用程序允许未验证的用户输入控制重定向中的URL,攻击通过构建URL,使用户重定向到任意URL,利用这个漏洞可以诱使用户访问某个页面,挂马、密码记录、下载任意文件等,常被用来钓鱼。

更正:防止重定向漏洞的方法是创建一份合法URL列表,用户只能从中进行选择,进行重定向操作。后端url在配置文件中配置的,是固定的

43.代码注入:HTTP响应截断

问题原因:程序从一个不可信赖的数据源获取数据,未进行验证就置于HTTP头文件中发给用户,可能会导致HTTP响应截断攻击。

更正:防止HTTP响应截断攻击的最安全的方法是创建一份安全字符白名单,只接受完全由这些受认可的字符组成的输入出现在HTTP响应头文件中。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/150605
推荐阅读
相关标签
  

闽ICP备14008679号