首页 > 学院 > 开发设计 > 正文

ASP.NET中的SQL注入攻击与防护

2019-11-17 01:26:59
字体:
来源:转载
供稿:网友

asp.net中的SQL注入攻击与防护

什么是SQL注入攻击?

它是在执行SQL查询的时候,由于接收了用户的非法参数从而导致,所执行的SQL语义与业务逻辑原本所要查询的语义不相符,从而实现的攻击。

例如我们经常使用的用户登录,通常会出现这样的表单:

用户名:________________

密码:________________

登录

正常情况下,我们需要让用户填写他们自己的用户名和密码之后,程序会接收用户输入的参数 执行查询操作,然后根据查询结果,判断用户是否能够登录。

但是,如果程序员在编写SQL查询操作时候,没有注意SQL注入问题的话,那么用户可以通过这个表单很轻易的实现一些非正常的访问,下面我们据具体的例子来说明。

我们先来布局一个简单的表单:

其中这个表单中包含了用户名 密码的输入文本框,为了大家便于观看,密码我没有使用passWord,明文显示。其中防止注入的复选框是为了我写第二段 防止注入的登录逻辑时,可以用一个按钮来表示,而用了单选框是否被选中来进行区别。

我们先来看第一段登录代码:

 1     //声明连接 2     PRivate static OleDbConnection ConnAcc; 3     protected void Page_Load(object sender, EventArgs e) 4     { 5  6     } 7  8     /// <summary> 9     /// 按钮点击事件10     /// </summary>11     /// <param name="sender"></param>12     /// <param name="e"></param>13     protected void Button1_Click(object sender, EventArgs e)14     {15         string name = tbx_name.Text;16         string pwd = tbx_pwd.Text;17 18         UserLogin(name, pwd);19     }20 21 22 23     /// <summary>24     /// 第一种登录验证操作25     /// </summary>26     /// <param name="_loginname"></param>27     /// <param name="_loginpwd"></param>28     private void UserLogin(string _loginname,string _loginpwd)29     {30         DataSet ds = new DataSet(); //声明数据集31         string sql = "select * from Users Where rg_LoginName='" + _loginname + "' and rg_LoginPwd='" + _loginpwd + "'"; //创建查询语句32         try33         {34 35             Open(); //打开连接36             OleDbCommand comm = new OleDbCommand(sql, ConnAcc); //创建查询37             new OleDbDataAdapter(comm).Fill(ds, 0, 1, "Users"); //填充数据集38         }39         catch (OleDbException exception)40         {41             ds = null;42             Label1.Text = exception.Message; //异常处理43             return;44         }45         finally46         {47             Free();48         }49         if (ds != null && ds.Tables[0].Rows.Count > 0) //如果数据集中有记录 代表输入的用户名密码组合正确50         {51             Label1.Text = "用户[" + tbx_name.Text + "]登录成功!"; //显示52 53             //int uid = int.Parse(ds.Tables[0].Rows[0]["rg_ID"].ToString());54             //保存UID等用户信息到session或者cookies中55             //由于本案例重点是登录身份验证通过环节,因此此处逻辑可以不继续写56         }57         else58         {59             Label1.Text = "用户名或密码错误";60         }61     }62 63 64 65 66     /// <summary>67     /// 打开数据库连接68     /// </summary>69     private static void Open()70     {71         ConnAcc = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + HttpContext.Current.Server.MapPath("~/App_Data/db.mdb") + ";");72         if (ConnAcc.State == ConnectionState.Closed)73         {74             ConnAcc.Open();75         }76     }77 78 79     /// <summary>80     /// 关闭连接81     /// </summary>82     private static void Free()83     {84         if (ConnAcc != null)85         {86             ConnAcc.Close();87             ConnAcc.Dispose();88             ConnAcc = null;89         }90     }

我在数据库中添加了一条记录 用户名为admin,密码为123456

然后我们来看一下登录的实际效果:

OK!登录成功!看起来一切都很圆满,但是事实上是这样吗?聪明的你一定会想到,如果就此结束了,那本文就毫无意义了,而且这种登录我刚上学的时候就会了。

没错,这只是看上去功能实现了而已,但事实上,这种登录的判断逻辑存在着很大的SQL注入风险,下面我就以此段代码为例,演示一个简单的SQL注入的经典例子。

刚才我们登录的时候是输入的用户名密码,密码如果与用户名不匹配就无法登录成功,那么当我不知道密码的情况下,试试下面的这个组合:

看到这里你貌似瞬间懂了,然后惊出一身冷汗,然后背后隐隐发凉,为什么不输入密码也能登录成功?那么我在程度代码这段加上一个断点调试一下相比你就明白了。

在填充数据集之前,加入断点,让程序运行时暂停在这个位置。

由于我输入的用户名为:admin,密码并没有输入正确的123456,而是输入的 ' or '1'='1 这意味这什么呢?请看下面的变量跟踪信息:

这是SQL语句变成了select * from Users Where rg_LoginName='admin' and rg_LoginPwd='' or '1'='1'

