原文:防SQL注入:生成参数化的通用分页查询语句

前些时间看了玉开兄的“如此高效通用的分页存储过程是带有sql注入漏洞的”这篇文章,才突然想起某个项目也是使用了累似的通用分页存储过程。使用这种通用的存储过程进行分页查询,想要防SQL注入,只能对输入的参数进行过滤,例如将一个单引号“'”转换成两个单引号“''”,但这种做法是不安全的,厉害的黑客可以通过编码的方式绕过单引号的过滤,要想有效防SQL注入,只有参数化查询才是最终的解决方案。但问题就出在这种通用分页存储过程是在存储过程内部进行SQL语句拼接,根本无法修改为参数化的查询语句,因此这种通用分页存储过程是不可取的。但是如果不用通用的分页存储过程,则意味着必须为每个具体的分页查询写一个分页存储过程,这会增加不少的工作量。

经过几天的时间考虑之后,想到了一个用代码来生成参数化的通用分页查询语句的解决方案。代码如下:

Code
    public class PagerQuery
    {
        private int _pageIndex;
        private int _pageSize = 20;
        private string _pk;
        private string _fromClause;
        private string _groupClause;
        private string _selectClause;
        private string _sortClause;
        private StringBuilder _whereClause;
        public DateTime DateFilter = DateTime.MinValue;         protected QueryBase()
        {
            _whereClause = new StringBuilder();
        }         /**//// <summary>
        /// 主键
        /// </summary>
        public string PK
        {
            get { return _pk; }
            set { _pk = value; }
        }         public string SelectClause
        {
            get { return _selectClause; }
            set { _selectClause = value; }
        }         public string FromClause
        {
            get { return _fromClause; }
            set { _fromClause = value; }
        }         public StringBuilder WhereClause
        {
            get { return _whereClause; }
            set { _whereClause = value; }
        }         public string GroupClause
        {
            get { return _groupClause; }
            set { _groupClause = value; }
        }         public string SortClause
        {
            get { return _sortClause; }
            set { _sortClause = value; }
        }         /**//// <summary>
        /// 当前页数
        /// </summary>
        public int PageIndex
        {
            get { return _pageIndex; }
            set { _pageIndex = value; }
        }         /**//// <summary>
        /// 分页大小
        /// </summary>
        public int PageSize
        {
            get { return _pageSize; }
            set { _pageSize = value; }
        }         /**//// <summary>
        /// 生成缓存Key
        /// </summary>
        /// <returns></returns>
        public override string GetCacheKey()
        {
            const string keyFormat = "Pager-SC:{0}-FC:{1}-WC:{2}-GC:{3}-SC:{4}";
            return string.Format(keyFormat, SelectClause, FromClause, WhereClause, GroupClause, SortClause);
        }         /**//// <summary>
        /// 生成查询记录总数的SQL语句
        /// </summary>
        /// <returns></returns>
        public string GenerateCountSql()
        {
            StringBuilder sb = new StringBuilder();             sb.AppendFormat(" from {0}", FromClause);
            if (WhereClause.Length > 0)
                sb.AppendFormat(" where 1=1 {0}", WhereClause);             if (!string.IsNullOrEmpty(GroupClause))
                sb.AppendFormat(" group by {0}", GroupClause);             return string.Format("Select count(0) {0}", sb);
        }         /**//// <summary>
        /// 生成分页查询语句,包含记录总数
        /// </summary>
        /// <returns></returns>
        public string GenerateSqlIncludeTotalRecords()
        {
            StringBuilder sb = new StringBuilder();
            if (string.IsNullOrEmpty(SelectClause))
                SelectClause = "*";             if (string.IsNullOrEmpty(SortClause))
                SortClause = PK;             int start_row_num = (PageIndex - 1)*PageSize + 1;             sb.AppendFormat(" from {0}", FromClause);
            if (WhereClause.Length > 0)
                sb.AppendFormat(" where 1=1 {0}", WhereClause);             if (!string.IsNullOrEmpty(GroupClause))
                sb.AppendFormat(" group by {0}", GroupClause);             string countSql = string.Format("Select count(0) {0};", sb);
            string tempSql =
                string.Format(
                    "WITH t AS (SELECT ROW_NUMBER() OVER(ORDER BY {0}) as row_number,{1}{2}) Select * from t where row_number BETWEEN {3} and {4};",
                    SortClause, SelectClause, sb, start_row_num, (start_row_num + PageSize - 1));             return tempSql + countSql;
        }         /**//// <summary>
        /// 生成分页查询语句
        /// </summary>
        /// <returns></returns>
        public override string GenerateSql()
        {
            StringBuilder sb = new StringBuilder();
            if (string.IsNullOrEmpty(SelectClause))
                SelectClause = "*";             if (string.IsNullOrEmpty(SortClause))
                SortClause = PK;             int start_row_num = (PageIndex - 1)*PageSize + 1;             sb.AppendFormat(" from {0}", FromClause);
            if (WhereClause.Length > 0)
                sb.AppendFormat(" where 1=1 {0}", WhereClause);             if (!string.IsNullOrEmpty(GroupClause))
                sb.AppendFormat(" group by {0}", GroupClause);             return
                string.Format(
                    "WITH t AS (SELECT ROW_NUMBER() OVER(ORDER BY {0}) as row_number,{1}{2}) Select * from t where row_number BETWEEN {3} and {4}",
                    SortClause, SelectClause, sb, start_row_num, (start_row_num + PageSize - 1));
        }
    }

