看好你的门-保护数据存储区(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注入的。**

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。