代码漏洞说明

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 Hacker\r\nHTTP/1.1 200 OK\r\n...,那么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 user information by name-->
<select id="queryByUserName" resultMap="userResultMap" parameterType="String">
	select * from db_user where user_name=${username}
</select>

如果攻击者能够替代username中的任意字符串,它们可以使用下面的关于username的字符串进行SQL注入。
validuser' OR '1'='1
当其注入到命令时,命令就会变成:
select * from db_user where user_name ='validuser' OR '1'='1'。即使所输入字符串不是来源于不可信赖的数据源,程序仍然存在着一定风险。

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

  1. 正确使用参数化API进行SQL查询。
  2. 如果构造SQL指令时需要动态加入约束条件,可以通过创建一份合法字符串列表,使其对应于可能要加入到SQL指令中的不同元素,来避免SQL注入攻击。
    例如:以下代码片段采用#变量名称,创建参数化查询SQL语句。
<!--select user information by name-->
<select id="queryByUserName" resultMap="userResultMap" parameterType="String">
	select * from db_user where user_name=#{username}
</select>
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
        }
    }
}

在上面的例子中,攻击者能够自由控制输入的字符串变量usernamepassword中的内容,他们可以使用下面的关于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 user information by name-->
<select id="queryByUserName" resultMap="userResultMap" parameterType="String">
select * from db_user where user_name=${username}
</select>

用户调用以下命令时:

String username = request.getParameter("name");
user.queryByUserName(username);

如果攻击者能够替代username中的任意字符串,它们可以使用下面的关于username的字符串进行SQL注入。
validuser' OR '1'='1
当其注入到命令时,命令就会变成:
select * from db_user where user_name ='validuser' OR '1'='1'

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

  1. 正确使用参数化API进行SQL查询。
  2. 如果构造SQL指令时需要动态加入约束条件,可以通过创建一份合法字符串列表,使其对应于可能要加入到SQL指令中的不同元素,来避免SQL注入攻击。
    例如:以下代码片段采用#变量名称,创建参数化查询SQL语句。
<!--select user information by name-->
<select id="queryByUserName" resultMap="userResultMap" parameterType="String">
	select * from db_user where user_name=#{username}
</select>
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
        }
    }
}

