上软件工程这门课的时候,王老师说写代码的时候要严谨,顺带地提到了SQL注入并进行了简单的演示。那么什么是SQL注入呢?SQL注入是一种注入攻击,由于应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在应用程序中事先定义好的查询语句的结尾添加恶意的SQL语句,从而在管理员不知情的情况下,攻击者能够完全控制应用程序后面的数据库服务器实行非法操作。比如:攻击者可以使用SQL注入漏洞绕过应用程序安全措施;可以绕过网页或Web应用程序的身份验证和授权,并检索整个SQL数据库的内容;可以使用SQL注入来增删改查数据库中的数据记录,还可以未经授权非法访问用户的敏感数据:客户信息,个人数据,商业机密,知识产权等。
下面我简单地还原一下王老师在课堂上的演示。
这是一个非常简单的登录界面。(“安全登录”这个按钮划重点?)
它的后端代码只是简单地限制了用户输入的账号密码不能为空,具体代码如下:
using System;
using System.Data.SqlClient;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void ButtonLogin_Click(object sender, EventArgs e)
{
string username = textBox1.Text.Trim(); //获取用户输入的账号
string password = textBox2.Text.Trim(); //获取用户输入的密码
//与sql sever建立连接,并指定到Library这个数据库
SqlConnection connection = new SqlConnection("Data Source=.;Initial Catalog=Library;User ID=sa;Password=sql123");
connection.Open(); //打开连接
//用SqlCommand来执行SQL语句:SELECT userid,password FROM usertable WHERE userid = (用户输入的username) AND password = (用户输入的password);
SqlCommand SqlCommand = new SqlCommand("SELECT username,password FROM login_table WHERE username ='"+username+"' AND password ='"+password+"'", connection);
SqlDataReader data = SqlCommand.ExecuteReader();
data.Read();
if (String.IsNullOrEmpty(username) || String.IsNullOrEmpty(password))
{
MessageBox.Show("用户名或密码不能为空!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else if (data.HasRows) //HasRows用来判断查询结果中是否有数据
{
MessageBox.Show("登录成功!","通知",MessageBoxButtons.OK,MessageBoxIcon.Asterisk); //登录成功
}
else
{
MessageBox.Show("登录失败!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
data.Close(); //关闭SqlDataReader对象
connection.Close(); //关闭连接
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Enter) //用户输入完毕时敲回车键可以进行登录
{
ButtonLogin_Click(sender,e);
}
}
}
}
这里我直接用的之前写过的数据库大作业——图书管理系统里的用户登录表(之前存储的密码都是经过MD5加密过后的)。
我在SSMS里给这张表新加了一行:admin, 123456, 管理员。
简单地输入几组数据测试一下:
①输入账号:NULL 密码:NULL 结果:登录失败。
②输入账号:admin 密码:123 结果:登录失败
③输入账号:admin 不输入密码 结果如下:
④输入账号:admin 密码:123456 (这组数据库中存储的正确的账号密码)结果:登录成功
经过这几组测试,这个登录程序似乎很完美,简直就是无懈可击好嘛?怎么会有问题呢?(手动滑稽.jpg)
然而并不是所有的用户都是像上面那样友好的,骚操作来了!
输入账号:admin';-- 密码:1(让密码不为空就行)敲回车,结果:登录成功!(爆破成功?)
为什么会出现这样的结果呢?原因很简单,--在SQL中的作用是注释掉这一行--后面的代码。我们期望执行的代码如下:
SELECT userid,password FROM usertable WHERE userid = (用户输入的username) AND password = (用户输入的password);
而上面那个恶意输入 实际执行的代码是如下的,可以在SSMS中执行一下这条SQL语句看看。
SELECT username,password FROM login_table WHERE username ='admin';--AND password ='" + password + "'"
是不是感觉SQL注入攻击很危险?来玩点刺激的 现在我要来爆破一下我大二写过的那个登录时采用MD5加密的图书馆管理系统。
魏老师当时对这个图书管理系统的评价还是比较不错的,数据库实验成绩好像是90...? 记不太清了,爆破就完事了。我当时太嫩了,只对密码进行了MD5加密,并没有对用户输入的内容用正则表达式进行限制(比如账号中不能输入'或者;啊等等。源代码在github上了:https://github.com/Don2025/MyCode/tree/master/Library
输入账号:admin 密码:123 验证码:cvex 结果:登录失败(说明程序还是正常的,下面开始SQL注入)
输入账号:admin';-- 密码:123(不为空就行,为了对比上面那组测试用例还是用123)验证码:1pta
结果:登录成功。直接非法进入到了管理员界面。
利用应用程序的漏洞进行SQL注入后,我可以利用管理员的身份对用户信息进行非法访问:
我还可以帮自己或者别人还书(其实书根本没还到图书馆但是记录显示还了),还可以把图书馆的书籍删除(全删除的那一种)。
我们可以注销之后,换一种方式继续爆破一下。当我们不知道有没有admin这个管理员账号时,可以这样操作。
账号:' or 1=1;-- 密码:123 验证码:pr14
可以看到结果同样是登录成功,我们非法地以管理员身份进入到了后台。
当用户恶意地输入的账号为' or 1=1;时,在登录的时候等价于让数据库执行了下面这个恶意的SQL语句。
SELECT username,password FROM login_table WHERE username ='' or 1=1;--AND password ='" + password + "'"
1=1是为true的,可以在SSMS中执行看一下结果
①强迫使用参数化语句来传递用户输入的内容。简单来说就是在编写SQL语句的时候,用户输入的变量不是直接嵌入到SQL语句,而是通过参数来传递这个变量以有效的防治SQL注入式攻击。用户的输入的内容绝对不能够直接被嵌入到SQL语句中,必须用正则表达式进行过滤或者使用参数化的语句来传递用户输入的变量。采用这种措施,可以杜绝大部分的SQL注入式攻击。
②加强对用户输入内容的检查与验证。在SQLServer中,有比较多的用于验证用户输入内容的工具,可以帮助管理员来对付SQL注入式攻击。比如:测试字符串变量的内容,只接受所需的值,拒绝包含二进制数据、转义序列和注释字符的输入内容,测试用户输入内容的大小和数据类型,强制执行适当的限制与转换。这些验证工具有助于防止脚本注入,防止某些缓冲区溢出攻击。这即有助于防止有意造成的缓冲区溢出,对于防治注入式攻击有比较明显的效果。