关于java:java安全编码指南之输入注入injection

30次阅读

共计 4503 个字符,预计需要花费 12 分钟才能阅读完成。

简介

注入问题是平安中一个十分常见的问题,明天咱们来探讨一下 java 中的 SQL 注入和 XML 注入的防备。

SQL 注入

什么是 SQL 注入呢?

SQL 注入的意思是,用户输出了某些参数,最终导致 SQL 的执行偏离了程序设计者的本意,从而导致越权或者其余类型的谬误。

也就是说因为用户输出的起因,导致 SQL 的涵义发送了变动。

拿咱们最罕用的登录的 SQL 语句来说,咱们可能会写上面的 SQL 语句:

select * from user where username='<username>' and password='<password>'

咱们须要用户传入 username 和 password。

怎么对这个 SQL 语句进行注入呢?

很简略,当用户的 username 输出是上面的状况时:

somebody'or'1'='1

那么整个 SQL 语句将会变成:

select * from user where username='somebody' or '1'='1' and password='<password>'

如果 somebody 是一个无效的用户,那么 or 前面的语言齐全不会执行,最终导致不校验明码就返回了用户的信息。

同样的,歹意攻击者能够给 password 输出上面的内容能够失去同样的后果:

'or'1'='1

整个 SQL 解析为:

select * from user where username='somebody' and password=''or'1'='1'

这条语句将会返回所有的用户信息,这样即便不晓得确定存在的用户名也能够通过 SQL 语句的判断。

这就是 SQL 注入。

java 中的 SQL 注入

java 中最罕用的就是通过 JDBC 来操作数据库,咱们应用 JDBC 创立好连贯之后,就能够执行 SQL 语句了。

上面咱们看一个 java 中应用 JDBC SQL 注入的例子。

先创立一个通用的 JDBC 连贯:

    public Connection getConnection() throws ClassNotFoundException, SQLException {
        Connection con = null;
            Class.forName("com.mysql.jdbc.Driver");
            System.out.println("数据库驱动加载胜利");
            con = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8", "root", "");
            System.out.println("数据库连贯胜利");
          return con;
    }

而后再本人拼装 SQL 语句而后调用:

public void jdbcWithInjection(String username,char[] password) throws SQLException, ClassNotFoundException {Connection connection = getConnection();
        if (connection == null) {// Handle error}
        try {String pwd = encodePassword(password);

            String sqlString = "SELECT * FROM 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");
            }
        } finally {
            try {connection.close();
            } catch (SQLException x) {}}
    }

下面的例子中,只有 username 会产生注入,password 不会,因为咱们应用了 encodePassword 办法对 password 进行了转换:

public String encodePassword(char[] password){return Base64.getEncoder().encodeToString(new String(password).getBytes());
    }

应用 PreparedStatement

为了避免 SQL 注入,咱们个别举荐的是应用 PreparedStatement,java.sql.PreparedStatement 可对输出参数进行本义,从而避免 SQL 注入。

留神,肯定要正确的应用 PreparedStatement,如果是不正确的应用,同样会造成 SQL 注入的后果。

上面看一个不正确应用的例子:

String sqlString = "SELECT * FROM user WHERE username ='"
                    + username +
                    "'AND password ='" + pwd + "'";
            PreparedStatement stmt = connection.prepareStatement(sqlString);
            ResultSet rs = stmt.executeQuery();

下面的代码中,咱们还是本人进行了 SQL 的拼装,尽管最初咱们应用了 preparedStatement,然而没有达到成果。

正确应用的例子如下:

String sqlString =
                    "select * from user where username=? and password=?";
            PreparedStatement stmt = connection.prepareStatement(sqlString);
            stmt.setString(1, username);
            stmt.setString(2, pwd);
            ResultSet rs = stmt.executeQuery();

咱们须要将用户输出作为参数 set 到 PreparedStatement 中去,这样才会进行本义。

XML 中的 SQL 注入

可扩大标记语言(XML)旨在帮忙存储,结构化和传输数据。因为其平台独立性,灵活性和绝对简略性,XML 已在许多应用程序中失去应用。然而,因为 XML 的多功能性,它容易受到包含 XML 注入在内的各种攻打的攻打。

那么什么是 XML 注入呢?咱们举个例子:

<item>
  <name>Iphone20</name>
  <price>5000.0</price>
  <quantity>1</quantity>
</item>

下面的例子中,咱们应用了 XML 定义了一个 iphone20 的价格和数量。一个 iphone20 5000 块。

下面的 XML 中,如果 quantity 是用户输出的数据的话,那么用户能够这样输出:

1</quantity><price>20.0</price><quantity>1

最初得出的 XML 文件如下:

<item>
  <name>Iphone20</name>
  <price>5000.0</price>
  <quantity>1</quantity>
  <price>20.0</price><quantity>1</quantity>
</item>

一般来说,咱们在解析 XML 的过程中,如果发现有反复的 tag,那么前面的 tag 会笼罩后面的 tag。

后果就是 1 个 iphone20 当初的价格是 20 块,十分划算。

XML 注入的 java 代码

咱们看下 XML 的注入在 java 代码中是怎么实现的:

    public String createXMLInjection(String quantity){
        String xmlString = "<item>\n<name>Iphone20</name>\n"
                + "<price>5000.0</price>\n" + "<quantity>" + quantity
                + "</quantity></item>";
        return xmlString;
    }

能够看到咱们间接应用用户输出的 quantity 作为 XML 的拼接,这样做很显著是有问题的。

怎么解决呢?有两种办法。

  • 第一种办法

第一种办法就是对用户输出的 quantity 进行校验:

    public String createXML(String quantity){int count = Integer.parseUnsignedInt(quantity);
        String xmlString = "<item>\n<name>Iphone20</name>\n"
                + "<price>5000.0</price>\n" + "<quantity>" + count
                + "</quantity></item>";
        return xmlString;
    }

下面代码中,咱们对 quantity 进行了 Integer 的转换,从而防止了用户的非法输出。

  • 第二种办法

第二种办法是应用 XML Schema,来对生成的 XML 进行格局校验。

先看一下咱们改怎么定义这个 XML Schema:


<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="item">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="name" type="xs:string"/>
      <xs:element name="price" type="xs:decimal"/>
      <xs:element name="quantity" type="xs:nonNegativeInteger"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>
</xs:schema>

下面咱们定义了一个 XML element 的序列 sequence。如果用户输出了非定义格局的其余 XML,就会报错。

咱们看下绝对应的 java 代码该怎么写:

StreamSource ss = new StreamSource(new File("schema.xsd"));
            Schema schema = sf.newSchema(ss);
            SAXParserFactory spf = SAXParserFactory.newInstance();
            spf.setSchema(schema);
            SAXParser saxParser = spf.newSAXParser();
            XMLReader reader = saxParser.getXMLReader();
            reader.setContentHandler(defHandler);
            reader.parse(xmlStream);

下面咱们列出了 XML 验证的代码,残缺的代码能够参考文末的代码链接,这里就不一一贴出来了。

本文的代码:

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

本文已收录于 http://www.flydean.com/java-security-code-line-injection/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!

正文完
 0