看好你的门-保护数据存储区(2)-查看PreparedStatement最终执行的SQL
首先需要声明,本文纯属一个毫无远见和真才实学的小小开发人员的愚昧见解,仅供用于web系统安全方面的参考。
1、前提
很多同学都希望PreparedStatement 打印出最终执行的SQL,可能用于学习,也可能用于系统维护,也有可能用于其他的目标;
我也有这个想法和需求,但是经过多次实践和尝试,我发现在我的能力范围,我是无法实现的。
于是我找到了一个工具,log4jdbc ,这个工具能够切入JDBC层,对实际SQL执行前,把SQL抽出来。
这个工具相当强大,不过最终的运行结果会超出你的设想…….
PreparedStatement 打印出最终执行的SQL。
利用log4jdbc工具。
2、使用log4jdbc工具
详见我的博客:
好记性不如烂笔头14-使用log4jdbc显示完整SQL语句和执行时间
http://blog.csdn.net/ffm83/article/details/43407905
有详细的说明
3、测试用的JSP页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>阿饭同学的测试</title>
</head>
<body>
<form action="aLogin.action" method="post" name="form1">
<table width="392" border="1">
<tr>
<td height="35"><br>
<div align="center">
<p>用户名:<input type="text" name="username" size=16 ></p>
<p>密码: <input type="password" name="password" size=16 > </p>
</div></td>
</tr>
<tr align="center">
<td colspan="2" bgcolor="#FFCCFF"><input type="submit"
value="登陆" /></td>
</tr>
</table>
</form>
</body>
</html>
4、利用log4jdbc工具打印PreparedStatement的最终执行SQL的JAVA代码
package com.struts2;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.lang.xwork.StringUtils;
import org.apache.struts2.ServletActionContext;
import com.db.DBUtils;
import com.db.Log4JDBCTest;
import com.opensymphony.xwork2.ActionSupport;
/**
* 一个简单的登陆认证功能,仅用于说明情况
*
* @author 范芳铭
*/
public class LoginAction extends ActionSupport {
private static final long serialVersionUID = 7854497526623985504L;
public String execute() throws Exception {
System.out.println("---LoginAction start--");
HttpServletRequest request = ServletActionContext.getRequest();
String username = request.getParameter("username");
String password = request.getParameter("password");
request.setAttribute("username", username);
// 用户名和密码如果有一个为空,返回失败
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
return "false";
}
Connection con = null;
try {
//con = DBUtils.getConnO2O();
con = Log4JDBCTest.getConnBM();
String sql = "select count(*) as count from ffm_user where "
+ "username = ? and password = ? ";
System.out.println("打印出来的SQL:" + sql);
PreparedStatement ps = con.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
int count = rs.getInt("count");
//不考虑密码加密的问题,如果在数据库中找到一个结果,那么就当密码正确
if (count > 0) {
return "success";
} else {
return "false";
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DbUtils.closeQuietly(con);
}
return "success";
}
}
5、启动应用,运行
输入:用户名:ffm,密码:1
打印出来的SQL:select count(*) as count from ffm_user where username = ? and password = ?
Log4jdbc打印出来的SQL:
select count(*) as count from ffm_user where username = ‘ffm’ and password = ‘1’ {executed in 147 msec}
是不是感觉很不错,确实满足了我们的需求
我们换一种输入吧。
输入:用户名:’ or 0=’0’– ,密码输入:2 或者其他任意
打印出来的SQL:select count(*) as count from ffm_user where username = ? and password = ?
Log4jdbc打印出来的SQL:
select count(*) as count from ffm_user where username = ‘’ or 0=’0’–’ and password = ‘2’ {executed in 331 msec}
看起来也很不错,但是这里有一个巨大的坑在里面。
仔细观察程序,输入:用户名:’ or 0=’0’– ,密码输入:2 是无法通过验证的,而 Log4jdbc打印出来的SQL,放在plsql中执行,是通过验证的。
也就是说,实际执行的SQL,不是我们所打印出来看到的。
6、结论
**Log4jdbc 只能打印PreparedStatement 最终执行的常规SQL,不能打印带有特殊字符的SQL。
使用PreparedStatement的参数的方法,是能够针对性的防御SQL注入的。**
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。