什么是SQL注入攻击?html
它是在执行SQL查询的时候,因为接收了用户的非法参数从而致使,所执行的SQL语义与业务逻辑本来所要查询的语义不相符,从而实现的攻击。程序员
例如咱们常用的用户登陆,一般会出现这样的表单:web
用户名:________________sql
密 码:________________数据库
登陆数组
正常状况下,咱们须要让用户填写他们本身的用户名和密码以后,程序会接收用户输入的参数 执行查询操做,而后根据查询结果,判断用户是否可以登陆。安全
可是,若是程序员在编写SQL查询操做时候,没有注意SQL注入问题的话,那么用户能够经过这个表单很轻易的实现一些非正常的访问,下面咱们据具体的例子来讲明。cookie
咱们先来布局一个简单的表单:ide
其中这个表单中包含了用户名 密码的输入文本框,为了你们便于观看,密码我没有使用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 try 33 { 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 finally 46 { 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 else 58 { 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 try 14 { 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 finally 27 { 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 else 39 { 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 try 22 { 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 finally 42 { 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 //保存UID等用户信息到Session或者cookies中 51 //因为本案例重点是登陆身份验证经过环节,所以此处逻辑能够不继续写 52 } 53 else 54 { 55 Label1.Text = "用户名或密码错误"; 56 } 57 }
你必定会细心的注意到,与第一个登陆方法不一样的地方时,在声明了sql语句以后,又声明了一个参数数组,而且赋值,在comm中遍历插入。经过这样的方式,咱们无需过滤单引号,参数数组会把那些带有语义的SQL语句看成字符串值来处理,从而绝对的从根本上避免了SQL注入攻击
OK,关于SQL注入攻击与防范就说到这里,关于此类话题还有不少不少,不过咱们只要知道使用参数数组的方式提交查询命名就能够从根本上避免此类问题的发生,具体的其余关于SQL注入的问题,感兴趣的同窗能够自行在网上查找资料,有什么疑问欢迎留言
本文的DEMO下载:http://files.cnblogs.com/webconfig/SQL_Injection_Attack_DEMO.rar
本文出自 低调码农的笔记簿 http://www.cnblogs.com/webconfig/p/3622498.html 转载请注明出处,若有谬误不当之处,欢迎指正拍砖,不胜感谢!!