什么是SQL注入攻击?

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

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

用户名:________________

密   码:________________

登录

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

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

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

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

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

     //声明连接
private static OleDbConnection ConnAcc;
protected void Page_Load(object sender, EventArgs e)
{ } /// <summary>
/// 按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Button1_Click(object sender, EventArgs e)
{
string name = tbx_name.Text;
string pwd = tbx_pwd.Text; UserLogin(name, pwd);
} /// <summary>
/// 第一种登录验证操作
/// </summary>
/// <param name="_loginname"></param>
/// <param name="_loginpwd"></param>
private void UserLogin(string _loginname,string _loginpwd)
{
DataSet ds = new DataSet(); //声明数据集
string sql = "select * from Users Where rg_LoginName='" + _loginname + "' and rg_LoginPwd='" + _loginpwd + "'"; //创建查询语句
try
{ Open(); //打开连接
OleDbCommand comm = new OleDbCommand(sql, ConnAcc); //创建查询
new OleDbDataAdapter(comm).Fill(ds, , , "Users"); //填充数据集
}
catch (OleDbException exception)
{
ds = null;
Label1.Text = exception.Message; //异常处理
return;
}
finally
{
Free();
}
if (ds != null && ds.Tables[].Rows.Count > ) //如果数据集中有记录 代表输入的用户名密码组合正确
{
Label1.Text = "用户[" + tbx_name.Text + "]登录成功!"; //显示 //int uid = int.Parse(ds.Tables[0].Rows[0]["rg_ID"].ToString());
//保存UID等用户信息到Session或者cookies中
//由于本案例重点是登录身份验证通过环节,因此此处逻辑可以不继续写
}
else
{
Label1.Text = "用户名或密码错误";
}
} /// <summary>
/// 打开数据库连接
/// </summary>
private static void Open()
{
ConnAcc = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + HttpContext.Current.Server.MapPath("~/App_Data/db.mdb") + ";");
if (ConnAcc.State == ConnectionState.Closed)
{
ConnAcc.Open();
}
} /// <summary>
/// 关闭连接
/// </summary>
private static void Free()
{
if (ConnAcc != null)
{
ConnAcc.Close();
ConnAcc.Dispose();
ConnAcc = null;
}
}

我在数据库中添加了一条记录 用户名为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>
/// 第一种登录验证操作
/// </summary>
/// <param name="_loginname"></param>
/// <param name="_loginpwd"></param>
private void UserLogin(string _loginname,string _loginpwd)
{
_loginname = _loginname.Replace("'", ""); //过滤用户输入参数中的单引号
_loginpwd = _loginpwd.Replace("'", ""); //过滤用户输入参数中的单引号 DataSet ds = new DataSet(); //声明数据集
string sql = "select * from Users Where rg_LoginName='" + _loginname + "' and rg_LoginPwd='" + _loginpwd + "'"; //创建查询语句
try
{ Open(); //打开连接
OleDbCommand comm = new OleDbCommand(sql, ConnAcc); //创建查询
new OleDbDataAdapter(comm).Fill(ds, , , "Users"); //填充数据集
}
catch (OleDbException exception)
{
ds = null;
Label1.Text = exception.Message; //异常处理
return;
}
finally
{
Free();
}
if (ds != null && ds.Tables[].Rows.Count > ) //如果数据集中有记录 代表输入的用户名密码组合正确
{
Label1.Text = "用户[" + tbx_name.Text + "]登录成功!"; //显示 //int uid = int.Parse(ds.Tables[0].Rows[0]["rg_ID"].ToString());
//保存UID等用户信息到Session或者cookies中
//由于本案例重点是登录身份验证通过环节,因此此处逻辑可以不继续写
}
else
{
Label1.Text = "用户名或密码错误";
}
}

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

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

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

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

 /// <summary>
/// 第二种登录验证操作(参数数组形式)
/// </summary>
/// <param name="_loginname"></param>
/// <param name="_loginpwd"></param>
private void UserLoginSafeMode(string _loginname, string _loginpwd)
{
DataSet ds = new DataSet();
string sql = "select * from Users Where rg_LoginName=@rg_LoginName and rg_LoginPwd=@rg_LoginPwd"; //创建查询语句 //创建参数数组
OleDbParameter[] parameters ={
new OleDbParameter("@rg_LoginName",OleDbType.VarChar),
new OleDbParameter("@rg_LoginPwd",OleDbType.VarChar)
}; //参数数组赋值
parameters[].Value = _loginname;
parameters[].Value = _loginpwd; try
{
Open();
OleDbCommand comm = new OleDbCommand(sql, ConnAcc);
if (parameters != null)
{
//向comm中插入参数
foreach (OleDbParameter p in parameters)
{
comm.Parameters.Add(p);
}
}
new OleDbDataAdapter(comm).Fill(ds, , , "Users");
}
catch (OleDbException exception)
{
ds = null;
Label1.Text = exception.Message; //异常处理
return;
}
finally
{
Free();
}
if (ds != null && ds.Tables[].Rows.Count > ) //如果数据集中有记录 代表输入的用户名密码组合正确
{
Label1.Text = "用户[" + tbx_name.Text + "]登录成功!"; //显示 //int uid = int.Parse(ds.Tables[0].Rows[0]["rg_ID"].ToString());
//保存UID等用户信息到Session或者cookies中
//由于本案例重点是登录身份验证通过环节,因此此处逻辑可以不继续写
}
else
{
Label1.Text = "用户名或密码错误";
}
}

