基于gin的golang web开发:永远不要相信用户的输入
作为后端开发者我们要记住一句话:“永远不要相信用户的输入”,这里所说的用户可能是人,也可能是另一个应用程序。“永远不要相信用户的输入”是安全编码的准则,也就是说,任何输入的内容在验证无害之前都是有害的。很多应用程序的安全漏洞都和用户输入有关,比如SQL注入漏洞。
我们可以通过参数验证、sql语句过滤和参数化查询等方式对用户的输入进行处理来规避这种安全隐患。本文介绍第一种方法,并对基于gin的golang web开发:模型验证进行补充,了解更多的参数验证方法。
验证非必填的邮箱字段
需求是这样的:我们需要验证一个字段可以为空,同时字段的值为合法的电子邮箱。
type MailRequest struct {
Email string `json:"email" binding:"email"` // 邮箱地址
}
代码的执行结果和我们想的不太一样,我们没有为字段设置required标签,但是传入空字符串时会提示Email必须是一个有效的邮箱,解决方法是加入omitempty验证规则。omitempty允许条件验证,在没有为字段设置值的情况下,跳过后面的验证规则。注意omitempty要放在其他规则前面。下面是修改后的代码:
type MailRequest struct {
Email string `json:"email" binding:"omitempty,email"` // 邮箱地址
}
验证0值
先看代码
type AddRoleRequest struct {
Available int `json:"available" binding:"required"` // 是否可用 0 不可用 1 可用
}
Available 字段为int类型,添加了required验证规则,0为一个有效的值。Available为0时不能通过Gin的参数验证。这里只需要把字段类型修改为*int即可。
type AddRoleRequest struct {
Available *int `json:"available" binding:"required"` // 是否可用 0 不可用 1 可用
}
自定义错误消息
前文基于gin的golang web开发:模型验证结尾部分,我们没有把参数验证的错误消息完全翻译成中文,字段名还是英文的。显然还有更优雅的做法,给用户提示一个更友好的错误信息。
{
"error": "Username为必填字段;"
}
返回值中的Username为字段名称,可以通过自定义标签的方式修改错误信息中的字段名。我们自定义一个display标签,然后使用标签的值替换掉验证器中的字段。
func init() {
translator := zh.New()
uni = ut.New(translator, translator)
trans, _ = uni.GetTranslator("zh")
validate := binding.Validator.Engine().(*validator.Validate)
// 注意这里
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
return fld.Tag.Get("display")
})
_ = zh_translations.RegisterDefaultTranslations(validate, trans)
}
func Translate(err error) string {
var result string
errors := err.(validator.ValidationErrors)
for _, err := range errors {
errMessage := err.Translate(trans)
result += errMessage + ";"
}
return result[:len(result)-1] // <--
}
type AddUserRequest struct {
Username string `json:"username" binding:"required" display:"用户名"`
Password string `json:"password" binding:"required" display:"密码"` // 登录密码
Nickname string `json:"nickname" binding:"required" display:"昵称"` // 昵称
}
注意代码中validate.RegisterTagNameFunc方法注册display标签,Translate方法也有一些改进,去掉了结果中最后一个分号。
举个栗子
func checkUser(user string, password string) bool {
db := GetDbContext()
defer db.Close()
dataSql := `
select count(1) from sys_user
where username = '` + user + `' and password = '` + password + `'`
count := 0
log.Println(dataSql)
db.QueryRow(dataSql).Scan(&count)
return count > 0
}
这段代码用于判断账号密码是否正确,但是没有验证用户输入的user和password参数,恶意用户构造一个特殊的密码1' or '1'='1,dataSql中拼接的sql语句变为
select count(1) from sys_user
where username = 'xxx' and password = '1' or '1'='1'
查询结果大于0,方法返回真。这就造成了sql注入。我们可以在password字段上增加规则alphanum验证字段内容只能为字母或数字。1' or '1'='1不能通过参数验证也就规避掉了SQL注入的问题。例子中拼接SQL语句是为了方便演示,正式项目中不推荐这种写法。完整代码如下:
type UserAndPassword struct {
User string `json:"user" binding:"required,alphanum"`
Pwd string `json:"pwd" binding:"required,alphanum"`
}
func IsLoginIn(c *gin.Context) {
var req = UserAndPassword{}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.String(http.StatusOK, strconv.FormatBool(checkUser(req.User, req.Pwd)))
}
func checkUser(user string, password string) bool {
db := GetDbContext()
defer db.Close()
dataSql := `
select count(1) from sys_user
where username = '` + user + `' and password = '` + password + `'`
count := 0
log.Println(dataSql)
db.QueryRow(dataSql).Scan(&count)
return count > 0
}
文章出处:基于gin的golang web开发:永远不要相信用户的输入
基于gin的golang web开发:永远不要相信用户的输入的更多相关文章
- 基于gin的golang web开发:路由
Gin是一个用Golang编写的HTTP网络框架.它的特点是类似于Martini的API,性能更好.在golang web开发领域是一个非常热门的web框架. 启动一个Gin web服务器 使用下面的 ...
- 基于gin的golang web开发:路由二
在基于gin的golang web开发:路由中我们介绍了Gin的路由和一些获取链接中参数的方法,本文继续介绍其他获取参数的方法. 文件上传 在web开发中文件上传是一个很常见的需求,下面我们来看一下基 ...
- 基于gin的golang web开发:模型绑定
在前两篇文章介绍路由的时候,我们了解到gin可用通过类似DefaultQuery或DefaultPostForm等方法获取到前端提交过来的参数.参数不多的情况下也很好用,但是想想看,如果接口有很多个参 ...
- 基于gin的golang web开发:模型验证
Gin除了模型绑定还提供了模型验证功能.你可以给字段指定特定的规则标签,如果一个字段用binding:"required"标签修饰,在绑定时该字段的值为空,那么将返回一个错误.开发 ...
- 基于gin的golang web开发:访问mysql数据库
web开发基本都离不开访问数据库,在Gin中使用mysql数据库需要依赖mysql的驱动.直接使用驱动提供的API就要写很多样板代码.你可以找到很多扩展包这里介绍的是jmoiron/sqlx.另外还有 ...
- 基于gin的golang web开发:使用数据库事务
在前文介绍访问数据库时介绍了github.com/jmoiron/sqlx包,本文基于这个包使用数据库事务. defer 在使用数据库事务之前,首先需要了解go语言的defer关键字.defer是go ...
- 基于gin的golang web开发:mysql增删改查
Go语言访问mysql数据库需要用到标准库database/sql和mysql的驱动.标准库的Api使用比较繁琐这里再引入另一个库github.com/jmoiron/sqlx. go get git ...
- 基于gin的golang web开发:中间件
gin中间件(middleware)提供了类似于面向切面编程或路由拦截器的功能,可以在请求前和请求之后添加一些自定义逻辑.实际开发中有很多场景会用到中间件,例如:权限验证,缓存,错误处理,日志,事务等 ...
- 基于gin的golang web开发:集成swagger
在前后端分离的项目维护一份完整且及时更新的api文档会极大的提高我们的工作效率,传统项目中接口文档都是由后端开发手写的,这种文档很难保证及时性,久而久之便失去了参考意义.swagger给我们提供了一种 ...
随机推荐
- day03基本数据类型
运算符补充 今天补充一些简单知识: #!/usr/bin/env python # -*- coding:utf-8 -*- value = "我是中国人" # 判断'中国'是 ...
- js 值类型与引用类型
说明之前先提一个提问题,看一下你是怎么理解的 1. 值类型 简单的数据类型,存放在栈中 var num = 100; var num2 = num; num += 100; console.log(n ...
- Linux文件操作常用命令
一.一些文件操作命令. 1.cd /home 进入"home目录" 2.cd ../ 返回上一级目录 3.cd - 返回上次所在的目录 4.pwd 显示工程路径 5.ll 显示 ...
- php反序列化漏洞入门
前言 这篇讲反序列化,可能不会很高深,我之前就被反序列化整懵逼了. 直到现在我对反序列化还是不够深入,今天就刚好可以研究研究. 0x01.反序列化漏洞介绍 序列化在内部没有漏洞,漏洞产生是应该程序在处 ...
- 从创建进程到进入main函数,发生了什么?
前几天,读者群里有小伙伴提问:从进程创建后,到底是怎么进入我写的main函数的? 今天这篇文章就来聊聊这个话题. 首先先划定一下这个问题的讨论范围:C/C++语言 这篇文章主要讨论的是操作系统层面上对 ...
- 配置交换机之间直连链路聚合-LACP模式
组网图形 LACP模式链路聚合简介 以太网链路聚合是指将多条以太网物理链路捆绑在一起成为一条逻辑链路,从而实现增加链路带宽的目的.链路聚合分为手工模式和LACP模式. LACP模式需要有链路聚合控制协 ...
- Android 教你如何发现 APP 卡顿
最近部门打算优化下 APP 在低端机上的卡顿情况,既然想优化,就必须获取卡顿情况,那么如何获取卡顿情况就是本文目的. 一般主线程过多的 UI 绘制.大量的 IO 操作或是大量的计算操作占用 CPU,导 ...
- 用 Java 训练深度学习模型,原来可以这么简单!
本文适合有 Java 基础的人群 作者:DJL-Keerthan&Lanking HelloGitHub 推出的<讲解开源项目> 系列.这一期是由亚马逊工程师:Keerthan V ...
- Learn day5 迭代器\生成器\高阶函数\推导式\内置函数\模块(math.time)
1.迭代器 # ### 迭代器 """能被next调用,并不断返回下一个值的对象""" """ 特征:迭代器会 ...
- 【QT】 QThread部分源码浅析
本文章挑出QThread源码中部分重点代码来说明QThread启动到结束的过程是怎么调度的.其次因为到了Qt4.4版本,Qt的多线程就有所变化,所以本章会以Qt4.0.1和Qt5.6.2版本的源码来进 ...