我们查询原本的意思是:select * from Users Where rg_LoginName=用户名 and rg_LoginPwd=密码

而现在的意思就变成了 select * from Users Where rg_LoginName='admin' and rg_LoginPwd=空 or '1'='1'

简单的说就是相当于用户通过输入的密码中带有SQL语义的值,变相修改了我们的查询语句,在本例中,无论用户是否知道密码,他只要在密码中输入' or '1'='1',就一定能够登录成功,因为有了 or '1'='1'的恒成立,所以前面的判断语句统统失效。这就是非常典型的SQL注入攻击,通过此种方法,可以进入后台,篡改数据等操作。

除此以外,包括页面间传参,搜索的时候都有可能被多种形式的注入,这是一个相当危险的安全隐患,使你得所有用户身份验证形同虚设。

那么如何避免SQL语句的注入呢?

一个最简单快捷的方法就是过滤单引号,我们来修改登录的代码

 1     /// <summary> 2     /// 第一种登录验证操作 3     /// </summary> 4     /// <param name="_loginname"></param> 5     /// <param name="_loginpwd"></param> 6     private void UserLogin(string _loginname,string _loginpwd) 7     { 8         _loginname = _loginname.Replace("'", ""); //过滤用户输入参数中的单引号 9         _loginpwd = _loginpwd.Replace("'", ""); //过滤用户输入参数中的单引号10 11         DataSet ds = new DataSet(); //声明数据集12         string sql = "select * from Users Where rg_LoginName='" + _loginname + "' and rg_LoginPwd='" + _loginpwd + "'"; //创建查询语句13         try14         {15 16             Open(); //打开连接17             OleDbCommand comm = new OleDbCommand(sql, ConnAcc); //创建查询18             new OleDbDataAdapter(comm).Fill(ds, 0, 1, "Users"); //填充数据集19         }20         catch (OleDbException exception)21         {22             ds = null;23             Label1.Text = exception.Message; //异常处理24             return;25         }26         finally27         {28             Free();29         }30         if (ds != null && ds.Tables[0].Rows.Count > 0) //如果数据集中有记录 代表输入的用户名密码组合正确31         {32             Label1.Text = "用户[" + tbx_name.Text + "]登录成功!"; //显示33 34             //int uid = int.Parse(ds.Tables[0].Rows[0]["rg_ID"].ToString());35             //保存UID等用户信息到Session或者cookies中36             //由于本案例重点是登录身份验证通过环节,因此此处逻辑可以不继续写37         }38         else39         {40             Label1.Text = "用户名或密码错误";41         }42     }

通过把用户输入的单引号进行过滤,那么他在输入中所带的SQL语义的值 就不成立了,

我们继续使用刚才的组合:admin,' or '1'='1 来断点查看下变量:

这个时候我们可以发现,rg_LoginPwd=' or 1=1' 在把用户输入的单引号过滤掉之后 用户输入的密码就是 or 1=1(被单引号括起来,被当作字符串处理)不具有任何的SQL语义了,当然这个密码是不正确的,因此就不会出现SQL注入的问题了

然而利用过滤单引号的方式,只能在一定程度上避免SQL注入的攻击,却并不能100%的保证安全,最安全的做法就是使用微软提供的参数数组的形式进行提交命令。下面我们再来写一个登录的方法。

 1 /// <summary> 2     /// 第二种登录验证操作(参数数组形式) 3     /// </summary> 4     /// <param name="_loginname"></param> 5     /// <param name="_loginpwd"></param> 6     private void UserLoginSafeMode(string _loginname, string _loginpwd) 7     { 8         DataSet ds = new DataSet(); 9         string sql = "select * from Users Where rg_LoginName=@rg_LoginName and rg_LoginPwd=@rg_LoginPwd"; //创建查询语句10 11         //创建参数数组12         OleDbParameter[] parameters ={13             new OleDbParameter("@rg_LoginName",OleDbType.VarChar),14             new OleDbParameter("@rg_LoginPwd",OleDbType.VarChar)15         };16 17         //参数数组赋值18         parameters[0].Value = _loginname;19         parameters[1].Value = _loginpwd;20 21         try22         {23             Open();24             OleDbCommand comm = new OleDbCommand(sql, ConnAcc);25             if (parameters != null)26             {27                 //向comm中插入参数28                 foreach (OleDbParameter p in parameters)29                 {30                     comm.Parameters.Add(p);31                 }32             }33             new OleDbDataAdapter(comm).Fill(ds, 0, 1, "Users");34         }35         catch (OleDbException exception)36         {37             ds = null;38             Label1.Text = exception.Message; //异常处理39             return;40         }41         finally42         {43             Free();44         }45         if (ds != null && ds.Tables[0].Rows.Count > 0) //如果数据集中有记录 代表输入的用户名密码组合正确46         {47             Label1.Text = "用户[" + tbx_name.Text + "]登录成功!"; //显示48 49             //int uid = int.Parse(ds.Tables[0].Rows[0]["rg_ID"].ToString());50
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表