前些时间看了玉开兄的“如此高效通用的分页存储过程是带有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注入:生成参数化的通用分页查询语句的更多相关文章
-
C#防SQL注入代码的实现方法
对于网站的安全性,是每个网站开发者和运营者最关心的问题.网站一旦出现漏洞,那势必将造成很大的损失.为了提高网站的安全性,首先网站要防注入,最重要的是服务器的安全设施要做到位. 下面说下网站防注入的几点 ...
-
Sqlparameter防SQL注入
一.SQL注入的原因 随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多.但是由于这个行业的入门门槛不高,程序员的水平及经验也参差不齐,相当大一部分程序员在编写代码的时候,没有对 ...
-
golang 防SQL注入 基于反射、TAG标记实现的不定参数检查器
收到一个任务,所有http的handler要对入参检查,防止SQL注入.刚开始笨笨的,打算为所有的结构体写一个方法,后来统计了下,要写几十上百,随着业务增加,以后还会重复这个无脑力的机械劳作.想想就l ...
-
.Net防sql注入的方法总结
#防sql注入的常用方法: 1.服务端对前端传过来的参数值进行类型验证: 2.服务端执行sql,使用参数化传值,而不要使用sql字符串拼接: 3.服务端对前端传过来的数据进行sql关键词过来与检测: ...
-
SpringBoot微服务电商项目开发实战 --- api接口安全算法、AOP切面及防SQL注入实现
上一篇主要讲了整个项目的子模块及第三方依赖的版本号统一管理维护,数据库对接及缓存(Redis)接入,今天我来说说过滤器配置及拦截设置.接口安全处理.AOP切面实现等.作为电商项目,不仅要求考虑高并发带 ...
-
PDO防sql注入原理分析
使用pdo的预处理方式可以避免sql注入. 在php手册中'PDO--预处理语句与存储过程'下的说明: 很多更成熟的数据库都支持预处理语句的概念.什么是预处理语句?可以把它看作是想要运行的 SQL 的 ...
-
【荐】PDO防 SQL注入攻击 原理分析 以及 使用PDO的注意事项
我们都知道,只要合理正确使用PDO,可以基本上防止SQL注入的产生,本文主要回答以下几个问题: 为什么要使用PDO而不是mysql_connect? 为何PDO能防注入? 使用PDO防注入的时候应该特 ...
-
C#语言Winform防SQl注入做用户登录的例子
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...
-
PHP防SQL注入不要再用addslashes和mysql_real_escape_string
PHP防SQL注入不要再用addslashes和mysql_real_escape_string了,有需要的朋友可以参考下. 博主热衷各种互联网技术,常啰嗦,时常伴有强迫症,常更新,觉得文章对你有帮助 ...
随机推荐
-
相似性度量(Similarity Measurement)与“距离”(Distance)
在做分类时常常需要估算不同样本之间的相似性度量(Similarity Measurement),这时通常采用的方法就是计算样本间的“距离”(Distance).采用什么样的方法计算距离是很讲究,甚至关 ...
-
LoadRunner ---检查点
判断脚本是否执行成功是根据服务器返回的状态来确定的,如果服务器返回的HTTP状态为 200 OK ,那么VuGen 就认为脚本正确地运行了,并且是运行通过的.在绝大多数系统出错时会返回错误页面码? 不 ...
-
Android开发——通过扫描二维码,打开或者下载Android应用
Android开发——通过扫描二维码,打开或者下载Android应用 在实现这个功能的时候,被不同的浏览器折磨的胃疼,最后实现了勉强能用,也查考了一下其他人的博客 android实现通过浏览器点击 ...
-
自己写loader
http://www.cnblogs.com/lynxcat/archive/2013/03/08/2950373.html http://addyosmani.com/blog/building-a ...
-
【转】 UIButton上使用UIEdgeInsetsMaketitle跟图片对齐
[转]http://blog.csdn.net/yanxiaoqing/article/details/7230660 默认情况下,不设置的效果,都使居中现实,button为150*150 使用以下设 ...
-
let内嵌lambda使用set!构成闭包
查了半天没有找到scheme中判断数据类型的函数,索性自己写了个type?,发现闭包和递归有着微妙的联系. 本例中,*变量是types,外层let初始化了types的值,内层let里的(set! t ...
-
springboot 的部分细节
Application.properties 中#指定端口号 server.port= #指定访问路径必须以/crud/xxx 开始 server.servlet.context-path=/crud ...
-
spark 基础开发 Tips总结
本篇博客主要是 sparksql 从初始开发注意的一些基本点以及力所能及的可优化部分的介绍: 所使用spark版本:2.0.0 scala版本:2.11.8 1. SparkSession ...
-
chromedriver 下载地址
重要的事情说三遍 chromedriver 下载地址 chromedriver 下载地址 chromedriver 下载地址 http://chromedriver.storage.googleapi ...
-
【pyhon】Python里的字符串查找函数find和java,js里的indexOf相似,找到返回序号,找不到返回-1
# 例子: target='www.163.com' ')) ')==-1: print('263不存在于字符串'+target+'中') 运行: C:\Users\horn1\Desktop\pyt ...