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

简介: 原文:防SQL注入:生成参数化的通用分页查询语句      前些时间看了玉开兄的“如此高效通用的分页存储过程是带有sql注入漏洞的”这篇文章,才突然想起某个项目也是使用了累似的通用分页存储过程。使用这种通用的存储过程进行分页查询,想要防SQL注入,只能对输入的参数进行过滤,例如将一个单引号“'”转换成两个单引号“''”,但这种做法是不安全的,厉害的黑客可以通过编码的方式绕过单引号的过滤,要想有效防SQL注入,只有参数化查询才是最终的解决方案。
原文: 防SQL注入:生成参数化的通用分页查询语句

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

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

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Code
    public class PagerQuery
img_405b18b4b6584ae338e0f6ecaf736533.gifimg_1c53668bcee393edac0d7b3b3daff1ae.gif    
{
        
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()
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
            _whereClause 
= new StringBuilder();
        }


img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
/**//// <summary>
        
/// 主键
        
/// </summary>

        public string PK
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
get return _pk; }
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
set { _pk = value; }
        }


        
public string SelectClause
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
get return _selectClause; }
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
set { _selectClause = value; }
        }


        
public string FromClause
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
get return _fromClause; }
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
set { _fromClause = value; }
        }


        
public StringBuilder WhereClause
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
get return _whereClause; }
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
set { _whereClause = value; }
        }


        
public string GroupClause
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
get return _groupClause; }
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
set { _groupClause = value; }
        }


        
public string SortClause
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
get return _sortClause; }
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
set { _sortClause = value; }
        }


img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
/**//// <summary>
        
/// 当前页数
        
/// </summary>

        public int PageIndex
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
get return _pageIndex; }
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
set { _pageIndex = value; }
        }


img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
/**//// <summary>
        
/// 分页大小
        
/// </summary>

        public int PageSize
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
get return _pageSize; }
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
set { _pageSize = value; }
        }


img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
/**//// <summary>
        
/// 生成缓存Key
        
/// </summary>
        
/// <returns></returns>

        public override string GetCacheKey()
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
            
const string keyFormat = "Pager-SC:{0}-FC:{1}-WC:{2}-GC:{3}-SC:{4}";
            
return string.Format(keyFormat, SelectClause, FromClause, WhereClause, GroupClause, SortClause);
        }


img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
/**//// <summary>
        
/// 生成查询记录总数的SQL语句
        
/// </summary>
        
/// <returns></returns>

        public string GenerateCountSql()
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
            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);
        }


img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
/**//// <summary>
        
/// 生成分页查询语句,包含记录总数
        
/// </summary>
        
/// <returns></returns>

        public string GenerateSqlIncludeTotalRecords()
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
            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;
        }


img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
/**//// <summary>
        
/// 生成分页查询语句
        
/// </summary>
        
/// <returns></returns>

        public override string GenerateSql()
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
            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以上版本的,希望这些代码对大家有用

目录
相关文章
|
17小时前
|
SQL 安全
jeecg-boot sql注入漏洞解决
jeecg-boot sql注入漏洞解决
20 0
|
17小时前
|
SQL 关系型数据库 MySQL
0基础学习SQL注入之万能账号密码(BUUctf例题-[极客大挑战 2019]EasySQL1)
0基础学习SQL注入之万能账号密码(BUUctf例题-[极客大挑战 2019]EasySQL1)
|
17小时前
|
SQL NoSQL 关系型数据库
一个基于 BigQuery 的 SQL 注入挖掘案例
一个基于 BigQuery 的 SQL 注入挖掘案例
6 0
|
17小时前
|
SQL 测试技术 网络安全
Python之SQLMap:自动SQL注入和渗透测试工具示例详解
Python之SQLMap:自动SQL注入和渗透测试工具示例详解
18 0
|
17小时前
|
SQL 安全 关系型数据库
SQL 注入神器:SQLMap 参数详解
SQL 注入神器:SQLMap 参数详解
|
17小时前
|
SQL 关系型数据库 MySQL
【MySQL-4】简述SQLの通用语法及4种基本语句介绍(DDL/DML/DQL/DCL)
【MySQL-4】简述SQLの通用语法及4种基本语句介绍(DDL/DML/DQL/DCL)
|
17小时前
|
SQL 存储 Java
如何避免SQL注入?
【4月更文挑战第30天】如何避免SQL注入?
25 0
|
17小时前
|
SQL 安全 PHP
【PHP 开发专栏】PHP 防止 SQL 注入的方
【4月更文挑战第30天】本文介绍了PHP防止SQL注入的策略,包括理解SQL注入的原理和危害,如数据泄露和系统控制。推荐使用参数化查询(如PDO扩展)、过滤和验证用户输入,以及选择安全的框架和库(如Laravel)。此外,强调了保持警惕、定期更新维护和开发人员安全培训的重要性,以确保应用安全。
|
17小时前
|
SQL 存储 安全
|
17小时前
|
SQL Oracle 关系型数据库
常见 SQL 注入绕过方法
常见 SQL 注入绕过方法