ASP.NET的SQL注入攻击与预防 使用SQLServer
前言
本文通过一个以用户登录为例的示例项目,讲述了SQL注入攻击与预防。
示例项目使用了VS2017与SqlServer2008r2,采用WebAPI的架构。
下面让我们从头开始。
开始
Step.1 新建数据库表
在Test数据库下新建users表,含有3个字段(id,uid,pwd). 并添加一条数据。
Step.2 新建ASP.NET项目
打开 VS2017,选择【文件】->【新建】 ->【项目】,并在【新建项目】 窗口中选择 Visual C# -> Web -> ASP.NET Web应用程序, 点击确定。
在弹出窗口中选择空模板,并添加 Web API的核心引用,点击确定。
在解决方案资源管理器中,打开App_Start/WebApiConfig.cs,在routeTemplate中添加action段:
routeTemplate: "api/{controller}/{action}/{id}",
打开Web.config,添加connectionString[name=‘db’],用来连接数据库。
选择 【项目】 -> 【管理nuget程序包】->下载安装Jquery.
在解决方案资源管理器中,右键点击Controllers文件夹,依次选择【添加】->【控制器】->【Web API 2控制器-空】,点击添加,取名为UserController . 将UserController.cs中的代码替换为下面代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Data;
using System.Data.SqlClient;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Configuration;
namespace SQLTest.Controllers
{
public class UserController : ApiController
{
public JObject Login([FromBody]JObject requestJson)
{
JObject respJson = new JObject();
string uid = requestJson["uid"].ToString();
string pwd = requestJson["pwd"].ToString();
string conStr = ConfigurationManager.ConnectionStrings["db"].ToString();
SqlConnection con = new SqlConnection(conStr);
con.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = $"select * from users where uid='{uid}' and pwd='{pwd}'";
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
if (dt.Rows.Count == 1) respJson.Add("success", "true");
else respJson.Add("success", "false");
return respJson;
}
}
}
添加Html页面,并将代码替换为如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
uid:<input id="uid" type="text" /><br />
pwd:<input id="pwd" type="text" /><br />
<input type="button" id="submit" value="login" onclick="login()"/><br />
<script src="Scripts/jquery-3.3.1.min.js"></script>
<script>
function login() {
var postData = {
uid: $("#uid").val(),
pwd: $("#pwd").val()
};
var url = "api/User/Login";
$.post(url, postData, function (resp) {
if (resp.success == "true") {
alert("login ok");
}
else {
alert("err");
}
});
}
</script>
</body>
</html>
至此,项目新建完毕。解决方案目录如下:
Step.3 Sql注入攻击示例
F5运行项目。
首先进行正常登录。uid输入wufan, pwd输入qwe123后,点击login按钮:
可以看到登录正常。
下面我们模拟正常情况下的密码错误。将pwd填入qwe1234,点击login按钮,可以看到登录失败。
接下来进行Sql注入攻击。在uid处填入wufan’-- ,pwd仍然使用上面的错误密码,点击login按钮后,可以看到竟然也登录成功了。
这就是sql注入攻击。恶意添加单引号来提前关闭sql语句中的uid参数,并使用两个杠将接下来的东西都注释掉,使得实际查询的sql语句变成:
select * from users where uid='wufan'-- and pwd='qwe1234'
两个杠后面的代码被注释后,实际sql为:
select * from users where uid='wufan'
在这种情况下,密码随便填也能登录成功。
Step.4 Sql注入的预防
第一种方法,就是在前后端对用户输入进行校验,比如不允许输入特殊字符,或者将用户输入的特殊字符进行转义等。
第二种方法,是使用 SqlCommand.Parameters . 将UserController.cs/Login方法改为下面代码即可。
public JObject Login([FromBody]JObject requestJson)
{
JObject respJson = new JObject();
string uid = requestJson["uid"].ToString();
string pwd = requestJson["pwd"].ToString();
string conStr = ConfigurationManager.ConnectionStrings["db"].ToString();
SqlConnection con = new SqlConnection(conStr);
con.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = "select * from users where [email protected] and [email protected]";
cmd.Parameters.Add("@uid",SqlDbType.NVarChar,50);
cmd.Parameters.Add("@pwd",SqlDbType.NVarChar, 50);
cmd.Parameters["@uid"].Value = uid;
cmd.Parameters["@pwd"].Value = pwd;
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
if (dt.Rows.Count == 1) respJson.Add("success", "true");
else respJson.Add("success", "false");
return respJson;
}
运行项目后,重复Step3中的sql注入攻击,可以看到登录失败,符合预期。