使用方法:

PagerQuery query = new PagerQuery();

query.PageIndex = 1;

    query.PageSize = 20;

    query.PK = "ID";

    query.SelectClause = "*";

    query.FromClause = "TestTable";

    query.SortClause = "ID DESC";

if (!string.IsNullOrEmpty(code))

    {

     query.WhereClause.Append(" and ID= @ID");

    }

a) GenerateCountSql ()方法生成的语句为:

Select count(0) from TestTable Where 1=1 and ID= @ID

b) GenerateSql()方法生成的语句为:

WITH t AS (SELECT ROW_NUMBER() OVER(ORDER BY ECID DESC) as row_number, * from TestTable where 1=1 and ID= @ID) Select * from t where row_number BETWEEN 1 and 20

c) GenerateSqlIncludetTotalRecords()方法生成的语句为:

WITH t AS (SELECT ROW_NUMBER() OVER(ORDER BY E.ECID DESC) as row_number,* from TestTable where 1=1 and ID= @ID) Select * from t where row_number BETWEEN 1 and 20;Select count(0) from ECBasicInfo where 1=1 and ID= @ID;

注意:以上代码生成的SQL语句是曾对SQL SERVER 2005以上版本的,希望这些代码对大家有用

防SQL注入:生成参数化的通用分页查询语句的更多相关文章

  1. C#防SQL注入代码的实现方法

    对于网站的安全性,是每个网站开发者和运营者最关心的问题.网站一旦出现漏洞,那势必将造成很大的损失.为了提高网站的安全性,首先网站要防注入,最重要的是服务器的安全设施要做到位. 下面说下网站防注入的几点 ...

  2. Sqlparameter防SQL注入

    一.SQL注入的原因 随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多.但是由于这个行业的入门门槛不高,程序员的水平及经验也参差不齐,相当大一部分程序员在编写代码的时候,没有对 ...

  3. golang 防SQL注入 基于反射、TAG标记实现的不定参数检查器

    收到一个任务,所有http的handler要对入参检查,防止SQL注入.刚开始笨笨的,打算为所有的结构体写一个方法,后来统计了下,要写几十上百,随着业务增加,以后还会重复这个无脑力的机械劳作.想想就l ...

  4. .Net防sql注入的方法总结

    #防sql注入的常用方法: 1.服务端对前端传过来的参数值进行类型验证: 2.服务端执行sql,使用参数化传值,而不要使用sql字符串拼接: 3.服务端对前端传过来的数据进行sql关键词过来与检测: ...

  5. SpringBoot微服务电商项目开发实战 --- api接口安全算法、AOP切面及防SQL注入实现

    上一篇主要讲了整个项目的子模块及第三方依赖的版本号统一管理维护,数据库对接及缓存(Redis)接入,今天我来说说过滤器配置及拦截设置.接口安全处理.AOP切面实现等.作为电商项目,不仅要求考虑高并发带 ...

  6. PDO防sql注入原理分析

    使用pdo的预处理方式可以避免sql注入. 在php手册中'PDO--预处理语句与存储过程'下的说明: 很多更成熟的数据库都支持预处理语句的概念.什么是预处理语句?可以把它看作是想要运行的 SQL 的 ...

  7. 【荐】PDO防 SQL注入攻击 原理分析 以及 使用PDO的注意事项

    我们都知道,只要合理正确使用PDO,可以基本上防止SQL注入的产生,本文主要回答以下几个问题: 为什么要使用PDO而不是mysql_connect? 为何PDO能防注入? 使用PDO防注入的时候应该特 ...

  8. C#语言Winform防SQl注入做用户登录的例子

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...

  9. PHP防SQL注入不要再用addslashes和mysql_real_escape_string

    PHP防SQL注入不要再用addslashes和mysql_real_escape_string了,有需要的朋友可以参考下. 博主热衷各种互联网技术,常啰嗦,时常伴有强迫症,常更新,觉得文章对你有帮助 ...

