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

    快速传送 手撸ORM浅谈ORM框架之基础篇 手撸ORM浅谈ORM框架之Add篇 手撸ORM浅谈ORM框架之Update篇 手撸ORM浅谈ORM框架之Delete篇 手撸ORM浅谈ORM框架之Query ...

  2. 欧拉计划第十一题--java实现

    先做个说明,里面所有的题都是我自己写的解题报告,由于我的能力有限,是个刚学java的小白,有很多不足的地方,还望各位大佬不奢赐教,谢谢! Largest product in a grid In th ...

  3. Navicat连接远程MySQL8.0数据库

    前言: 如果你有一台服务器,并且安装了Mysql8.0及以上版本数据库.此时想通过本地Navicat软件连接远程服务器上的mysql数据库.那么接下来你就要完成以下准备工作: 登录远程服务器上的数据库 ...

  4. pc和移动与ipad自适应布局的相关问题和解决

    一.通过CSS检测本机设备屏幕大小分配样式 1.最小尺寸分辨率1024*768(传统17寸显示器),则可以采用940px.960px.或者常用的980px作为最小宽度. ---- 在可视区域的宽度小于 ...

  5. Kubernetes Controller详解

    运行容器化应用是Kubernetes最重要的核心功能.为满足不同的业务需要,Kubernetes提供了多种Controller,主要包括Deployment.DaemonSet.Job.CronJob ...

  6. NB-IoT的eDRX模式主要目的是什么

    传统的2.56秒寻呼间隔对UE的电量消耗较大,NB-IoT的eDRX模式主要目的就是支能够持更长周期的寻呼监听,从而达到省电的目的.而在下行数据发送频率小时,通过核心网和用户终端的协商配合,用户终端调 ...

  7. ant-design-vue中tree增删改

    ant-design-vue中tree增删改 1. 使用背景 新项目中使用了ant-design-vue组件库.该组件库完全根基数据双向绑定的模式实现.只有表单组件提供少量的方法.所以,在使用ant- ...

  8. 【Kata Daily 190929】Password Hashes(密码哈希)

    题目: When you sign up for an account somewhere, some websites do not actually store your password in ...

  9. javaweb项目中jsp的from表单提交action内容与web.xml的servlet-mapping对应

    login.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> ...

  10. Java多线程技术:实现多用户服务端Socket通信

    目录 前言回顾 一.多用户服务器 二.使用线程池实现服务端多线程 1.单线程版本 2.多线程版本 三.多用户与服务端通信演示 四.多用户服务器完整代码 最后 前言回顾 在上一篇<Java多线程实 ...