如果攻击者能够替代usernamepassword中的任意字符串,它们可以使用下面的关于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.Statementjava.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.对从数据库或其它后端数据存储获取不可信赖的数据进行合理验证(如年龄只能是数字),对特殊字符(如<、>、'、"以及<script>、javascript等进行过滤。
2.根据数据将要置于HTML上下文中的不同位置(HTML标签、HTML属性、JavaScript脚本、CSS、URL),对所有不可信数据进行恰当的输出编码。
例如:采用OWASP ESAPI对数据输出HTML上下文中不同位置,编码方法如下。

//HTML encode
ESAPI.encoder().encodeForHTML(inputData);
//HTML attribute encode
ESAPI.encoder().encodeForHTMLAttribute(inputData);
//JavaScript encode
ESAPI.encoder().encodeForJavaScript(inputData);
//CSS encode
ESAPI.encoder().encodeForCSS(inputData);
//URL encode
ESAPI.encoder().encodeForURL(inputData);

3.设置HttpOnly属性,避免攻击者利用跨站脚本漏洞进行Cookie劫持攻击。在Java EE中,给Cookie添加HttpOnly的代码如下:

...
response.setHeader("Set-Cookie","cookiename=cookievalue; path=/; Domain=domainvaule; Max-age=seconds; HttpOnly");
...
8 跨站脚本:反射型XSS

反射型XSS是指应用程序通过Web请求获取不可信赖的数据,并在未检验数据是否存在恶意代码的情况下,将其发送给用户。反射型XSS一般可以由攻击者构造带有恶意代码参数的URL来实现,在构造的URL地址被打开后,其中包含的恶意代码参数被浏览器解析和执行。这种攻击的特点是非持久化,必须用户点击包含恶意代码参数的链接时才会触发。
例如:下面JSP代码片段的功能是从HTTP请求中读取输入的用户名(username)并显示到页面。

<%
String name= request.getParameter("username"); %>
...
姓名: <%= name%>
...

如果name里有包含恶意代码,那么Web浏览器就会像显示HTTP响应那样执行该代码,应用程序将受到反射型XSS攻击。

修复建议

为了避免反射型XSS攻击,建议采用以下方式进行防御:
1.对用户的输入进行合理验证(如年龄只能是数字),对特殊字符(如<、>、'、"以及<script>、javascript等进行过滤。
2.根据数据将要置于HTML上下文中的不同位置(HTML标签、HTML属性、JavaScript脚本、CSS、URL),对所有不可信数据进行恰当的输出编码。
例如:采用OWASP ESAPI对数据输出HTML上下文中不同位置,编码方法如下。

//HTML encode
ESAPI.encoder().encodeForHTML(inputData);
//HTML attribute encode
ESAPI.encoder().encodeForHTMLAttribute(inputData);
//JavaScript encode
ESAPI.encoder().encodeForJavaScript(inputData);
//CSS encode
ESAPI.encoder().encodeForCSS(inputData);
//URL encode
ESAPI.encoder().encodeForURL(inputData);

3.设置HttpOnly属性,避免攻击者利用跨站脚本漏洞进行Cookie劫持攻击。在Java EE中,给Cookie添加HttpOnly的代码如下:

...
response.setHeader("Set-Cookie","cookiename=cookievalue; path=/; Domain=domainvaule; Max-age=seconds; HttpOnly");
...
9. 输入验证:重定向

应用程序允许未验证的用户输入控制重定向中的URL,攻击通过构建URL,使用户重定向到任意URL,利用这个漏洞可以诱使用户访问某个页面,挂马、密码记录、下载任意文件等,常被用来钓鱼。
例如:以下Servlet代码会接收前台的url参数,然后Servlet进行一系列业务操作后重定向到该链接,一般情况下这个链接可能是默认的,例如登陆处登陆成功后跳到首页,但是这种情况没有限制用户输入自定义的链接。

public class RedirectServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException{
        ...
        String query = request.getQueryString();
        if (query.contains("url")) {
            String url = request.getParameter("url");
            response.sendRedirect(url);
        }
    }
}

常见的场景是受害者收到一封电子邮件,指示该用户打开http://trusted.example.com/ecommerce/redirect.asp?url=www.wilyhacker.com链接,用户有可能会打开该链接,因为他会认为这个链接将转到可信赖的站点。然而,一旦用户打开该链接,上面的代码会将浏览器重定向至http://www.wilyhacker.com。很多用户都被告知,要始终监视通过电子邮件收到的URL,以确保链接指向一个他们所熟知的可信赖站点。尽管如此,如果攻击者对目标URL进行16进制编码:http://trusted.example.com/ecommerce/redirect.asp?url=%77%69%6C%79%68%61%63%6B%65%72%2E%63%6F%6D以提高用户辨别URL的难度。更糟糕的是像这样的url通过例如QQ等程序进行传输的时候,这些程序的安全机制是不能识别url参数中的危险链接的。

修复建议

防止重定向漏洞的方法是创建一份合法URL列表,用户只能从中进行选择,进行重定向操作。
例如:以下Servlet代码先对url进行判断,再决定是否重定向到该链接。

public class RedirectServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws   ServletException,IOException{
        ...
        String query = request.getQueryString();
        if (query.contains("url")) {
            String url = request.getParameter("url");
            if(safeUrls.contains(url)){
                response.sendRedirect(url);
            }
        ...
        }
    }
}
10 输入验证:路径遍历

应用程序对用户可控制的输入未经合理校验,就传送给一个文件API。攻击者可能会使用一些特殊的字符(如../)摆脱受保护的限制,访问一些受保护的文件或目录。
例如:下面代码片段通过验证输入路径是否以/safe_dir/为开头,来判断是否进行创建、删除操作。

...
String path = getInputPath();
if (path.startsWith("/safe_dir/")){
    File f = new File(path);
    f.delete()
}
...

攻击者可能提供类似下面的输入:
/safe_dir/../important.dat
程序假定路径是有效的,因为它是以/safe_dir/开头的,但是../将导致程序删除important.dat文件的父目录。

修复建议:

预防路径遍历的威胁,有以下三种方法:

  1. 程序对非受信的用户输入做过滤和验证,对网站用户提交的文件路径进行硬编码或统一编码,过滤非法字符。
  2. 对文件后缀进行白名单控制,拒绝包含了恶意的符号或空字节。
  3. 合理配置Web服务器的目录访问权限。
11. 密码管理:不安全的随机数

Java API中提供了java.util.Random类实现PRNG(),该PRNG是可移植和可重复的,如果两个java.util.Random类的实例使用相同的种子,会在所有Java实现中生成相同的数值序列。
例如:下面代码片段中,使用了java.util.Random类,该类对每一个指定的种子值生成同一个序列。

