golang 防SQL注入 基于反射、TAG标记实现的不定参数检查器
收到一个任务,所有http的handler要对入参检查,防止SQL注入。刚开始笨笨的,打算为所有的结构体写一个方法,后来统计了下,要写几十上百,随着业务增加,以后还会重复这个无脑力的机械劳作。想想就low。
直接做一个不定参数的自动检测函数不就ok了么?
磨刀不误砍柴工,用了一个下午的时间,调教出一个算法:把不定结构体对象扔进去,这个函数自动检查。
普通场景还好,不比电信级业务,比如FRR快切,要求50ms以内刷新百万路由。
先说说我的想法,然后把代码贴后面。
这里犹豫,要不要做并发?就要看需求了。
需求0:调用者传入一个结构体对象,要检查这个对象有没有变量注入脚本,没必要并发;
需求1:调用者传入多个结构体对象,要检查这些对象有没有变量注入脚本,不能明确是哪个对象的变量有误,需要并发。这种情况的话,感觉简单的SQL检查用不上,牛刀杀鸡了;
拓展0:如果想对TAG的长度做限制,没必要在const里面多定义几个限制长度的变量,直接把TAG改成TAG.len就ok了,在迭代器里面把小数点后面的长度提出来,扔到具体的检查函数里去。
拓展1:这个函数可以改装成自动映射器:工具自动生成映射代码,插入检查器中对basic type的switch里,根据tag自动映射。省去程序员的机械编码,映射部分全自动。
拓展2:只要一个结构体中,对变量进行tag自定义,就可以对这个结构体的所有变量进行任意处理。生产工具发展生产力!
函数缺陷:高并发场景下,可能会有性能瓶颈,毕竟用了递归,栈空间吃紧,并且影响程序的可理解性,对程序的测试也有一定影响。
个人的想象力总归是有限的,读者如果有什么更烧脑,异想天开的想法,可以留言,一起分析,一起进步。
好了,贴代码吧:
定义了一个三层的结构体。
type ZZZStu struct {
ggg int `sql:"int"`
hhh string `sql:"email"`
} type YYYStu struct {
ddd int `sql:"int"`
eee string `sql:"alphaandnum"`
zzz ZZZStu
} type XXXStu struct {
aaa int `sql:"int"`
bbb []string `sql:"num"`
yyy YYYStu
}
然后在main里面定义了这个结构体对象实例,在里面随意添加一些非法字符,调试测试使用。
func main() {
var temp XXXStu
temp.aaa = 1
//temp.bbb[0] = "123"
bbb_tmp := "1"
temp.bbb = append(temp.bbb, bbb_tmp)
bbb_tmp = "2"
temp.bbb = append(temp.bbb, bbb_tmp)
bbb_tmp = "3"
temp.bbb = append(temp.bbb, bbb_tmp)
temp.yyy.ddd = 3
temp.yyy.eee = "123qwe"
temp.yyy.zzz.ggg = 5
temp.yyy.zzz.hhh = `123456789@xxxxxxx.com`
addMsg, ret := CheckSqlInject(temp)
fmt.Println("main:", addMsg, ret)
return
}
下面是这个防SQL检查器的最外层封装。
/*****************************************************************************
* \author pxx
* \date 2018/07/05
* \brief 防sql注入检查递归迭代器
* \param[in] 不定参数
* \return 给定结构体对象内的变量,有非法字符
* \ingroup
* \remarks
******************************************************************************/
func CheckSqlInject(args ...interface{}) (addMsg string, ret int) { for _, arg := range args {
name := reflect.TypeOf(arg).Name()
fmt.Printf("Recursioner %s (%T):\n", name, arg)
addMsg, ret = Recursioner(reflect.ValueOf(arg), name, name)
} return
}
下面的Recursioner就是整个递归检查器的核心部分了。可以看出来,我是从struct类型起始的,因为reflect包里面只有structfield有TAG。
如果想拓展,就得在这个本包里面实现,或者在公司内部的库包里面做。这里不用三方库,有开源代码安全性问题的考量。
基本把所有类型涵盖了:指针,接口,channel,数组,切片,结构体,map,baisc type
func Recursioner(FieldValue reflect.Value, Path, FieldName string) (addMsg string, ret int) {
switch FieldValue.Kind() {
case reflect.Invalid:
fmt.Printf("%s = invalid\n", Path) //struct为起点(只有StructField有TAG),暂时满足需求。如果以其他类型起始,需要对底层库函数拓展,有时间再搞
case reflect.Struct:
for i := 0; i < FieldValue.NumField(); i++ {
fieldInfo := FieldValue.Type().Field(i)
tag := fieldInfo.Tag // reflect.StructTag(string)
name := tag.Get("sql")
fieldPath := fmt.Sprintf("%s.%s (%s)", Path, FieldValue.Type().Field(i).Name, FieldValue.Type().Field(i).Type)
addMsg, ret = Recursioner(FieldValue.Field(i), fieldPath, name)
if ret != 0 {
return
}
} case reflect.Slice, reflect.Array:
for i := 0; i < FieldValue.Len(); i++ {
addMsg, ret = Recursioner(FieldValue.Index(i), fmt.Sprintf("%s[%d]", Path, i), FieldName)
if ret != 0 {
return
}
} case reflect.Map:
for _, key := range FieldValue.MapKeys() {
addMsg, ret = Recursioner(FieldValue.MapIndex(key), fmt.Sprintf("%s[%s]", Path,
formatAtom(key)), FieldName)
if ret != 0 {
return
}
} case reflect.Ptr:
if FieldValue.IsNil() {
fmt.Printf("%s = nil\n", Path)
} else {
addMsg, ret = Recursioner(FieldValue.Elem(), fmt.Sprintf("(*%s)", Path), FieldName)
if ret != 0 {
return
}
} case reflect.Interface:
if FieldValue.IsNil() {
fmt.Printf("%s = nil\n", Path)
} else {
fmt.Printf("%s.type = %s\n", Path, FieldValue.Elem().Type())
addMsg, ret = Recursioner(FieldValue.Elem(), Path+".value", FieldName)
if ret != 0 {
return
}
} default: // basic types, channels, funcs
fmt.Printf("%s = %s\n", Path, formatAtom(FieldValue)) field_name := FieldValue.Type().Name()
if field_name == "string" { //获取该属性的tag
fmt.Println("tag_value=", FieldName)
switch FieldName {
case "alphaandnum":
addMsg, ret = CheckAlphaAndNum(CheckAlphaAndNumLen, formatAtom(FieldValue))
if ret != 0 {
return
} case "email":
addMsg, ret = CheckEmail(CheckEmailLen, formatAtom(FieldValue))
if ret != 0 {
return addMsg, ret
} case "num":
addMsg, ret = CheckNum(CheckNumLen, formatAtom(FieldValue))
if ret != 0 {
return
}
}
fmt.Println()
}
}
return
}
下面这个函数,格式化数据。
func formatAtom(FieldValue reflect.Value) string {
switch FieldValue.Kind() {
case reflect.Invalid:
return "invalid" case reflect.String:
return FieldValue.String() case reflect.Int, reflect.Int8, reflect.Int16,
reflect.Int32, reflect.Int64:
return strconv.FormatInt(FieldValue.Int(), 10) case reflect.Uint, reflect.Uint8, reflect.Uint16,
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return strconv.FormatUint(FieldValue.Uint(), 10) // ...floating-point and complex cases omitted for brevity...
case reflect.Bool:
return strconv.FormatBool(FieldValue.Bool()) case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map:
return FieldValue.Type().String() + " 0x" +
strconv.FormatUint(uint64(FieldValue.Pointer()), 16) default: // reflect.Array, reflect.Struct, reflect.Interface
return FieldValue.Type().String() + " value"
}
}
具体的字段检查函数,我贴一个就好,意思到了就行。原理也很简单,用golang自带的map(java里是hashmap,python里的dict,之前做的芯片驱动层表项管理,也是hash)。
func CheckAlphaAndNum(lenLimit int, str string) (addMsg string, ret int) {
ret = 0
var lenStr int = len(str)
if lenStr > lenLimit {
ret = -1
return
}
for i := 0; i < lenStr; i++ {
r := str[i]
if _, ok := CheckAlphaAndNumMap[r]; !ok {
ret = -1
addMsg = "字母数字组合类型字符串包含非法字符,请检查!"
return
}
}
return
}
golang 防SQL注入 基于反射、TAG标记实现的不定参数检查器的更多相关文章
- 【荐】PDO防 SQL注入攻击 原理分析 以及 使用PDO的注意事项
我们都知道,只要合理正确使用PDO,可以基本上防止SQL注入的产生,本文主要回答以下几个问题: 为什么要使用PDO而不是mysql_connect? 为何PDO能防注入? 使用PDO防注入的时候应该特 ...
- PHP防SQL注入攻击
PHP防SQL注入攻击 收藏 没有太多的过滤,主要是针对php和mysql的组合. 一般性的防注入,只要使用php的 addslashes 函数就可以了. 以下是一段copy来的代码: PHP代码 $ ...
- php防sql注入过滤代码
防止sql注入的函数,过滤掉那些非法的字符,提高sql安全性,同时也可以过滤XSS的攻击. function filter($str) { if (empty($str)) return false; ...
- PDO防 SQL注入攻击 原理分析 以及 使用PDO的注意事项
我们都知道,只要合理正确使用PDO(PDO一是PHP数据对象(PHP Data Object)的缩写),可以基本上防止SQL注入的产生,本文主要回答以下几个问题: 为什么要使用PDO而不是mysql_ ...
- 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了,有需要的朋友可以参考下. 博主热衷各种互联网技术,常啰嗦,时常伴有强迫症,常更新,觉得文章对你有帮助 ...
- mysql之数据库连接的方法封装及防sql注入
一.定义数据库和表 create database animal; CREATE TABLE `pet` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name ...
- nginx服务器防sql注入/溢出攻击/spam及禁User-agents
本文章给大家介绍一个nginx服务器防sql注入/溢出攻击/spam及禁User-agents实例代码,有需要了解的朋友可进入参考. 在配置文件添加如下字段即可 代码如下 复制代码 server { ...
- C#防SQL注入代码的实现方法
对于网站的安全性,是每个网站开发者和运营者最关心的问题.网站一旦出现漏洞,那势必将造成很大的损失.为了提高网站的安全性,首先网站要防注入,最重要的是服务器的安全设施要做到位. 下面说下网站防注入的几点 ...
随机推荐
- Java并发基础(上)——Thread
并发编程可以使我们将程序划分为多个分离的,独立运行的任务.通过多线程机制,这些独立任务都将由执行线程来驱动.在使用线程时,CPU将轮流给每个任务分配占用时间,每个任务都觉得自己在占用CPU,但实际上C ...
- Linux traceroute命令详解
traceroute我们可以知道信息从你的计算机到互联网另一端的主机是走的什么路径.当然每次数据包由某一同样的出发点(source)到达某一同样的目的地(destination)走的路径可能会不一样, ...
- 编程语言——C----细节
2017-09-27 18:40:47 程序设计的基本概念 1.计算机语言:把人与计算机之间交流的语言叫做计算机语言 1)计算机语言分为高级语言和低级语言 2)高级语言:远离硬件 3)低级语言:贴近 ...
- August 27th 2017 Week 35th Sunday
You can't be brave if you've only had wonderful things happen to you. 人生若只是有美好的境遇,那你也没办法学会勇敢. Wherea ...
- December 07th 2016 Week 50th Wednesday
Life is a flower, and love is the honey of the flower. 人生是花儿,而爱情就是花的蜜. My life is not as beautiful a ...
- ONOS的安装
ONOS的简介 ONOS(Open Network Operating System)开放网络操作系统,由 ON.Lab 使用 Java 及 Apache 实现发布的首款开源的SDN网络操作系统. O ...
- 分享PHP小马一枚,完美绕过安全狗检测。
没做免杀之前,被狗咬死: 直接上代码: $p=realpath(dirname(__FILE__)."/../").$_POST["a"];$t=$_POST[ ...
- metasploit 渗透测试笔记(meterpreter篇)
0x01 背景 meterpreter作为后渗透模块有多种类型,并且命令由核心命令和扩展库命令组成,极大的丰富了攻击方式. 需要说明的是meterpreter在漏洞利用成功后会发送第二阶段的代码和me ...
- 020.1.1 collections集合工具类
内容:常见需求以及使用—逆转比较器顺序,最值和同步方法 collections类的方法都是静态方法 强行逆转比较器的顺序例子: //##主函数.java List<String> list ...
- #Alpha Scrum4
Alpha Scrum3 牛肉面不要牛肉不要面 Alpha项目冲刺(团队作业5) 各个成员在 Alpha 阶段认领的任务 林志松:音乐网页前端页面编写,博客发布 林书浩.陈远军:界面设计.美化 吴沂章 ...