SQL注入示例及防范措施介绍


文章目录

  • SQL注入示例及防范措施介绍
  • 一、SQL注入简介
  • 二、SQL防注入方法
  • 三、总结



一、SQL注入简介

SQL注入是将Web页面的原URL、表单域或数据包输入的参数,修改拼接成SQL语句传递给Web服务器,进而传给数据库服务器以执行数据库命令。其根本原因是开发人员创建动态数据库查询语句时拼接了来自客户端不可信的输入。SQL注入攻击能够读取数据库中的敏感数据,修改数据库(插入/更新/删除)数据,执行数据库管理员的操作(比如关闭DBMS),还能使用DBMS上存在的文件去覆盖数据库内容,在某些情况下还能执行操作系统的命令,破坏了数据的完整性与机密性。
SQL注入代码示例如下:

String customerId = "'test' or 1=1";
String sql ="selectAIF_ACCNO from MB_ACCINF where AIF_CSTNO="+customerId;//拼接SQL参数

在数据库服务器中执行的SQL语句将为:

selectAIF_ACCNO from MB_ACCINF where AIF_CSTNO='test' or 1=1

永真条件1=1成为了查询条件的一部分,查询将返回表中所有的信息,从而造成数据泄露。
代码中直接使用了“+”运算符进行SQL语句拼接,会将恶意代码拼入最终的SQL语句,导致了SQL注入问题。

二、SQL防注入方法

目前SQL防注入的方法主要有三种:

  • 预编译法;
  • 避免从前端传不必要的值;
  • 对传入的参数进行过滤。
  1. 预编译法
    使用最广泛的是预编译法。预编译法可以将SQL语句和变量分离进行编译,从而支持动态化查询,确保了遇到攻击时也不会改变原有SQL语句的意图。
    在JAVA中,通常使用PrepareStatement对SQL语句进行预编译,主要包括三步:
    ①导入PrepareStatement类
    ②使用Connection类的prepareStatement()方法预编译SQL
    ③使用PrepareStatement类的setXXX()方法将参数传入SQL语句。
    示例代码如下:
String custName = request.getParameter("customerName");
String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
PrepareStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1,custName);
ResultSet results = pstmt.executeQuery();

预编译法需要消耗一部分内存,将SQL语句缓存起来循环使用。预编译法可以理解为将SQL语句变成一个函数,函数的变量用占位符表示,后面注入的参数系统会默认它仅仅是一个参数,而不会认为是一个SQL语句而再次编译,从而避免SQL注入攻击。
mybatis提供了预编译支持,具体为使用#{xxx}传参而不是${xxx}。
示例代码如下:

<delete id="deleteById">
	DELETE
	FROM 'ORM_USER'
	WHERE 'id'=#{id}
</delete>
  1. 避免从前端传不必要的值
    需要特别说明的是,在给order by字句传入字段时,不能使用预编译的方法,因为setString会将字段加入单引号传入,而order by后的字段不能出现引号。此时,需先拼接SQL语句,再对字段的长度、内容等进行过滤处理。对排序依据能够在后端确认的字段,应当避免从前端传入。
  2. 对传入的参数进行过滤
    有多种方法可以对传入的字段进行过滤,如:对单引号等特殊字符进行转义等;对AND、INSERT等关键字进行字符串过滤;限制表单或查询字符串输入的长度等。
    对特殊字符进行转义可以避免输入的参数被视为SQL命令进行拼接,当不合法时会立即抛出异常。
    示例代码如下:
String userName = "xiaoMing'or '1'='1";
String password = "123456";
userName = StringEscapeUtils.escapeSql(userName);
password = StringEscapeUtils.escapeSql(password);
String sql = "SELECT COUNT(userId) FROM t_user WHERE userName='"+userName+"'AND password = '"+password+"'";

对AND、INSERT等关键字进行字符串过滤是指利用正则表达式或提前设置好敏感字符串来防范SQL的注入。
示例代码如下:

protected static booleansqlValidate(String str){
	str = str.toLowerCase();//统一转为小写
	String badStr = "'|and|exec|execute|insert|select|delete|update|count|drop|*|%|chr|mid|master|truncate|"+"char|declare|sitename|netuser|xp_cmdshell|;|or|-|+|,|like'|and|exec|execute|insert|create|drop|"+"table|from|grant|use|group_count|column_name|"+"information_schema.columns|table_schema|union|where|delete|update|order|by|count|*|"+"chr|mid|master|truncate|char|declare|or|;|-|--|+|,|like|//|/|%|#";
	String[] badStrs = badStr.split("\\|");
	for(int i = 0;i<badStrs.length;i++){
		if(str.indexOf(badStrs[i])>=0){
			return true;
			}
		}
		return false;
	}

三、总结

SQL注入是比较常见的网络攻击方式之一,它针对程序员编程时的疏忽,通过注入恶意SQL语句实现敏感信息获取、篡改数据甚至删除整个数据库。SQL注入有多种防范的方法,主要是通过预编译的方法防范,在实际开发过程中需要针对具体场景选择合适的防范方式。