java安全编码指南之:输入校验

简介: java安全编码指南之:输入校验

目录



简介



为了保证java程序的安全,任何外部用户的输入我们都认为是可能有恶意攻击意图,我们需要对所有的用户输入都进行一定程度的校验。


本文将带领大家探讨一下用户输入校验的一些场景。一起来看看吧。


在字符串标准化之后进行校验


通常我们在进行字符串校验的时候需要对一些特殊字符进行过滤,过滤之后再进行字符串的校验。


我们知道在java中字符是基于Unicode进行编码的。但是在Unicode中,同一个字符可能有不同的表示形式。所以我们需要对字符进行标准化。


java中有一个专门的类Normalizer来负责处理,字符标准化的问题。


我们看下面一个例子:


public void testNormalizer(){
        System.out.println(Normalizer.normalize("\u00C1", Normalizer.Form.NFKC));
        System.out.println(Normalizer.normalize("\u0041\u0301", Normalizer.Form.NFKC));
    }


输出结果:


Á
Á


我们可以看到,虽然两者的Unicode不一样,但是最终表示的字符是一样的。所以我们在进行字符验证的时候,一定要先进行normalize处理。


考虑下面的例子:


public void falseNormalize(){
        String s = "\uFE64" + "script" + "\uFE65";
        Pattern pattern = Pattern.compile("[<>]"); // 检查是否有尖括号
        Matcher matcher = pattern.matcher(s);
        if (matcher.find()) {
            throw new IllegalStateException();
        }
        s = Normalizer.normalize(s, Normalizer.Form.NFKC);
    }


其中\uFE64表示的是<,而\uFE65表示的是>,程序的本意是判断输入的字符串是否包含了尖括号,但是因为直接传入的是unicode字符,所以直接compile是检测不到的。


我们需要对代码进行下面的改动:


public void trueNormalize(){
        String s = "\uFE64" + "script" + "\uFE65";
        s = Normalizer.normalize(s, Normalizer.Form.NFKC);
        Pattern pattern = Pattern.compile("[<>]"); // 检查是否有尖括号
        Matcher matcher = pattern.matcher(s);
        if (matcher.find()) {
            throw new IllegalStateException();
        }
    }


先进行normalize操作,然后再进行字符验证。


注意不可信字符串的格式化


我们经常会使用到格式化来对字符串进行格式化,在格式化的时候如果格式化字符串里面带有用户输入信息,那么我们就要注意了。


看下面的例子:


public void wrongFormat(){
        Calendar c = new GregorianCalendar(2020, GregorianCalendar.JULY, 27);
        String input=" %1$tm";
        System.out.format(input + " 时间不匹配,应该是某个月的第 %1$terd 天", c);
    }


粗看一下没什么问题,但是我们的input中包含了格式化信息,最后输出结果:


07 时间不匹配,应该是某个月的第 27rd 天


变相的,我们获取到了系统内部的信息,在某些情况下面,可能会暴露系统的内部逻辑。


上面的例子我们应该将input也作为一个参数,如下所示:


public void rightFormat(){
        Calendar c = new GregorianCalendar(2020, GregorianCalendar.JULY, 27);
        String input=" %1$tm";
        System.out.format("%s 时间不匹配,应该是某个月的第 %terd 天",input, c);
    }


输出结果:


%1$tm 时间不匹配,应该是某个月的第 27rd 天


小心使用Runtime.exec()



我们知道Runtime.exec()使用来调用系统命令的,如果有恶意的用户调用了“rm -rf /”,一切的一切都完蛋了。


所以,我们在调用Runtime.exec()的时候,一定要小心注意检测用户的输入。


看下面的一个例子:


public void wrongExec() throws IOException {
        String dir = System.getProperty("dir");
        Runtime rt = Runtime.getRuntime();
        Process proc = rt.exec(new String[] {"sh", "-c", "ls " + dir});
    }


上面的例子中,我们从系统属性中读取dir,然后执行了系统的ls命令来查看dir中的内容。


如果有恶意用户给dir赋值成:


/usr & rm -rf /


