虽然我在现实中还没有碰到过自己开发的东西出现安全漏洞,但是作为一名开发者我深知web安全的重要性。今天在这里就一块分享一下自己的一点体会。这里会提到几个方面,但是是以SQL Injection为主。
问题一:防止SQL注入 (也是主要的安全漏洞之一)
1.1如何防止SQL注入:
其实早就想谈谈这方面的问题了,作为一个开发者如果不注意这方面的问题我想不能算一个合格的开发者。
今天看到一个同学在写SQL 注入的新技术,让我又重新想起了写这方面文章事情。
引起SQL注入的原因比较多,但是主要有以下三点:
1>连接字符串组合SQL指令。
2>开发的数据库权限过大。
3>未对输入进行检测和限制。
1.2SQL注入的方式:
1>利用“;”添加多条命令
2>利用参数的单引号特性
3>利用注释–或/**/
1.3看一个sql注入的Sample
假设我们有一个登录页面,登录的时候输入用户名(tbUserName)和密码:(tbUserPwd),如果我们在验证的时候用:select * from [User]where UserName='"+tbUserName.Text+"' and UserPwd='"+tbUserPwd.Text+"'的方式进行验证,也就是我们
通过连接字符串构造sql语句,然后我们通过判读得到的记录,例如得到的是一个DataTable,我们就可以通过
DataTable.Row.Count>0然后登录成功。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Data; using Cmj.MyData; namespace SQLInjection { public partial class Login : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } DBHelper helper = new DBHelper(); protected void btnLogin_Click(object sender, EventArgs e) { //未防止sql注入 string sql = "select * from [User] where UserName='" + tbUserName.Text + "' and UserPwd='" + tbUserPwd.Text + "'"; DataTable dt = helper.GetDataTable(CommandType.Text, sql); if (dt.Rows.Count > 0) { Session["isLogin"] = true; Response.Redirect("Default.aspx"); } else { ClientScript.RegisterStartupScript(this.GetType(), "cmj", "<mce:script type='text/javascript'><!– alert('用户名或密码错误,请重新输入!') // –></mce:script>"); } } } }
当然了,如果正常使用这是不会有问题的,但是如果我在tbUserName的文本框中输入:'or 1=1– 后你会看到登录
也是成功的,原因很简单,这是我们的sql语句是:select * from [User]
where UserName=''or ''=''–' and UserPwd='' 前半句是永远成立的,后半句被我们注释掉了,肯定DataTable.Row.Count>0
是成立的。
如果聪明一点你可能将判断修改成DataTable.Row.Count==0登录成功。但是我们如果输入:';select top 1 * from [User]– 当然如果这样输入就需要知道表名为User,不过这也是很简单的事情。而且这样还不算过分,如果输入:'; drop table [User];– 怎么办,又或者直接删除数据库等。。。太可怕了吧!
1.4防御SQL注入
1>使用参数化查询
2>组合字符串时进行字符串取代(将一个单引号替换成两个)
3>使用更安全的连接,例如Linq to SQL
4>使用SQL防注入系统
那么我们就来修改上面的例子吧,现在我就以第一种方法来解决吧。我们知道在刚才查询的时候我们用的是拼接字符串的方式,现在
我修改成:select * from [User] where UserName=@name and UserPwd=@password 然后我通过.net本省的SqlCommand.Parameters.Add()来添加相应的参数来避免sql注入。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Data; using Cmj.MyData; namespace SQLInjection { public partial class Login : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } DBHelper helper = new DBHelper(); protected void btnLogin_Click(object sender, EventArgs e) { //防止sql注入 string sql = "select * from [User] where UserName=@name and UserPwd=@password"; object[,] obj = new object[2, 2]; obj[0, 0] = "@name"; obj[0, 1] = tbUserName.Text; obj[1, 0] = "@password"; obj[1, 1] = tbUserPwd.Text; DataTable dt = helper.GetDataTable(CommandType.Text, sql, obj); if (dt.Rows.Count == 1) { Session["isLogin"] = true; Response.Redirect("Default.aspx"); } else { ClientScript.RegisterStartupScript(this.GetType(), "cmj", "<mce:script type='text/javascript'><!– alert('用户名或密码错误,请重新输入!') // –></mce:script>"); } } } }
问题二:加密敏感信息
我们现在数据库中有一个用户UserName为:jianxin160 密码为:cmjstudio 因此我们在上面的代码比较时如果用户输入的是相应的用户名和密码就没有任何问题了,但是有没有想过数据库的User表中存储那用的明文密码是很不安全的,很容易暴露被不法分子获取。这就要我们对敏感信息加密存储。方法比较多,比较常见的就是取其哈希码,例如上面用户的秘密我们存储的时候就进行加密,加密结果是:01 E7 F0 0D 4F 17 7C 4C B2 EC C4 C1 BE 92 9E 48 我们将这样的信息存储到数据库对应的字段中,那么我们在查询的时候就先将用户输入的秘密加密后和数据库中比较来判断是否有此用户。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Data; using Cmj.MyData; namespace SQLInjection { public partial class Login : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } DBHelper helper = new DBHelper(); protected void btnLogin_Click(object sender, EventArgs e) { //密码加密 Cmj.MySecurity.MyHash hash = new Cmj.MySecurity.MyHash(); string sql = "select * from [User] where UserName=@name and UserPwd=@password"; object[,] obj = new object[2, 2]; obj[0, 0] = "@name"; obj[0, 1] = tbUserName.Text; obj[1, 0] = "@password"; obj[1, 1] = hash.getHashValue(tbUserPwd.Text, Cmj.MySecurity.MyHash.algorithm.MD5).Trim(); DataTable dt = helper.GetDataTable(CommandType.Text, sql, obj); if (dt.Rows.Count == 1) { Session["isLogin"] = true; Response.Redirect("Default.aspx"); } else { ClientScript.RegisterStartupScript(this.GetType(), "cmj", "<mce:script type='text/javascript'><!– alert('用户名或密码错误,请重新输入!') // –></mce:script>"); } } } }
问题三:保证数据安全传输
我们如何保证输入的密码在传输过程中是安全的呢?也就是我们在对tbUserPwd进行加密同数据库中加密过的信息比较之前秘密安全如何保障,这一点一定要搞清楚,我们输入到文本框的可是没有加密过的信息(我们在文本框中输入的秘密是:cmjstudio),不过怎么加密都是在服务器端的事情,但是在密码到达服务器端之前还是要靠http传输的,而http信息是没有加密的)。
为了方便的看出这一点我用Proxy Snifer Pro(Snifer Pro也可以,但是由于我的系统是windows 7 64x ,目前没办法用)来抓一个包看看。
这是我在正常登录的时候用Snifer Pro抓的一个包,可以清楚的看到图中我用白色边框圈出来的部分正是我们的输入信息,用户名当然还无所谓,但是关键是密码也暴露无疑。用户按照正常的操作,输入正确的用户名密码,此时用户自己看到的密码一般肯定就星号、圆点或其他掩盖字符来防止其他人看到,但是当用户在点击登录按钮后此时信息将传到服务器端进行验证。但是在信息从客户端到达浏览器的途中,密码是以明文字符串进行传输的。换句话说此时不法分子就可以利用各种手段在这个阶段做文章来获得用户的登录信息。如何解决这个问题?目前主流的方法就是通过SSL加密来解决(也就是通过Https协议传输)。注意:具体如何使用Proxy Sniffer会在其他文章中探讨。
问题四:补充一点东西
多数情况下我们还要对输入的数据进行合法性的验证,例如我们上面的例子中可以用javascript在前台对输入进行长度和字符格式限制,包括各种正则表达式匹配。
问题五:专业的软件检测
最后我想说的是,这样就安全了?是否还有没有考虑到的呢?不敢保证吧?
问题就是如何发现那些没有发现的漏洞,我想我们要认证思考,如果再认证思考过之后实在是想不到了,这时我们不妨用专业的软件来检验我们的程序。在这里介绍一种测试web应用程序的软件–IBM Rational AppScan,我们通过它来检查我们的漏洞,在此之前我们先故意曝露出SQL注入 来看一下软件发现情况。这是我扫描的结果:
首先我们清楚的看到sql注入被检测到了。而且给出了问题信息,例如我选中sql注入中的tbUserName之后提示信息:
我们看一下它的现在我从程序中避免sql注入之后(同时启用数据库中加密)的状况:
我们看到sql注入情况没有了。这时我们继续分析,看上面的“已解密的登录请求”,具体问题信息如下:
我们发现对验证参数没有进行传输加密的问题也被发现了,确实很不错:-),好的我们将这个问题也解决,通过SSL来加密。这是我们的检测情况是:
问题解决了吧,我们继续看上面我点出的另一个问题那就是启用了asp.net提示,给出的问题信息是:
我们将启用调试关闭之后再看一下:
上面的问题也不复存在了吧,当然还有一些级别较低的漏洞我就不再赘余了,大家可以自己试试。
我们基本上将web安全这一块大致说完了,就到这里吧!O(∩_∩)O~
原文链接:https://blog.csdn.net/iteye_20835/article/details/81855282
原创文章,作者:优速盾-小U,如若转载,请注明出处:https://www.cdnb.net/bbs/archives/18371