挖藕!模拟sql查看qq密码
模拟抓取到的qq数据
CREATE DATABASE `jdbcStudy` CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `jdbcStudy`;
CREATE TABLE `users`(
`id` INT PRIMARY KEY,
`NAME` VARCHAR(40),
`PASSWORD` VARCHAR(40),
`email` VARCHAR(60),
birthday DATE
);
INSERT INTO `users`(`id`,`NAME`,`PASSWORD`,`email`,`birthday`)
VALUES(1,猪猪侠,123456,zs@sina.com,1980-12-04),
(2,lisi,郭为,lisi@sina.com,1981-12-04),
(3,jbman,123456,wangwu@sina.com,1979-12-04)
结果(修改部分后)
通过封装实现数据库的连接
一般我们通过jdbc去操作数据库时是通过,加载驱动,连接数据库,通过数据库获得sql对象,再利用sql对象执行sql语句
但如果每次都需首先连接数据库代码将大大增加代码量,这里我们封装一个实现数据库连接的类来实现耦合
1. 创建配置文件(包含驱动,url 密码,用户名)
# properties 属性,特性
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false
username=root
password=123456
2. 实现封装工具类(涉及类加载机制复习)
封装工具类来实现 加载驱动,获取连接数据库,释放连接。(以上都是每次不变的操作) I/O
注:这里涉及到 Properties类的使用,遗忘建议复习
package com.lesson02.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
//封装工具类来实现 加载驱动,获取连接数据库,释放连接。(以上都是每次不变的操作) I/O
public class jdbcutils {
//提升作用域
private static String driver=null;
private static String url=null;
private static String username=null;
private static String password=null;
static{
try{
//从配置文件中读取
InputStream in = jdbcutils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
//读取到properties对象中
properties.load(in);
driver= properties.getProperty("driver");
username=properties.getProperty("username");
url=properties.getProperty("url");
password=properties.getProperty("password");
//驱动只需加载一次
Class.forName(driver);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
//释放连接资源
public static void release(Connection connection, Statement statement, ResultSet resultSet) throws SQLException {
if (resultSet!=null){
resultSet.close();
}
if (statement!=null){
statement.close();
}
if (connection!=null){
connection.close();
}
}
}
3. 利用封装工具类实现插入数据
代码实现:利用封装工具类,通过反射利用工具类中方法,实现将数据插入数据库表
package com.lesson02.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Testinsert {
public static void main(String[] args) throws SQLException {
Connection connection=null;
Statement statement=null;
ResultSet set=null;
try {
connection=jdbcutils.getConnection();//通过反射获取方法:获取数据库连接
statement = connection.createStatement();
String sql="INSERT INTO `users`(id,`NAME`,`PASSWORD`,`email`,`birthday`)"+
"VALUES(6,'asd','123456','1231421@qq.com','2000-01-02')";
int i = statement.executeUpdate(sql);
if (i>0){
System.out.println("插入成功 !");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//最后释放资源
jdbcutils.release(connection,statement,set);
}
}
}
ps:之后的删除查找操作只需修改sql语句即可
sql注入查看用户qq密码
由于以上sql存在漏洞,会被攻击导致泄露,
实际上就是 SQL字符串可以与or拼接
在用statement对象执行sql语句时,会存在sql注入问题,(对用户输入数据的合法性没有判断或过滤不严)
例如
Name = " or 1=1 " --------->结果为true会查询所有结果
这样我们就可以由此得到所有人的密码了!!!哈哈哈
在通过执行Select时(这是我们不知道数据库表中的密码列)
我们可以通过利用sql注入缺陷来实现查表中的密码
这是我们乱输入的密码 :5sa6
再利用or 拼接即可查询道全部数据
login(" asdsa 'or' 1=1","5sa6 'or' 1=1"); //乱输入的
package com.lesson02.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class SQL注入 {
public static void main(String[] args) throws SQLException {
//login("zhangsan","123456");
login(" asdsa 'or' 1=1","5sa6 'or' 1=1"); //乱输入的
}
public static void login(String username,String password) throws SQLException {
Connection connection=null;
Statement statement=null;
ResultSet set=null;
try {
connection=jdbcutils.getConnection();//通过反射获取方法:获取数据库连接
statement = connection.createStatement();
String sql="SELECT *FROM `users` WHERE `NAME`='"+username+"'AND `PASSWORD`='"+password+"'";
ResultSet set1 = statement.executeQuery(sql);
while (set1.next()){
System.out.println("抓取"+set1.getObject("NAME"));
System.out.println("的密码是"+set1.getObject("PASSWORD"));
System.out.println("================");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//最后释放资源
jdbcutils.release(connection,statement,set);
}
}
}
最后的结果(密码到手了哈哈哈!!!!!!!!!!!!!!!)
漏洞的解决
解决方法:使用preparestatement来执行sql,更安全
PreparedStatement对象
PreparedStatement :防止SQL注入,效率更高
与statement的区别:后者并非直接传参,而是通过set方法进行传参,sql语句中的参数用 ?通配符
prepareStatement 防止SQL注入的本质是,将传递进来的参数当作字符
假设其中存在转义字符,比如说 ’ 会被直接转义
我们可以看下mysql的底层的setString();方法,其实就是用值代替了?的位置,并且在值的两边加上了单引号,然后再把值当中的所有单引号替换成了斜杠单引号,说白了就是把值当中的所有单引号给转义了!这就达到了防止sql注入的目的
package com.lesson02.utils;
import java.sql.*;
public class Testinsert {
public static void main(String[] args) throws SQLException {
Connection connection=null;
PreparedStatement pst=null;//源码发现prestatement为其子类,传参方式不同
try {
connection=jdbcutils.getConnection();
// 使用?占位符替代参数
String sql="insert into `users` ('id','NAME','PASSWORD',`email`,`birthday`)values(?,?,?,?,?)";
pst=connection.prepareStatement(sql);//需预编译
//手动给参数赋值
pst.setInt(1,4);
pst.setString(2,"xue");
pst.setString(3,"123456");
pst.setString(4,"1223@ad.com");
//注意点: sql.date :数据库的时间
// util.date :java中的
pst.setDate(5,new Date(new java.util.Date().getTime()));
int i = pst.executeUpdate();
if (i>0){
System.out.println("插入成功 !");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//最后释放资源
jdbcutils.release(connection,null,null);
}
}
}
这样,我们就无法随意抓取数据库中的字段了(555555555555555)