import java.util.Random;
// ...
public static void main (String args[]) {
    // ...
    for (int i = 0; i < 10; i++) {
        Random random = new Random(123456);
        int number = random.nextInt(21);
    ...
    }
}

修复建议:
在安全性要求较高的应用中,应使用更安全的随机数生成器,如java.security.SecureRandom类。
例如:下面代码片段中,使用java.security.SecureRandom来生成更安全的随机数。

import java.security.SecureRandom;
import java.security.NoSuchAlgorithmException;
// ...
public static void main (String args[]) {
    try {
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        for (int i = 0; i < 10; i++) {
            int number = random.nextInt(21);
    ...
        }
    } catch (NoSuchAlgorithmException nsae) {
    ...
    }
}
12. 密码管理:硬编码密码

程序中采用硬编码方式处理密码,一方面会降低系统安全性,另一方面不易于程序维护。
例如:下列代码中采用硬编码方式处理密码。

public class ConnectionConfig{
String url = "localhost";
String name = "admin";
String password = "123456";
...
}

修复建议:

程序中不应对密码进行硬编码,可以使用配置文件或数据库存储的方式来存储系统所需的数据;并且录入数据时,还可以在对敏感数据做加密处理之后再进行数据的录入。

例如:下列代码中从配置文件中获取经过加密的密码值并解密使用。

public class ConnectionConfig{
	String url = EncryptUtil.decrypt(PropertiesUtil.get("connection.url"));
	String name = EncryptUtil.decrypt(PropertiesUtil.get("connection.username"));
	String password = EncryptUtil.decrypt(PropertiesUtil.get("connection.password"));
...
}
13. 密码管理:不安全的哈希算法

在安全性要求较高的系统中,不应使用被业界公认的不安全的哈希算法(如MD2、MD4、MD5、SHA、SHA1等)来保证数据的完整性。
例如:下面代码片段中,采用MD5算法来保证数据的完整性。

...
byte[] b = str.getBytes();
MessageDigest md = null;
try {
	md = MessageDigest.getInstance("MD5");
	md.update(b);
	...
}catch (NoSuchAlgorithmException e){
...
}
...

修复建议:
在安全性要求较高的系统中,应采用散列值>=224比特的SHA系列算法(如SHA-224、SHA-256、SHA-384和SHA-512)来保证敏感数据的完整性。
例如:下面代码片段中,使用SHA-256算法取代MD5算法保证数据完整性。

...
byte[] b = str.getBytes();
MessageDigest md = null;
try {
	md = MessageDigest.getInstance("SHA-256");
	md.update(b);
...
} catch (NoSuchAlgorithmException e) {
...
14. 密码管理:硬编码加密密钥

当程序中使用硬编码加密密钥时,所有项目开发人员都可以查看该密钥,甚至如果攻击者可以获取到程序class文件,可以通过反编译得到密钥,硬编码加密密钥会大大降低系统安全性。
例如:下列代码使用硬编码加密密钥执行AES加密。

private static String encryptionKey = "dfashsdsdfsdgagascv";
...
byte[] keyBytes = encryptionKey.getBytes();
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher encryptCipher = Cipher.getInstance("AES");
encryptCipher.init(Cipher.ENCRYPT_MODE, key);
...

修复建议:
程序应采用不小于8个字节的随机生成的字符串作为密钥。
例如:以下代码使用KeyGenerator来生成密钥。

...
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] keyBytes = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher encryptCipher = Cipher.getInstance("AES");
encryptCipher.init(Cipher.ENCRYPT_MODE, key);
...
15. 代码质量:敏感信息泄露

该软件在通信信道中传输敏感或安全的关键数据,这些数据可以被未经授权的攻击者嗅探泄密。
例如:下面代码片段中,getConnection()方法中密码使用了通信信道传入的值。

...
password = readerBuffered.readLine();
Connection connection = DriverManager.getConnection("data-url", "root", password);
...

从通信信道中获取的密码有可能已经被嗅探,从而暴露connection连接密码。
修复建议:
使用安全通信信道来传输敏感信息,或者对需要传输的敏感数据先进行加密。
例如:下面代码片段中,getConnection()方法中使用的密码为加密后的密码。

...
password = readerBuffered.readLine();
Connection connection = DriverManager.getConnection("data-url", "root", EncryptUtils.encrypt(password));
...
16. 代码注入:动态解析代码

程序运行时动态解析源代码指令将易于受到攻击。
例如:下面代码片段的作用是计算用户输入的表达式的值。

...
expression = form.expInput.value;
result = eval(expression);
...

