防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注入:生成参数化的通用分页查询语句的更多相关文章
- 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了,有需要的朋友可以参考下. 博主热衷各种互联网技术,常啰嗦,时常伴有强迫症,常更新,觉得文章对你有帮助 ...
随机推荐
- HDU 3415 Max Sum of Max-K-sub-sequence(单调队列)
转载请注明出处:http://blog.csdn.net/u012860063 Max Sum of Max-K-sub-sequence Time Limit: 2000/1000 MS (Java ...
- zoj 3696 Alien's Organ(泊松分布)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3696 Alien's Organ Time Limit: 2 S ...
- python基础课程_学习笔记26:编程的乐趣
编程的乐趣 编程柔术 当你坐下来,打算如何组织计划要定时,具体程序,然而,无论什么经验.在实现时间的函数的,你会逐渐学会了原来的设计,实用的新知识.我们不应该忽视沿途汲取的教训,相反,它们用于其他设计 ...
- Hadoop Mapreduce刮
前言 的一个渣渣程序猿一枚,因为个人工作,须要常常和hadoop打交道,可是自己之前没有接触过hadoop.所以算是边学边用,这个博客算是记录一下学习历程,梳理一下自己的思路,请各位看官轻拍.本博客大 ...
- Android在ListView滑动数据混乱
我相信做过Android应用程序开发或多或少都遇到了这个问题.或者是在ListView数据损坏幻灯片事件.要么GridView数据损坏幻灯片事件. 让我们来看看一个网友写的文章,个人感觉还不错的文章: ...
- Quartz(GUI)图形界面程序----Quartz Web
下载.设置和运行Quartz(GUI)图形界面程序----Quartz Web 一.获取Quartz Web程序(Quartz GUI).早期的 Quartz 框架开发者意识到一个 GUI 对于某类用 ...
- Duanxx的Altium Designer学习:PCB试想一下,在目前的水平
1 Shift+S 这个快捷键能高亮当前层,而且使其它层变成灰色.见下图: 2 隐藏指定层 在图中右下角的地方,右键.会弹出一个选项条.选择Hide Layers.能够选择想要隐藏的 ...
- java这些东西发展(1)-------大约ORA00604和ORA12705
******************************有关myEclipse和oracle在连接发生的一个问题********************************* 用户界面显示的评 ...
- 2014年百度之星程序设计大赛 - 资格赛 1002 Disk Schedule(双调欧几里得旅行商问题)
Problem Description 有非常多从磁盘读取数据的需求,包含顺序读取.随机读取.为了提高效率,须要人为安排磁盘读取.然而,在现实中,这样的做法非常复杂.我们考虑一个相对简单的场景.磁盘有 ...
- 13 于C#如何获得在IP住址
首先,需要加入一个命名空间 using System.Net.NetworkInformation; using System.Net.Sockets; 以下是具体代码 GetPrivateIP(); ...