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

时间: 2024-08-08 03:09:12

看好你的门-保护数据存储区(2)-查看PreparedStatement最终执行的SQL的相关文章

看好你的门-保护数据存储区(1)-SQL注入防御

首先需要声明,本文纯属一个毫无远见和真才实学的小小开发人员的愚昧见解,仅供用于web系统安全方面的参考. 1.常用的SQL注入防御的方法 一个能连接数据库的应用. 1.对用户端输入的数据进行严格防范: 2.使用PreparedStatement执行Sql语句: 3.不仅仅要在页面层面进行验证,在服务端层面还要同步进行这些验证: 2.使用正则表达式屏蔽特殊字符 使用SQL注入攻击多在特殊字符上下手脚,如"'","*","/" ,"–&qu

看好你的门-攻击数据存储区(5)-LDAP注入攻击

首先需要声明,本文纯属一个毫无远见和真才实学的小小开发人员的愚昧见解,仅供用于web系统安全方面的参考. 本文部分内容来自互联网和翻译 1.一些多余的话 LDAP注入和SQL注入,原理上非常相似 但是LDAP往往包含很多的数据,相对来说,危害性更加大. 2.一些案例 这些案例不一定是LDAP造成的,但是性质基本上差不多 WooYun: 腾讯某服务配置不当内部海量敏感信息泄露! http://www.wooyun.org/bugs/wooyun-2013-045626 WooYun: 腾讯某研发中

看好你的门-攻击数据存储区(4)-XPath注入攻击

首先需要声明,本文纯属一个毫无远见和真才实学的小小开发人员的愚昧见解,仅供用于web系统安全方面的参考. 1.一些多余的话 XPath注入和SQL注入,原理上非常相似 但是XPath注入的对象主要是XML,相对来说,危害性更加大 2.保存用户信息的XML <?xml version="1.0" encoding="utf-8" ?> <root> <user> <id>1</id> <username

选定的数据存储区出现问题,原因可能是服务器名称或凭据无效,或者权限不足

问题: ASP.NET Configuration site->安全,报错 "选定的数据存储区出现问题,原因可能是服务器名称或凭据无效,或者权限不足" 解决: 打开文件C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config 找到 <connectionStrings> <add name="LocalSqlServer" connectionString=&quo

【JDK源码分析】String的存储区与不可变性(转)

// ... literals are interned by the compiler // and thus refer to the same object String s1 = "abcd"; String s2 = "abcd"; s1 == s2; // --> true // ... These two have the same value // but they are not the same object String s1 = new

单片机中C语言的程序与数据存储

单片机中C语言的程序与数据存储 一.五大内存分区: 内存分成5个区,它们分别是堆.栈.自由存储区.全局/静态存储区和常量存储区. 1.栈区(stack):FIFO就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区.里面的变量通常是局部变量.函数参数等. 2.堆区(heap):就是那些由new分配的内存块,它们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete.如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收. 3.自由存储区:就是那些

android的数据存储方式

数据存储在开发中是使用最频繁的,Android平台中实现数据存储主要有5种方式,分别是: 1 SQLite: SQLite是一个轻量级嵌入式数据库,支持基本SQL语法,是常被采用的一种数据存储方式.Android为此数据库提供了一个名为SQLiteDatabase的类,封装了一些操作数据库的API. 2 SharedPreference:SharedPreferences也是一种轻型的数据存储方式,它的本质是基于XML文件存储key-value键值对数据,通常用来存储一些简单的配置信息.其存储位

C++数据存储方式

1.栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区,里面的变量通常是局部变量.函数参数等. 2.堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete.如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收. 3.全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区. 4.常量存储区,这是

看好你的门-客户端传数据(2)-URL参数

首先需要声明,本文纯属一个毫无远见和真才实学的小小开发人员的愚昧见解,仅供用于web系统安全方面的参考. 1. 简单说明 应用程序通常以终端用户无法直接查看或者修改的方式向服务器传送数据.很多的时候,开发者都优先考虑实现基本效果,而很少去考虑我们所采用的传输机制能够确保数据在传输过程中不会被修改. 在互联网中,大量的数据通过URL参数的方式进行传递,大部分的数据,是没有通过加密进行传输.在我所了解到的情况,大部分的数据是通过明码进行- 2. 优点: 不用追踪用户会话中的数据,减少保持在服务器上的