当前位置: 代码迷 >> SQL >> 看好您的门-保护数据存储区(2)-查看PreparedStatement最终执行的SQL
  详细解决方案

看好您的门-保护数据存储区(2)-查看PreparedStatement最终执行的SQL

热度:80   发布时间:2016-05-05 10:42:49.0
看好你的门-保护数据存储区(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注入的。**

  相关解决方案