你一定会细心的注意到,与第一个登录方法不同的地方时,在声明了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 转载请注明出处,如有谬误不当之处,欢迎指正拍砖,不胜感谢!!

ASP.NET中的SQL注入攻击与防护的更多相关文章

  1. 教你ASP.NET中如何防止注入攻击

    你应该在程序中验证所有的不信任输入.你应该假定所有的用户输入都是非法的.用户可以在应用程序中提供表单字段,查询字串,客户端cookies和浏览器环境值比如用户代理字串和IP地址等. 弱输入校验通常为注 ...

  2. Java应用开发中的SQL注入攻击

    1. 什么是SQL注入攻击? SQL注入攻击是黑客对数据库进行攻击的常用手段之一.随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员越来越多.但是由于程序员的水平及经验参差不齐,相当一部分 ...

  3. 实例讲解 SQL 注入攻击

    这是一篇讲解SQL注入的实例文章,一步一步跟着作者脚步探索如何注入成功,展现了一次完整的渗透流程,值得一读.翻译水平有限,见谅! 一位客户让我们针对只有他们企业员工和顾客能使用的企业内网进行渗透测试. ...

  4. 在php中防止SQL注入的方法

    摘要:我们php手手工安装的,php的默认配置文件在 /usr/local/apache2/conf/php.ini,我们最主要就是要配置php.ini中的内容,让我们执行 php能够更安全.整个PH ...

  5. Java程序员从笨鸟到菜鸟之(一百零一)sql注入攻击详解(二)sql注入过程详解

    在上篇博客中我们分析了sql注入的原理,今天我们就来看一下sql注入的整体过程,也就是说如何进行sql注入,由于本人数据库和网络方面知识有限,此文章是对网上大量同类文章的分析与总结,其中有不少直接引用 ...

  6. 转:PHP中防止SQL注入的方法

    [一.在服务器端配置] 安全,PHP代码编写是一方面,PHP的配置更是非常关键. 我们php手手工安装的,php的默认配置文件在 /usr/local/apache2/conf/php.ini,我们最 ...

  7. 防止SQL注入攻击的一些方法小结

    SQL注入攻击的危害性很大.在讲解其防止办法之前,数据库管理员有必要先了解一下其攻击的原理.这有利于管理员采取有针对性的防治措施. 一. SQL注入攻击的简单示例. statement := &quo ...

  8. SQL注入攻击

    SQL注入攻击是黑客对数据库进行攻击的常用手段之一.随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多.但是由于程序员的水平及经验也参差不齐,相当大一部分程序员在编写代码的时候, ...

  9. SQL注入攻击的种类和防范手段

    观察近来的一些安全事件及其后果,安全专家们已经得到一个结论,这些威胁主要是通过SQL注入造成的.虽然前面有许多文章讨论了SQL注入,但今天所讨论的内容也许可帮助你检查自己的服务器,并采取相应防范措施. ...

随机推荐

  1. Bootstrap_排版_代码

    不管使用哪种代码风格,在代码中碰到小于号(<)要使用硬编码“<”来替代,大于号(>)使用“>”来替代 一.单行内联代码 <code>:一般是针对于单个单词或单个句子 ...

  2. [Git]更新远程代码到本地仓库

    1. 查看远程仓库 $ git remote -v 2.从远程获取最新代码到本地 $ git fetch origin master 3.比较代码 $ git log -p master.. orig ...

  3. 判断input checkbox选中状态

    $("#IsAdmin").is(':checked') 判断收否选中 返回true 或者false

  4. [Android] hid设备按键流程简述

    hexdump /dev/hidraw0就能看到usbhid设备传输过来的裸流 如:按下Input键 003ae60 0000 0096 8000 006b 0000 0000 0000 0000 * ...

  5. C#之VS2010开发Web Service

    一:创建web service vs2010软件默认的framework是4.0版本,所以想创建web服务的时候压根看不到web服务应用程序.网上有人说vs2010的web service 跟wcf合 ...

  6. typedef 用法及 指针函数 和 函数指针

    typedef 本质上是定义了一种新的类型, 该新类型可以原有类型的别名或是原有类型的组合. 而#define 只是字符串的替换. 如定义: typedef char* CHARP; 则 CHARP ...

  7. UVAlive4287 Proving Equivalences(scc)

    题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=10294 [思路] 强连通分量. 求出bcc后,缩点形成DAG,设D ...

  8. freemarker的非空判断

    ${price} 判断非空写法就是 ${(price)!}

  9. 转:理解Java泛型

    JDK 5.0 中增加的泛型类型,是 Java 语言中类型安全的一次重要改进.但是,对于初次使用泛型类型的用户来说,泛型的某些方面看起来可能不容易明白,甚至非常奇怪.在本月的“Java 理论和实践”中 ...

  10. Js菜鸟学习

    (一) 1 代码如下: //页面中写入html内容 document.write("<h1>Hello World!</h1>") 2 代码如下: //为了 ...