随机推荐

  1. 【C语言探索之旅】 第一部分第八课:第一个C语言小游戏

    ​ 内容简介 1.课程大纲 2.第一部分第八课:第一个C语言小游戏 3.第一部分第九课预告: 函数 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C语言编写 ...

  2. liunx tomcat多站点配置

    <Host name="nav" debug="0" appBase="webapps"  unpackWARs="true ...

  3. 配置Tomcat出现Unsupported major.minor version 51.0

    在配置tomcat时,配置好jdk1.6,下载的tomcat8.0,结果执行start-up.bat,总是一闪而过,网上查了大量的资料,都说是可能是jdk没配置好,但实际上jdk的环境变量设置正常,后 ...

  4. 【iOS】Web Color 的 Swift 实现

    用Swift语言重写Web Color这个类. 这次是用函数实现的,感觉也非常简洁.眼下(2014.6.28) Xcode 6的方法提示还不健全,就仅仅实现了用颜色名字创建颜色的功能. 最新代码&am ...

  5. IOS应用上传须要做的工作

    苹果开发人员   https://developer.apple.com/ 证书创建流程 certificates (证书): 是电脑可以增加开发人员计划的凭证 证书分为:开发证书和公布(产品)证书, ...

  6. Cocos2d-x 3.0final 终结者系列教程04-引擎架构分析

    从前,有一个跟我来Android学生,总是问我: 沉老师,为什么Android的形式被称为Activity,为什么要onCreate方法写setContentView(R.layout.main)? ...

  7. AndroidAndroid程序提示和消息button响应事件

    首先,接口XML加入button响应函数 android:onClick="OnMyClick" <Button android:id="@+id/button1& ...

  8. ProgressDialog(三)——代号为中心的屏幕上显示ProgressDialog(ProgressBar)

    MainActivity如下面: package cc.testprogressdialog; import android.os.Bundle; import android.view.Gravit ...

  9. rm-rf 恢复过程中滥用

    多DBA一定rm -rf讨厌它,也许有一天自己将数据库,以消除一个中午,然后.那么就没有一个--这种情况下,--这个不幸真的发生,你真的无药可救?不必要,有解决方法.也许你遇到不幸时,有一天.你可以用 ...

  10. java实现生产者消费者问题(转)

    引言 生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,如下图所示,生产者向空间里存放数据,而消费者取用数据,如果不加以协调可能会出现以下情况: 生产者消费者图 ...