什么是SQL注入,简单的说就是我只需要知道任意一个用户名而不需要知道密码就可以登录进系统,一般都发生在登录界面
1.SqlServer数据库的设计
CREATE TABLE userTable
(
id int identity(1,1) primary key,
username varchar(20),
password varchar(20)
)
insert into userTable(username,password) values('zhangsan','123456')
insert into userTable(username,password) values('lisi','654321')
2.前端设计
为了方便演示,密码框也是文本输入样式
<form id="form1" runat="server">
用户名:<input type="text" name="username"/><br />
密 码:<input type="text" name="password"/><br />
<input type="submit" value="提交"/>
</form>
3.后台设计
后台只有在拼接SQL参数的时候才有可能发生SQL注入,所以后台会分2种情况演示:一种是拼接式sql,另一种是参数化查询sql。还有一点就是SQL注入的情况不仅仅发生在后台语言向数据库发起sql命令的时候,sqlServer中也是可以完成sql注入的,后面会演示
SqlConnection sqlConnection;
protected void Page_Load(object sender, EventArgs e)
{
if(IsPostBack)
{
String username = Request["username"];
String password = Request["password"];
if(sqlConnection==null)
sqlConnection = new SqlConnection("Server=.;user=sa;password=guobeizxc;database=lawnet");
//这里使用拼接的方式编写sql
String sql = "SELECT * FROM userTable WHERE username='" + username + "' AND password='" + password+"'";
SqlCommand sqlCommand = new SqlCommand(sql, sqlConnection);
sqlCommand.CommandType = System.Data.CommandType.Text;
DataSet ds = new DataSet();
SqlDataAdapter sda = new SqlDataAdapter(sqlCommand);
sda.Fill(ds);
//如果这个dataset里面有行,说明数据库返回了数据,即表示登录成功
if(ds.Tables[0].Rows.Count>0)
{
Response.Write("<script type='text/javascript'>alert('登录成功')</script>");
}
else
{
Response.Write("<script type='text/javascript'>alert('登录失败')</script>");
}
}
}
演示:
所以在后台使用拼接式sql很容易导致这种问题发生,目前主流的形势都是用的参数化查询,后台可以修改一下
SqlConnection sqlConnection;
protected void Page_Load(object sender, EventArgs e)
{
if(IsPostBack)
{
String username = Request["username"];
String password = Request["password"];
if(sqlConnection==null)
sqlConnection = new SqlConnection("Server=.;user=sa;password=guobeizxc;database=lawnet");
//这里使用拼接的方式编写sql
//String sql = "SELECT * FROM userTable WHERE username='" + username + "' AND password='" + password+"'";
//使用参数化查询
String sql = "SELECT * FROM userTable WHERE username=@username AND password=@password";
SqlCommand sqlCommand = new SqlCommand(sql, sqlConnection);
sqlCommand.CommandType = System.Data.CommandType.Text;
SqlParameter[] sqlParameters = { new SqlParameter("@username", username), new SqlParameter("@password", password) };
sqlCommand.Parameters.AddRange(sqlParameters);
DataSet ds = new DataSet();
SqlDataAdapter sda = new SqlDataAdapter(sqlCommand);
sda.Fill(ds);
//如果这个dataset里面有行,说明数据库返回了数据,即表示登录成功
if(ds.Tables[0].Rows.Count>0)
{
Response.Write("<script type='text/javascript'>alert('登录成功')</script>");
}
else
{
Response.Write("<script type='text/javascript'>alert('登录失败')</script>");
}
}
}
测试
这样就避免了SQL注入的风险,现在在数据库中演示,先看一下最终的sql长什么样子
declare @userid varchar(20),
@password varchar(20),
@sql nvarchar(max)
set @userid='''zhangsan'' or 1=1 --'
set @password='123456789'
set @sql='select * from userTable where username='+@userid +'and password='''+@password+''''
print @sql
看上去应该可以注入成功,现在使用sp_executesql执行一下看看
exec sp_executesql @sql
可以看出这是注入成功了,其实使用sp_executesql相当于在后台动态拼sql一样,sql查询编辑器会完完整整的执行,再换一种方式执行,下面这种方式相当于参数化查询sql,是可以避免注入的
declare @userid varchar(20),
@password varchar(20),
@sql nvarchar(max)
set @userid='''zhangsan'' or 1=1 --'
set @password='123456789'
select * from userTable where username=@userid and password=@password
sql注入的基本演示就是这些,读者也别浪费时间去各个网站去尝试,反正我是不太相信可以注入成功的。虽然现在的软件工程师水平良莠不齐,但现在基本上都是使用框架开发,一些基本的安全性问题框架都早就已经做好了,不太可能会出现这种低级错误。