sql注入漏洞
数据库验证用户的功能
//处理界面
package com.xidian;
import javax.servlet.http.*;
import java.io.*;
import java.sql.*;
public class Logincl extends HttpServlet {
public void doGet(HttpServletRequest req,HttpServletResponse res){
Connection ct=null;
Statement sm=null;
ResultSet rs=null;
try{
//接收用户名和密码
String u=req.getParameter("username");
String p=req.getParameter("password");
//连接数据库
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
//得到连接
ct=DriverManager.getConnection("jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=test","sa","dba");
//创建Statement
sm=ct.createStatement();
//sql注入漏洞
rs=sm.executeQuery("select top 1* from users where username= '"+u+"'and passwd='"+p+"'");
//验证
if(rs.next()){
//合法
//将验证成功的信息,写入session
//1.得到session
HttpSession hs=req.getSession(true);
//修改session的存在时间
hs.setMaxInactiveInterval(10);
//2.向session添加属性
hs.setAttribute("pass","ok");
res.sendRedirect("Welcome?uname="+u+"&upass="+p);
}else{
//不合法
res.sendRedirect("login");//要到的servlet的那个url
}
}
catch(Exception ex){
ex.printStackTrace();
}finally{
try {
if(rs!=null){
rs.close();
}
if(rs!=null){
sm.close();
}
if(rs!=null){
ct.close();
}
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
public void doPost(HttpServletRequest req,HttpServletResponse res){
this.doGet(req,res);
}
}
如果在sqlserver输入命令:select * from users where username='任意字符' and passwd='任意字符' or 1='1'
都能查找出结果,因为or 1='1'语句使得条件都成立。
如果用户在输入密码:任意字符' or 1='1 都能通过验证。
sql注入就是本来我只有我能操作数据库,本来只是让你输入内容就走,而你却输入命令,从而在我不知情下操作数据库。
修改后代码:
rs=sm.executeQuery("select top 1 passwd from users where username= '"+u+"'");
if(rs.next()){
//说明用户名是存在
String dbPasswd=rs.getString(1);
if(dbPasswd.equals(p)){
//用户合法
HttpSession hs=req.getSession(true);
//修改session的存在时间
hs.setMaxInactiveInterval(10);
//2.向session添加属性
hs.setAttribute("pass","ok");
res.sendRedirect("Welcome?uname="+u+"&upass="+p);
}
}
解决方法:
1.使用程序控制。不要在sm.executeQuery()中同时查询用户名和密码来验证以带来注入风险,应该通过输入的用户名在数据库中查找密码,然后将数据库中对应的用户密码与用户输入的密码进行比对。
2.会产生上门面的情况是因为上面的sql是使用动态拼接的方式,所以sql传入的方式可能改变sql的语义。所以要使用preparedStatement的参数化sql,通过先确定语义,再传入参数,就不会因为传入的参数改变sql的语义。
PreparedStatement取代Statement。
那为什么它这样处理就能预防SQL注入提高安全性呢?
其实是因为SQL语句在程序运行前已经进行了预编译,在程序运行时第一次操作数据库之前,SQL语句已经被数据库分析,编译和优化,对应的执行计划也会缓存下来并允许数据库已参数化的形式进行查询,
当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or 1=1也数据库会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令,PreparedStatement不允许在插入时改变查询的逻辑结构.如此,就起到了SQL注入的作用了!
//建立数据连接 conn=ds.getConnection(); //1.设置prepareStatement带占位符的sql语句 PreparedStatement ptmt = conn.prepareStatement("select * from user where userName = ? and password = ?"); ptmt.setString(1, "张三"); //2.设置参数 ptmt.setString(2, "123456"); rs=ptmt.executeQuery(); while(rs.next()){ System.out.println("登陆成功"); return; } System.out.println("登陆失败");