如果expression参数是合法的,程序将会正常运行。
例如:当该值为"1 + 1 * 2" 时,result变量被赋予的值将为 3。
如果攻击者提供一个恶意的输入,程序在没有进行合理校验的情况,将可以执行任意代码。
修复建议:
在任何时候,都应尽可能地避免动态的解析源代码。如果程序的功能要求对代码进行动态解析,应用程序不应该直接执行和解析未验证的用户输入。建议创建一份合法操作和数据对象列表,用户可以指定其中的内容,并且只能从中进行选择。
例如:计算表达式的程序可以改为。

...
expression = form.expInput.value;
if(/^[\d\-\+]*$/.test(expression)){
result = eval(expression);
}
...
17. 跨站脚本:基于DOM的XSS

应用程序的客户端代码从

document.location、request.url、document.URL、document.referrer

或其他任何攻击者可以修改的浏览器对象获取数据,如果未验证数据是否存在恶意代码的情况下,就将其动态更新到页面的DOM节点,应用程序将易于受到基于DOM的XSS攻击。
例如:下面的JavaScript代码片段可从URL中读取msg信息,并将其显示给用户。

var url=document.URL;
document.write(url.substring(url.indexOf("msg=")+4,url.length);

该段脚本解析URL,读取msg参数的值,并将其写入页面。如果攻击者设计一个恶意的URL,并以JavaScript代码作为msg参数,那么Web浏览器就会像显示HTTP响应那样执行该代码,应用程序将受到基于DOM的XSS攻击。
修复建议:
基于DOM的XSS是将用户可控的JavaScript数据输出到HTML页面中而产生的漏洞,为了避免基于DOM的XSS攻击,避免将用户控制的数据直接输出到DOM或脚本中执行。如果不能避免,则应进行严格的过滤。

18. 输入验证:重定向

应用程序允许未验证的用户输入控制重定向中的URL,可能会导致攻击者发动钓鱼攻击。
例1:以下JavaScript代码从用户输入表单的dest参数中读取目的URL,然后在新窗口中打开。

dsturl = myForm.dsturl.value;
window.open(dsturl,"newwin");

假如攻击者可以控制这个表单,那么用户就有可能打开一个恶意站点。
例2:以下是Node.js可能出现的。

var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.redirect(req.url);
});

与例1一样假如攻击者控制了这个url,那么就会导致用户可能打开恶意站点。
修复建议:
js和Node.js都要避免采用不可信数据源的数据来构造重定向的URL,如果业务逻辑需要涉及到用户输入,那么就创建一份合法URL列表,用户只能从中进行选择,进行重定向操作。
例如:以下代码中,用户只能输入数字来选择URL。

dst = myForm.dst.value;
if(dst == "1"){
	dsturl = "http://www.1.com"
}else if(dst == "2"){
	dsturl = "http://www.2.com"
}else{
	dsturl = "http://www.3.com"
}
window.open(dsturl,"newwin");
19. 密码管理:硬编码密码

程序中采用硬编码方式处理密码,一方面会降低系统安全性,一方面不易于维护。
例如

var password = "123456"

修复建议:
程序中所需密码应从配置文件中获取经过加密的密码值。

20. 密码管理:不安全的哈希算法

在安全性要求较高的系统中,不应使用被业界公认的不安全的哈希算法(如MD5、SHA-1等)来保证数据的完整性。
**例:**以下代码使用MD5的哈希算法加密。

md5("123456");

修复建议:
在安全性要求较高的系统中,应采用散列值>=224比特的SHA系列算法(如SHA-224、SHA-256、SHA-384和SHA-512)来保证敏感数据的完整性。

21 密码管理:硬编码加密密钥

请勿对加密密钥进行硬编码,因为这样所有项目开发人员都能查看该加密密钥,而且还会大大增加解决问题的难度。一旦代码被使用,除非对软件进行修补,否则加密密钥将再也不能更改。如果受加密密钥保护的帐户遭受入侵,系统所有者将被迫在安全性和可用性之间做出选择。
任何可访问该代码的人都能访问加密密钥。一旦应用程序发布,除非对程序进行修补,否则将无法更改加密密钥。雇员可以利用手中掌握的信息访问权限入侵系统。更糟糕的是,如果攻击者可以访问应用程序的可执行文件,就可以提取加密密钥值。
例如:下面代码的加密密钥设置为硬编码。

var crypto = require('crypto');
var algorithm = 'aes-256-ctr';
var cipher = crypto.createCipher(algorithm, "aaaaa");

修复建议:
避免对加密密钥进行硬编码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lang20150928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值