那么系统实际上执行的命令就是:


sh -c 'ls /usr & rm -rf /'


从而导致恶意的删除。


解决上面的问题也有几个方法,第一个方法就是对输入做个校验,比如我们只运行dir包含特定的字符:


public void correctExec1() throws IOException {
        String dir = System.getProperty("dir");
        if (!Pattern.matches("[0-9A-Za-z@.]+", dir)) {
            // Handle error
        }
        Runtime rt = Runtime.getRuntime();
        Process proc = rt.exec(new String[] {"sh", "-c", "ls " + dir});
    }


第二种方法就是使用switch语句,限定特定的输入:


public void correctExec2(){
        String dir = System.getProperty("dir");
        switch (dir){
            case "/usr":
                System.out.println("/usr");
                break;
            case "/local":
                System.out.println("/local");
                break;
            default:
                break;
        }
    }


还有一种就是不使用Runtime.exec()方法,而是使用java自带的方法。


正则表达式的匹配


在正则表达式的构建过程中,如果使用用户自定义输入,同样的也需要进行输入校验。

考虑下面的正则表达式:


(.*? +public\[\d+\] +.*<SEARCHTEXT>.*)


上面的表达式本意是想在public[1234]这样的日志信息中,搜索用户的输入。


但是用户实际上可以输入下面的信息:


.*)|(.*


最终导致正则表达式变成下面的样子:


(.*? +public\[\d+\] +.*.*)|(.*.*)


从而导致匹配所有的日志信息。


解决方法也有两个,一个是使用白名单,判断用户的输入。一个是使用Pattern.quote()来对恶意字符进行转义。


本文的代码:


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

相关文章
|
人工智能 监控 安全
Java智慧工地(源码):数字化管理提升施工安全与质量
随着科技的发展,智慧工地已成为建筑行业转型升级的重要手段。依托智能感知设备和云物互联技术,智慧工地为工程管理带来了革命性的变革,实现了项目管理的简单化、远程化和智能化。
519 5
|
SQL Java 数据库连接
如何用 Java 校验 SQL 语句的合法性?
本文介绍了五种校验 SQL 语句合法性的方案:1) 使用 JDBC API 的 `execute()` 方法,通过捕获异常判断合法性;2) 使用 JSqlParser 库解析 SQL 语句为 Java 对象;3) 使用正则表达式检查 SQL 语句格式;4) 使用 ANTLR 生成 SQL 解析器;5) 使用 Apache Calcite 解析 SQL。每种方法各有优劣,具体选择取决于需求和个人偏好。需要注意的是,这些方法仅能校验语法合法性,无法保证语义正确性,仍需防范 SQL 注入攻击。
958 6
|
SQL 安全 Java
Java 异常处理:筑牢程序稳定性的 “安全网”
本文深入探讨Java异常处理,涵盖异常的基础分类、处理机制及最佳实践。从`Error`与`Exception`的区分,到`try-catch-finally`和`throws`的运用,再到自定义异常的设计,全面解析如何有效管理程序中的异常情况,提升代码的健壮性和可维护性。通过实例代码,帮助开发者掌握异常处理技巧,确保程序稳定运行。
632 2
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
335 4
|
Java
Java实现随机生成某个省某个市的身份证号?如何编码?
【10月更文挑战第18天】Java实现随机生成某个省某个市的身份证号?如何编码?
1506 5
|
Java
Java开发实现图片URL地址检验,如何编码?
【10月更文挑战第14天】Java开发实现图片URL地址检验,如何编码?
739 4
|
Java
Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
【10月更文挑战第14天】Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
485 2
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
639 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
安全 Java 编译器
Java 泛型深入解析:类型安全与灵活性的平衡
Java 泛型通过参数化类型实现了代码重用和类型安全,提升了代码的可读性和灵活性。本文深入探讨了泛型的基本原理、常见用法及局限性,包括泛型类、方法和接口的使用,以及上界和下界通配符等高级特性。通过理解和运用这些技巧,开发者可以编写更健壮和通用的代码。
420 1
|
安全 Java API
java安全特性
java安全特性
197 9