作为后端开发者我们要记住一句话:“永远不要相信用户的输入”,这里所说的用户可能是人,也可能是另一个应用程序。“永远不要相信用户的输入”是安全编码的准则,也就是说,任何输入的内容在验证无害之前都是有害的。很多应用程序的安全漏洞都和用户输入有关,比如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'='1dataSql中拼接的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开发:永远不要相信用户的输入的更多相关文章

  1. 基于gin的golang web开发:路由

    Gin是一个用Golang编写的HTTP网络框架.它的特点是类似于Martini的API,性能更好.在golang web开发领域是一个非常热门的web框架. 启动一个Gin web服务器 使用下面的 ...

  2. 基于gin的golang web开发:路由二

    在基于gin的golang web开发:路由中我们介绍了Gin的路由和一些获取链接中参数的方法,本文继续介绍其他获取参数的方法. 文件上传 在web开发中文件上传是一个很常见的需求,下面我们来看一下基 ...

  3. 基于gin的golang web开发:模型绑定

    在前两篇文章介绍路由的时候,我们了解到gin可用通过类似DefaultQuery或DefaultPostForm等方法获取到前端提交过来的参数.参数不多的情况下也很好用,但是想想看,如果接口有很多个参 ...

  4. 基于gin的golang web开发:模型验证

    Gin除了模型绑定还提供了模型验证功能.你可以给字段指定特定的规则标签,如果一个字段用binding:"required"标签修饰,在绑定时该字段的值为空,那么将返回一个错误.开发 ...

  5. 基于gin的golang web开发:访问mysql数据库

    web开发基本都离不开访问数据库,在Gin中使用mysql数据库需要依赖mysql的驱动.直接使用驱动提供的API就要写很多样板代码.你可以找到很多扩展包这里介绍的是jmoiron/sqlx.另外还有 ...

  6. 基于gin的golang web开发:使用数据库事务

    在前文介绍访问数据库时介绍了github.com/jmoiron/sqlx包,本文基于这个包使用数据库事务. defer 在使用数据库事务之前,首先需要了解go语言的defer关键字.defer是go ...

  7. 基于gin的golang web开发:mysql增删改查

    Go语言访问mysql数据库需要用到标准库database/sql和mysql的驱动.标准库的Api使用比较繁琐这里再引入另一个库github.com/jmoiron/sqlx. go get git ...

  8. 基于gin的golang web开发:中间件

    gin中间件(middleware)提供了类似于面向切面编程或路由拦截器的功能,可以在请求前和请求之后添加一些自定义逻辑.实际开发中有很多场景会用到中间件,例如:权限验证,缓存,错误处理,日志,事务等 ...

  9. 基于gin的golang web开发:集成swagger

    在前后端分离的项目维护一份完整且及时更新的api文档会极大的提高我们的工作效率,传统项目中接口文档都是由后端开发手写的,这种文档很难保证及时性,久而久之便失去了参考意义.swagger给我们提供了一种 ...

随机推荐

  1. 文献管理软件 Zotero 安装、配置与使用

    简介 Zotero优缺点 使用Zotero作为主力文献管理工具的原因: 软件本身完全免费并且开源,不存在盗版问题 注册后本身只包括 300M 空间同步,但支持 WebDAV 同步,例如 Dropbox ...

  2. 3.5 MyLinkedList 实现

    3.5 MyLinkedList 类的实现 MyLinkedList 将用双链表实现,并且还需要保留该表两端的引用.这将需要三个类 MyLinkedList 类,包含到两端的链.表的大小以及一些方法. ...

  3. leetcode95:jump game

    题目描述 给出一个非负整数数组,你最初在数组第一个元素的位置 数组中的元素代表你在这个位置可以跳跃的最大长度 判断你是否能到达数组最后一个元素的位置 例如 A =[2,3,1,1,4], 返回 tru ...

  4. JS+CSS+HTML实现“代码雨”类似黑客帝国文字下落效果

    HTML代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <l ...

  5. 腾讯云--对象存储cos绑定自定义域名

    1.登录腾讯云控制台,找到对象存储一栏 2.选择一个你想绑定域名的存储桶 3.进入你选择的存储桶,点击域名管理 4.选择自定义源站域名.在域名处填写你要设置的自定义域名,在源站类型处选择静态网站源站, ...

  6. slideUp和slideDown的区别

    slideUp():通过使用滑动效果,隐藏被选元素,如果元素已显示出来的话.语法:$(selector).slideUp(speed,callback).speed:可选,表示动画运行的时候.call ...

  7. 源码分析:同步基础框架——AbstractQueuedSynchronizer(AQS)

    简介 AQS 全称是 AbstractQueuedSynchronizer,位于java.util.concurrent.locks 包下面,AQS 提供了一个基于FIFO的队列和维护了一个状态sta ...

  8. 154. Find Minimum in Rotated Sorted Array II(循环数组查找)

    Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. (i.e. ...

  9. peterson算法(软件互斥 转)

    1. 背景        首先,看个例子,进程P1,P2共用一个变量COUNT,初始值为0                                                 因为P1,P ...

  10. android打包持续集成

    背景: 领导下发需求,要求客户端实现代码自动上传至github的需求,本来我这项目是使用jenkins实现自动化打包操作的,由于两端不统一,再加上如果需要公司所有人看到包,需要给所有人开账号有些许不切 ...