• Java开发工程师(Web方向)


    第3章--SQL注入与防范

    SQL注入与防范

    经常遇到的问题:数据安全问题,尤其是sql注入导致的数据库的安全漏洞

    国内著名漏洞曝光平台:WooYun.org

    数据库泄露的风险:用户信息、交易信息的泄露等

    什么是SQL数据库注入?

    Web应用下,终端用户是无法直接访问数据库的,他们必须通过发送http请求到Java服务器,由Java服务器访问后端数据库。因此,恶意用户想要获取数据库中的数据,必须通过Java服务器来访问后端数据库而无法绕行。他们的唯一途径就是利用应用程序的漏洞,伪装自己的请求,欺骗业务程序,达到访问数据库的目的。

    之前学习的代码:用户登录场景

    User user = null;
    String sql = "select * from user where userName = '" + userName
        + "' and password = '" + password + "'";
    rs = stmt.executeQuery(sql);
    while(rs.next()) {
        user = new User();
        user.setUserName(rs.getString("userName"));
        user.setCardNum(rs.getString("cardNum"));
    }
    return user;

    Java应用程序接收到用户的输入后,检索后端数据库,匹配数据库记录。

    如果:

    用户输入为ZhangSan’;--  密码随便输入(因为不知道真实密码)

    也会登陆成功,为什么呢?

    select * from user where userName = 'ZhangSan'; -- 'and password='111';

    密码条件被注释

    总结:数据库注入就是用户在输入部分输入SQL命令,破坏原有SQL的语义,以达到欺骗服务器并发送恶意SQL的业务程序的漏洞。

    原因:SQL语句为动态拼接的,语义未知。

    解决方案:除了动态拼接,还有什么办法能使用web传入的参数呢?

    参数化sql的实现:1. 确定sql的语义;2. 传入参数

    利用Connection对象的.preparedStatement(sql)方法;

    preparedStatement()相对于Statement的最大优点:提供了参数化sql的实现(格式化的sql)

    select * from user where userName = ? and password = ?

    ?为占位符,替代了一个参数。sql的语义确定了。

    根据参数的类型:.setInt(); .setString(); .setBoolean(); 来替代参数化sql中的占位符。

    除了使用PreparedStatement,我们还应该有一些其他的注意事项:

    1. 执行严格的数据库权限管理

    仅给予Web应用访问数据库的最小权限

    严格禁止Drop table等权限

    2. 封装数据库错误:不能将数据库异常直接暴露给用户(因为数据库异常通常包含了大量的数据库信息)

    禁止直接将后端数据库异常信息暴露给用户

    对后端异常信息进行必要的封装,避免用户直接查看到后端异常

    3. 机密信息禁止明文存储

    涉密信息需要加密处理

    针对MySQL,可使用AES_ENCRYPT/AES_DECRYPT进行加密和解密

    课堂交流区:

    其他的SQL注入场景:可以使用 "or 1"

    i.e. select name from student where name = '' or 1; -- '';

    会返回所有

    SQL注入与防范单元测试

    本次得分为:100.00/100.00, 本次测试的提交时间为:2017-08-24
    1多选(40分)

    以下哪项描述是正确的?

    • A.PreparedStatement可以实现参数化SQL,防止SQL注入的风险。�13.33/40.00
    • B.SQL注入的本质是因为外部用户恶意改变了原有程序的执行语义,导致数据泄露。�13.33/40.00
    • C.SQL注入可以导致外部用户获取整个数据库的信息。�13.33/40.00
    • D.使用PreparedStatement,如果用户使用SQL注释符,也可能会改变SQL的语义执行。
    2判断(10分)

    SQL 注入是由于应用程序动态拼接SQL,用户利用该漏洞,注入特殊SQL语句,导致应用程序执行恶意SQL命令的现象。

    • A.√�10.00/10.00
    • B.×
    3判断(10分)

    Statement接口可以实现参数化SQL,防止动态拼接SQL导致的SQL注入漏洞。

    • A.×�10.00/10.00
    • B.√
    4判断(10分)

    应谨慎给予用户Delete权限,防止用户数据被恶意删除。

    • A.×
    • B.√�10.00/10.00
    5判断(10分)

    SQL注入漏洞是由于数据库密码泄露,导致用户登陆到数据库上执行了恶意操作。

    • A.√
    • B.×�10.00/10.00
    6判断(10分)

    PreparedStatement需要设置格式化SQL语句,格式化SQL语句就是将SQL的参数使用占位符替代的SQL语句。

    • A.×
    • B.√�10.00/10.00
    7判断(10分)

    应用程序需要捕获SQL异常,将异常信息展现给用户,方便用户处理。

    • A.√
    • B.×�10.00/10.00

    SQL注入与防范单元作业

    请完成SQL注入与防范的编程题目。

    1(100分)

    有一张学生表

    id name number
    1 XiaoMing 100
    2 XiaoLi 101
    3 XiaoZhao 102

    现在需要根据学生名称获取学生的期末考试分数。

    public static void getStudent(String name) throws ClassNotFoundException {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
          Class.forName(JDBC_DRIVER);
          conn = DriverManager.getConnection(DB_URL, USER, PASS);
          stmt = conn.createStatement();
          rs = stmt.executeQuery("select name,score from student where name =' " + name +"'");
          while (rs.next()) {
            System.out.println(rs.getString("name") + ":" + rs.getInt("score"));
          }
        } catch (SQLException e) {
          // ignore
        } finally {
          if (rs != null) {
            try {
              rs.close();
            } catch (Exception e) {
              // ignore
            }
          }
          if (stmt != null) {
            try {
              stmt.close();
            } catch (Exception e) {
              // ignore
            }
          }
          if (conn != null) {
            try {
              conn.close();
            } catch (SQLException e) {
              // ignore
            }
          }
        }
    }

    1. 请指出上面这段程序存在什么安全风险?并给出具体的测试用例。

    2. 请重新编写应用程序,解决上述风险。

    答:

    1. 安全风险:上述程序使用Statement对象,有SQL注入的风险。

    2. 程序错误:

      1. stmt.executeQuery(sql)中的sql语句有误,多了一个空格导致

      "select name,number from student where name ='  XiaoZhao'" 返回的为empty set

      2. 数据库中columns分别为id, name和number,而程序中select语句包含的attributes为name和number

    3. SQL注入测试用例:

      getStudent("' or 1;#");

    4. 解决SQL注入、修改错误后的代码:

    import java.sql.Connection;
    import java.sql.DriverManager;
    //import java.sql.Statement;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    public class Assignment {
    
        static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
        static final String DB_URL = "jdbc:mysql://localhost/helloworld";
        static final String USER = "matt";
        static final String PASS = "matt";
        
        public static void getStudent(String name) throws ClassNotFoundException {
            Connection conn = null;
    //        Statement stmt = null;
            PreparedStatement ptmt = null;
            ResultSet rs = null;
            try {
                Class.forName(JDBC_DRIVER);
                conn = DriverManager.getConnection(DB_URL, USER, PASS);
    //            stmt = conn.createStatement();
                String sql = "select name,number from student where name = ?";
                ptmt = conn.prepareStatement(sql);
                ptmt.setString(1, name);
    //            rs = stmt.executeQuery("select name,number from student where name =' " + name +"'");
                rs = ptmt.executeQuery();
                while (rs.next()) {
                    System.out.println(rs.getString("name") + ":" + rs.getInt("number"));
                }
            } catch (SQLException e) {
                // ignore
            } finally {
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (Exception e) {
                        // ignore
                    }
                }
    //            if (stmt != null) {
                if (ptmt != null) {
                    try {
    //                    stmt.close();
                        ptmt.close();
                    } catch (Exception e) {
                        // ignore
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        // ignore
                    }
                }
            }
        }
        
        public static void main (String[] args) throws ClassNotFoundException {
            getStudent("XiaoZhao");
            getStudent("' or 1;#");
        }
    }

     

     

  • 相关阅读:
    [转]测试的基本概念
    记录
    flash 与 程序通讯
    怎么正确的建立项目
    安装包的制作
    JS
    页面刷新定位
    [转]C#处理XML
    MSN机器人
    报表 的使用
  • 原文地址:https://www.cnblogs.com/FudgeBear/p/7420273.html
Copyright © 2020-2023  润新知