现在的位置: 首页 > 综合 > 正文

简单高效防注入攻击的动态多参数、动态SQL语句拼接方法,提高网站的安全性

2013年02月20日 ⁄ 综合 ⁄ 共 7480字 ⁄ 字号 评论关闭
文章目录

并非人人是高手,并非人人是神仙,我也有不懂的地方,我也有不注意的技术问题,多交流多学习就是最好的提高方法

 

   其实对与初学者来说,进行的动态的查询语句拼接也不是那么好做的事情,就是做出来了,也未必是经得起考验的足够灵活好用的,未必是能拿得出手可以进行推广的,是否能拿得出就是其中的关键。

 

   今天检查公司的软件项目质量,发现有2个同事写的程序存在SQL注入攻击的漏洞,当然也不能怪罪人家,他们也是刚参加工作1-2年,还没有那么丰富的技术经验、安全意识,不会注意到自己编写的系统会有严重的安全漏洞。

   公司在宁波也有一个政府网站的项目,在做系统安全检查自动扫描时,也被查出了一些SQL注入攻击的漏洞,这也使得让我更加提高了系统是否存在安全漏洞的意识,当然我写的系统很少会有这类的漏洞,但是整个公司有几十号人在做开发公司,所以也难免别人不会犯错。

  

   人工检查是否有SQL注入安全漏洞,其实非常简单,只要在查询输入框里输入有单引号的字符“'”,例如查询条件输入“吉日'嘎拉”其中有单引号,很可能这个查询的页面就崩溃了出现了错误页面,或者跳转到出错页面了,若有这样的情况发生,几乎99.3721%都可以看做是有潜在的SQL注入攻击漏洞。

 

   防止这个错误的发生,从技术上应该说是要用“参数化的查询”,贴一些代码,给大家参考一下我的做法:

代码

        #region public DataTable Search(string folderId, string searchValue, Boolean deleteMark) 查询
        
/// <summary>
        
/// 查询
        
/// </summary>
        
/// <param name="folderId">目录</param>
        
/// <param name="searchValue">查询条件</param>
        
/// <param name="deleteMark">删除标志</param>
        
