栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

代码漏洞说明

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

代码漏洞说明

1. 代码注入:命令注入

命令注入是指,在应用程序执行的命令中包含来源于不可信数据时,程序本身没有对这些不可信数据做正确、合理的验证和过滤,导致系统执行恶意命令。
例1:以下代码通过Runtime.exec()方法调用Windows的dir命令,列出目录列表。

package com.syn;

import java.io.*;

public class DirList {
    public static void main(String[] args) {
        String dir = System.getProperty("dir");
        Process process = null;
        InputStream istream = null;
        try {
            process = Runtime.getRuntime().exec("cmd.exe /c dir" + dir);
            int result = process.waitFor();
            if (result != 0) {
                System.out.println("process error: " + result);
            }
            istream = (result == 0) ? process.getInputStream() : process.getErrorStream();
            byte[] buffer = new byte[512];
            while (istream.read(buffer) != -1) {
                System.out.print(new String(buffer, "gb2312"));
            }
        } catch (IOException e1) {
            e1.printStackTrace();
        } catch (InterruptedException e2) {
            e2.printStackTrace();
        } finally {
            if (istream != null) {
                try {
                    istream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (process != null) {
                process.destroy();
            }
        }
    }
}

攻击者可以构造特殊命令参数来利用该程序,例如:
java -Ddir="..\ & shutdown -s -t 60" DirList
上面的命令实际上执行下面两条命令:
cmd.exe /c dir .. & shutdown -s -t 60
例2:以下代码片段来自一个Web应用程序,它该段代码通过运行rmanDB.bat脚本启动Oracle数据库备份,然后运行cleanup.bat脚本删除一些临时文件。脚本文件rmanDB.bat接受一个命令行参数,指明需要执行的备份类型。

...
String btype = request.getParameter("backuptype");
String cmd = new String("cmd.exe /K "c:\util\rmanDB.bat "+btype+"&& c:\utl\cleanup.bat"");
System.Runtime.getRuntime().exec(cmd);
...

该段代码没有对来自用户请求中的参数做任何校验。
通常情况下,Runtime.exec()函数不会执行多条命令,但在以上代码中,为了执行多条命令,程序调用Runtime.exec()方法,首先运行了cmd.exe指令,因此能够执行用&&分隔的多条命令了。如果攻击者传递了一个形式为&& del c:\dbms\*.*的字符串,那么该段代码将会在执行其他指定命令的同时执行这条命令。
例3:下面是利用Apache Axis框架搭建的WebService的源码片段。

public String getResponse(String endpoint) {
    Service service = new Service();
    Call call = (Call) service.createCall();
    call.setTargetEndpointAddress(new URL(endpoint));
    call.setOperation("list");
    ...
}

在搭建的Axis 1.4+Tomcat6的环境,将AdminService配置中的enableRemoteAdmin属性设置为true时,如果上面提供访问WebService的字符串变量endpoint是受外部可控的,那么攻击者很可能利用精心设计的字符串作为endpoint的输入值,从而构造出包含注入命令的SOAP请求协议,进而实施命令注入攻击。

修复建议:
防止命令注入的方法如下:

  1. 程序对非受信的用户输入数据进行净化,删除不安全的字符。
  2. 创建一份安全字符串列表,限制用户只能输入该列表中的数据。
  3. 配置URL访问控制策略:部署于公网的axis服务器,可通过ACL禁止对/services/AdminService及/services/FreeMarkerService路径的访问。
  4. 禁用axis远程管理功能:axis<=1.4版本默认关闭了远程管理功能,如非必要请勿开启。若需关闭,则需修改axis目录下WEB-INF文件夹中的server-config.wsdd文件,将其中"enableRemoteAdmin"的值设置为false。

例1:下面代码片段中,使用正则表达式匹配用户的输入。

...
if (!Pattern.matches("[0-9A-Za-z@.]+", dir)) {
// Handle error
}
...

例2:下面代码片段中,通过只向Runtime.exec()方法输入那些受信的字符串来防止命令注入。

...
String btype = null;
// only allow integer choices
int number = Integer.parseInt(request.getParameter("backuptype"));
switch (number) {
    case 1:
        btype = "tables"
        break; // Option 1
    case 2:
        btype = "users"
        break; // Option 2
    case 3:
        btype = "full"
        break; // Option 3
    default: // invalid
        break;
}
if (btype == null) {
// handle error
}
...
2. 代码注入:HTTP响应截断

程序从一个不可信赖的数据源获取数据,未进行验证就置于HTTP头文件中发给用户,可能会导致HTTP响应截断攻击。
例如:下列代码片段中,程序从HTTP请求中获取author的值,并将其设置到HTTP响应文件的cookie中。

...
String author = request.getParameter(AUTHOR_PARAM);
...
cookie cookie = new cookie("author", author);
cookie.setMaxAge(cookieExpiration);
response.addcookie(cookie);
...

如果请求中提交的是一个Jane Smith字符串,那么包含该cookie的HTTP响应可能表现为以下形式:

HTTP/1.1 200 OK
...
Set-cookie: author=Jane Smith
...

那么如果攻击者提交的是一个恶意字符串,比如Wiley HackerrnHTTP/1.1 200 OKrn...,那么HTTP响应就会被分割成以下形式的两个响应:

HTTP/1.1 200 OK
...
Set-cookie: author=Wiley Hacker
HTTP/1.1 200 OK
...

这样第二个响应已完全由攻击者控制,攻击者可以用所需的头文件和正文内容构建该响应实施攻击。

修复建议
防止HTTP响应截断攻击的最安全的方法是创建一份安全字符白名单,只接受完全由这些受认可的字符组成的输入出现在HTTP响应头文件中。
例如:以下代码片段中,验证了author的值是否由标准的字母数字字符组成。

...
String author = request.getParameter(AUTHOR_PARAM);
if (Pattern.matches("[0-9A-Za-z]+", author)) {
...
    cookie cookie = new cookie("author", author);
    cookie.setMaxAge(cookieExpiration);
    response.addcookie(cookie);
}
...
3. 代码注入:有风险的SQL查询:MyBatis

SQL注入是一种数据库攻击手段。攻击者通过向应用程序提交恶意代码来改变原SQL语句的含义,进而执行任意SQL命令,达到入侵数据库乃至操作系统的目的。在Mybatis Mapper Xml中,#变量名称创建参数化查询SQL语句,不会导致SQL注入。而$变量名称直接使用SQL指令,而$变量名称直接使用SQL指令,将会存在一定风险,当SQL指令所需的数据来源于不可信赖的数据源时,可能会导致SQL注入。
例如:以下代码片段采用$变量名称动态地构造并执行了SQL查询。


	select * from db_user where user_name=#{username}

4. 代码注入:SQL注入

SQL注入是一种数据库攻击手段。攻击者通过向应用程序提交恶意代码来改变原SQL语句的含义,进而执行任意SQL命令,达到入侵数据库乃至操作系统的目的。
例如:下面代码片段中,动态构造并执行了一个SQL查询来认证用户。

public void doPrivilegedAction(HttpServletRequest request, char[] password) throws SQLException {
    Connection connection = getConnection();
    if (connection == null) {
    // handle error
    }
    try {
        String username = request.getParameter("username");
        String pwd = hashPassword(password);
        String sqlString = "SELECT * FROM db_user WHERe username = '" + username + "' AND password = '" + pwd + "'";
        Statement stmt = connection.createStatement();
        ResultSet rs = stmt.executeQuery(sqlString);
        if (!rs.next()) {
            throw new SecurityException("User name or password incorrect");
        }
     // Authenticated; proceed
    } finally {
        try {
            connection.close();
        } catch (SQLException x) {
        // forward to handler
        }
    }
}

在上面的例子中,攻击者能够自由控制输入的字符串变量username和password中的内容,他们可以使用下面的关于username的字符串进行SQL注入。
validuser' OR '1'='1
当其注入到命令时,命令就会变成:
SELECt * FROM db_user WHERe username='validuser' OR '1'='1' AND password=''
同样,攻击者可以为password提供如下字符串。
' OR '1'='1
当其注入到命令时,命令就会变成:
SELECt * FROM db_user WHERe username='' AND password='' OR '1'='1'

修复建议
造成SQL注入攻击的根本原因在于攻击者可以改变SQL查询的上下文,使程序员原本要作为数据解析的数值,被篡改为命令了。防止SQL注入的方法如下:

  1. 正确使用参数化API进行SQL查询。
  2. 如果构造SQL指令时需要动态加入约束条件,可以通过创建一份合法字符串列表,使其对应于可能要加入到SQL指令中的不同元素,来避免SQL注入攻击。
  3. 避免出现一些详细的错误消息,防止攻击者利用报错信息来判断后台SQL的拼接形式,甚至是直接利用这些报错注入将数据库中的数据通过报错消息显示出来。
    例如:以下代码片段使用java.sql.PreparedStatement代替java.sql.Statement,在java.sql.PreparedStatement类中可以对输入字符串进行转义,如果使用正确的话,可以防止SQL注入。
public void doPrivilegedAction(HttpServletRequest request, char[] password) throws SQLException {
    Connection connection = getConnection();
    if (connection == null) {
        // handle error
    }
    try {
        String username = request.getParameter("username");
        String pwd = hashPassword(password);
        // Ensure that the length of user name is legitimate
        if ((username.length() > 8) {
            // Handle error
        }
        String sqlString = "select * from db_user where username=? and password=?";
        PreparedStatement stmt = connection.prepareStatement(sqlString);
        stmt.setString(1, username);
        stmt.setString(2, pwd);
        ResultSet rs = stmt.executeQuery();
        if (!rs.next()) {
            throw new SecurityException("User name or password incorrect");
        }
        // Authenticated, proceed
    } finally {
        try {
            connection.close();
        } catch (SQLException x) {
            // forward to handler
        }
    }
}
5. 代码注入:SQL注入:MyBatis

SQL注入是一种数据库攻击手段。攻击者通过向应用程序提交恶意代码来改变原SQL语句的含义,进而执行任意SQL命令,达到入侵数据库乃至操作系统的目的。在Mybatis Mapper Xml中,#变量名称创建参数化查询SQL语句,不会导致SQL注入。而$变量名称直接使用SQL指令,会导致SQL注入攻击。
例如:以下代码片段采用$变量名称动态地构造并执行了SQL查询。


	select * from db_user where user_name=#{username}

6. 代码注入:有风险的SQL查询

SQL注入是一种数据库攻击手段。攻击者通过向应用程序提交恶意代码来改变原SQL语句的含义,进而执行任意SQL命令,达到入侵数据库乃至操作系统的目的。系统中有一些方法存在着一定风险,当方法所需的数据来源于不可信赖的数据源时,可能会导致SQL注入。
例如:下面代码片段中,动态构造并执行了一个SQL查询来认证用户。

public void doPrivilegedAction(String username, char[] password) throws SQLException {
    Connection connection = getConnection();
    if (connection == null) {
    // handle error
    }
    try {
        String pwd = hashPassword(password);
        String sqlString = "SELECT * FROM db_user WHERe username = '" + username + "' AND password = '" + pwd + "'";
        Statement stmt = connection.createStatement();
        ResultSet rs = stmt.executeQuery(sqlString);
        if (!rs.next()) {
            throw new SecurityException("User name or password incorrect");
        }
    // Authenticated; proceed
    } finally {
        try {
            connection.close();
        } catch (SQLException x) {
    // forward to handler
        }
    }
}

如果攻击者能够替代username和password中的任意字符串,它们可以使用下面的关于username的字符串进行SQL注入。
validuser' OR '1'='1
当其注入到命令时,命令就会变成:
SELECt * FROM db_user WHERe username='validuser' OR '1'='1' AND password=''
同样,攻击者可以为password提供如下字符串。
' OR '1'='1
当其注入到命令时,命令就会变成:
SELECt * FROM db_user WHERe username='' AND password='' OR '1'='1'

修复建议

常见的修复方法:

  1. 使用预编译处理输入参数:要防御SQL注入,用户的输入就不能直接嵌套在SQL语句中。使用参数化的语句,用户的输入就被限制与一个参数当中。
  2. 输入验证:检查用户输入的合法性,以确保输入内容是正规数据。在客户端和服务器端都要进行数据检查,之所以要执行服务器端验证,是因为客户端的校验往往只是减轻服务器的压力和提高对用户的友好度,攻击者完全有可能通过抓包修改参数或者是获得网页的源代码后,修改验证合法性的脚本(或直接删除),然后将非法内容通过修改后的表单提交给服务器等手段绕过客户端的校验。因此,要保验证处理,唯一的办法就是在服务端也做验证。但是这些方法很容易出现由于过滤不严,导致恶意攻击者可能绕过这些过滤的情况发生,所以需要慎重使用。
  3. 错误消息处理:防范SQL注入,还要避免出现一些详细的错误信息,攻击者往往会利用给这些报错信息来判断后台SQL的拼接方式,甚至是直接利用给这些报错,将数据库中的数据通过报错信息显示出来。
  4. 加密处理:将用户登录名称、密码等数据加密保存。加密用户输入的数据,然后再将它与数据库中保存的数据比较,这相当与对用户输入的数据进行了“消毒”处理,用户输入的数据不再对数据库有任何特殊的意义,从而也就防止了攻击者注入SQL命令。
    例如:以下代码片段使用java.sql.PreparedStatement代替java.sql.Statement,java.sql.PreparedStatement类型的对象可以对输入参数做预编译处理,这样有效防止了SQL注入。
public void doPrivilegedAction(String username, char[] password) throws SQLException {
    Connection connection = getConnection();
    if (connection == null) {
    // Handle error
    }
    try {
        String pwd = hashPassword(password);
        // Ensure that the length of user name is legitimate
        if ((username.length() > 8) {
        // Handle error
        }
        String sqlString = "select * from db_user where username=? and password=?";
        PreparedStatement stmt = connection.prepareStatement(sqlString);
        stmt.setString(1, username);
        stmt.setString(2, pwd);
        ResultSet rs = stmt.executeQuery();
        if (!rs.next()) {
            throw new SecurityException("User name or password incorrect");
        }
        // Authenticated, proceed
    } finally {
        try {
            connection.close();
        } catch (SQLException x) {
        // forward to handler
        }
    }
}
7. 跨站脚本:存储型XSS

存储型XSS是指应用程序通过Web请求获取不可信赖的数据,并且在未检验数据是否存在XSS代码的情况下,将其存入数据库。当程序下一次从数据库中获取该数据时,致使页面再次执行XSS代码。存储型XSS可以持续攻击用户,在用户提交了包含XSS代码的数据存储到数据库后,每当用户在浏览网页查询对应数据库中的数据时,那些包含XSS代码的数据就会在服务器解析并加载,当浏览器读到XSS代码后,会当做正常的HTML和JS解析并执行,于是发生存储型XSS攻击。
例如:下面JSP代码片段的功能是根据一个已知用户雇员ID(id)从数据库中查询出该用户的地址,并显示在JSP页面上。

<%
...
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from users where id =" + id);
String address = null;
if (rs != null) {
rs.next();
address = rs.getString("address");
}
%>
家庭地址: <%= address %>

如果address的值是由用户提供的,且存入数据库时没有进行合理的校验,那么攻击者就可以利用上面的代码进行存储型XSS攻击。

修复建议:
为了避免存储型XSS攻击,建议采用以下方式进行防御:
1.对从数据库或其它后端数据存储获取不可信赖的数据进行合理验证(如年龄只能是数字),对特殊字符(如<、>、'、"以及