/// <returns>数据表</returns>
        public DataTable Search(string folderId, string searchValue, Boolean deleteMark)
        {
            
// 一、这里是将Boolean值转换为int类型。
            int delete = deleteMark ? 10;

            // 二、这里是开始进行动态SQL语句拼接,字段名、表明都进行了常量定义,表名字段名发生变化时,很容易就知道程序哪里都调用了这些。
            string sqlQuery = string.Empty;
            sqlQuery 
= " SELECT " + BaseNewsTable.FieldId
                    
+ "        ," + BaseNewsTable.FieldFolderId
                    
+ "        ," + BaseNewsTable.FieldTitle
                    
+ "        ," + BaseNewsTable.FieldFilePath
                    
+ "        ," + BaseNewsTable.FieldFileSize
                    
+ "        ," + BaseNewsTable.FieldReadCount
                    
+ "        ," + BaseNewsTable.FieldDescription
                    
+ "        ," + BaseNewsTable.FieldCategoryCode
                    
+ "        ," + BaseNewsTable.FieldEnabled
                    
+ "        ," + BaseNewsTable.FieldDeleteMark
                    
+ "        ," + BaseNewsTable.FieldSortCode
                    
+ "        ," + BaseNewsTable.FieldCreateUserId
                    
+ "        ," + BaseNewsTable.FieldCreateUserRealname
                    
+ "        ," + BaseNewsTable.FieldCreateDate
                    
+ "        ," + BaseNewsTable.FieldModifyUserId
                    
+ "        ," + BaseNewsTable.FieldModifyUserRealname
                    
+ "        ," + BaseNewsTable.FieldModifyDate
                    
+ " FROM " + this.CurrentTableName
                    
+ " WHERE " + BaseNewsTable.FieldDeleteMark + " = " + delete;

            // 三、我们认为 folderId 这个查询条件是安全,不是人为输入的参数,所以直接进行了SQL语句拼接
            if (!String.IsNullOrEmpty(folderId))
            {
                sqlQuery 
+= " AND " + BaseNewsTable.FieldFolderId + " = '" + folderId + "'";
            }

            // 四、这里是进行参数化的准备,因为是多个不确定的查询参数,所以用了List。
            List<DbParameter> dbParameters = new List<DbParameter>();

            // 五、这里看查询条件是否为空
            searchValue = searchValue.Trim();
            
if (!String.IsNullOrEmpty(searchValue))
            {
                
// 六、这里是进行支持多种数据库的参数化查询
                sqlQuery += " AND (" + BaseNewsTable.FieldTitle + " LIKE " + DbHelper.GetParameter(BaseNewsTable.FieldTitle);
                sqlQuery 
+= " OR " + BaseNewsTable.FieldCreateUserRealname + " LIKE " + DbHelper.GetParameter(BaseNewsTable.FieldCreateUserRealname);
                sqlQuery 
+= " OR " + BaseNewsTable.FieldContents + " LIKE " + DbHelper.GetParameter(BaseNewsTable.FieldContents);
                sqlQuery 
+= " OR " + BaseNewsTable.FieldDescription + " LIKE " + DbHelper.GetParameter(BaseNewsTable.FieldDescription) + ")";
                
                
// 七、这里是判断,用户是否已经输入了%
                if (searchValue.IndexOf("%"< 0)
                {
                    searchValue 
= "%" + searchValue + "%";
                }

                // 八、这里生成支持多数据库的参数
                dbParameters.Add(DbHelper.MakeInParam(BaseNewsTable.FieldTitle, searchValue));
                dbParameters.Add(DbHelper.MakeInParam(BaseNewsTable.FieldCreateUserRealname, searchValue));
                dbParameters.Add(DbHelper.MakeInParam(BaseNewsTable.FieldContents, searchValue));
                dbParameters.Add(DbHelper.MakeInParam(BaseNewsTable.FieldDescription, searchValue));
            }

            // 九、这里是将List转换为数组,进行数据库查询
            return DbHelper.Fill(sqlQuery, dbParameters.ToArray());
        }
        
#endregion

 

对以上代码进行一下简单的点评:

优点有如下:

    1:支持多数据库。

    2:dbParameters.Add,非常灵活,可以动态组织多个查询条件条件,想要几个参数就要几个参数,最后又巧妙的用了 dbParameters.ToArray(),这个是值得学习的。

    3:函数的命名、注释、参数的大小写比较规范。

    4:函数的注释写得很棒,对初学者学习模仿起了一个好榜样。

    5:searchValue.IndexOf("%"也想得比较深入,有些专业人事查询时,可能已经输入了%。

    6:表名、表字段都进行了定义,若后期表名、表字段发生了变化,程序不会有未知的错误,很容易在编码阶段就发现错误,在软件开发初期,表名、字段名变来变去是很常见的事情,若是好几个人开发的,又经常变来变去的,若是当时没控制好,后期就会乱套。

    7:SQL语句拼接时,没有用 WHERE 1=1 AND , 而是巧妙的运用了 DeleteMark 字段,是值得学习的。

    8:SQL语句中关键字都进行了大写,是比较符合行业规范的,大家也值得参考。

    9:不管如何,对自己写的每行代码,每行注释,都有了深入的讲解、深入的认识,这是大家值得学习的,若哪里写得不好,有错就改的精神,分享的精神是值得学习的。

  10:先关注好身边的事情、才能关注网上的事情,是大家值得学习的,别总是有过于远大的理想,过于理想化的梦想,把身边的事情都做好,把身边的同事都关注好,就是干得最出色最及格了,这个务实的精神也是大家值得学习的。

  11:  查询代码思路严谨得有些过分,实在是过分,区区一个动态查询居然分了9个步骤来实现,思路细腻严谨得让人实在是佩服啊,不佩服不行啊,菜鸟程序员的骄傲、学习模仿的楷模,呵呵。

 

  先就写到这里吧,再夸下去,估计很多人都要吐了。

 

  相应的源码给大家敬上,若能起到参考,我也很荣幸了

  /Files/jirigala/DotNet.CommonV3.0.rar

  /Files/jirigala/handbookV3.0.pdf

 

    

 

 

 

  

将权限管理、工作流管理做到我能力的极致,一个人只能做好那么很少的几件事情。

posted on 2010-07-28 19:49 吉日嘎拉 不仅权通用权限 阅读(4339) 评论(78) 编辑 收藏

评论

#1楼  回复 引用 查看    前排强势就坐

2010-07-28 20:02 | sheng.chao      

#2楼[楼主]  回复 引用 查看   

@sheng.chao
热烈欢迎鹏程的好朋友们。

#3楼  回复 引用 查看   

"先就写到这里吧,再夸下去,估计很多人都要吐了。" 呵呵

2010-07-28 20:06 | 风枫峰      

#4楼  回复 引用 查看   

支持!

2010-07-28 20:06 | 邀月      

#5楼[楼主]  回复 引用 查看   

@风枫峰

都不按推荐啊,只是支持啊?

#6楼[楼主]  回复 引用 查看   

强烈要求,按推荐,哈哈。

引用

邀月:支持!

#7楼  回复 引用 查看   

看不太懂。得下源码来看看,像一般的商城,商品高级搜索一般会用到inner join自身N次的,像这样的也可以用参数化查询?
下面是我公司商城的商品高级搜索SQL语句,也可以用参数化查询?
请教。。。

01 with temptbl as (
02     select ROW_NUMBER() OVER (ORDER BY proid desc)AS Row,proid,proname 型号,brdname 品牌,proimage 首图,
03     propricebulk 批发价,propricemarket 零售价,  dbo.[fn_productparam](proid) 参数 
04     from tb_product p
05     inner join tb_Brand b on b.brdid=p.probrand
06     inner join [tb_Product-Param] pp on pp.propproductid=p.proid 
07     and (pp.propparamid=3 and pp.propparamvalue='LGA 775')
08     inner join [tb_Product-Param] pp2 on pp2.propproductid=p.proid 
09     and (pp2.propparamid=5 and pp2.propparamvalue='0.045um')
10     inner join [tb_Product-Param] pp3 on pp3.propproductid=p.proid 
11     and (pp3.propparamid=4 and pp3.propparamvalue='Wolfdale')
12     where b.brdid=16 and protype=21 and propricemarket between 600 and 800
13 )
14 SELECT * FROM temptbl where Row between 1 and 5

2010-07-28 20:29 | 牛腩      

#8楼  回复 引用 查看   

一两年经验还会让SQL注入攻击漏洞存在?
不敢多言...。

2010-07-28 20:40 | Eric Liao      

#9楼  回复 引用 查看   

我最近都只用linq to EF了,可以不考虑这些了

2010-07-28 20:45 | 钧梓昊逑      

#10楼  回复 引用 查看   

学习。。。。。

2010-07-28 20:56 | 夏天爱上雨      

#11楼  回复 引用 查看   

拼sql之类的东西就不要上首页了,污染大家视线

2010-07-28 21:12 | 型格小妖      

#12楼  回复 引用 查看   

2010-07-28 21:27 | 爱的释放      

#13楼  回复 引用 查看   

@爱的释放
可以的

2010-07-28 21:28 | 爱的释放      

#14楼  回复 引用 查看   

@夏天爱上雨
也学习...

2010-07-28 21:28 | 爱的释放      

#15楼  回复 引用 查看   

神似唐骏,就会吹!

说来说去,关键的MakeInParam方法还是没有公布出来 ,不过我想也就是检查一下是否有危险字符,然后拼接成字符串!

dbParameters这个类,不知道是继承的参数类还是自己写的,如果是自己写的,完全是多此一举,用参数类加sqlhelper类,安全得很!

另外要说的是folderId字段既然是查询参数,为什么还要考虑是否是有可能人工输入呢?直接放在MakeInParam搞一下不是更安全?况且,这个参数肯定是前台传到后台的,人工输入是不可能了,但如果是注入程序,一样可以利用这个参数!

2010-07-28 21:54 | szyicol      

#16楼  回复 引用 查看   

吉日兄,顶一个,谢谢分享

#17楼  回复 引用 查看   

我发现,我们这边两个工作了1~2年的程序员,去乘地铁时上自动扶梯,那个跨步总是踩不稳,经常摔个狗吃屎。
当然也不能怪罪人家,他们也是刚参加工作1-2年,还没有像我这么丰富的技术经验、安全意识。
过两天我也写一篇大作,教大家如何跨上自动扶梯。

2010-07-28 22:20 | Cheney Shue      

#18楼  回复 引用 查看   

吉日的文档写的不错。

2010-07-28 22:36 | John Liu      

#19楼  回复 引用 查看   

抱歉!评论已关闭.