gin和gorm进阶功能(6)之安全相关 | 青训营笔记

483 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 10 天

SQL注入

SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

解决方法

  • 过滤输入内容,校验字符串:数据提交到数据库之前,对其中的不合法字符进行剔除。也可以验证输入数据的类型是否符合要求。
  • 参数化查询:目前被认为是最有效的预防sql注入方法。需要填入数据的地方,使用参数(Parameter)来给值 先把参数用?代替 ,再编译后再填入参数具体值。就算有破坏指令,也不会被执行
  • 安全测试、安全审计:开发中对代码进行审查,测试环节进行测试,上线后定期扫描安全漏洞

规范

  • 避免使用动态sql 最好使用准备好的语句和参数化查询 避免将用户输入的数据直接放入sql
  • 不要将敏感数据保留在纯文本中 加密数据库中的敏感数据 以防攻击者排出敏感数据
  • 限制数据库访问权限和特权
  • 避免直接向用户显示数据库报错 攻击者可能使用这些错误消息来获取数据库信息

下面使用的解决方法是参数化查询(推荐)。

代码

package dao  
  
import (  
   "go_douyin/database"  
   "go_douyin/model")  
  
//(以下代码可以防止SQL注入)  
// FollowMapper 自定义FollowMapper的类型,于follow实体类对应即可  
type FollowMapper struct{}  
  
func NewFollowMapper() *FollowMapper {  
   return &FollowMapper{}  
}  
  
// 显示关注列表  
func (FollowMapper) FollowFindList(userId uint64) []model.User {  
   var followedUsers []model.User  
   //查询数据(联表查询)  
   database.SqlDB.Table("tb_follow_list").Select("tb_users.*").Joins("JOIN tb_users ON tb_follow_list.follow_user_id = tb_users.user_id").Where("tb_follow_list.user_id = ?", userId).Scan(&followedUsers)  
   return followedUsers  
}  

XSS攻击

XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript。

解决方法

处理的过程是在服务端配置富文本标签和属性的白名单,不允许出现其他标签或属性(例如script、iframe、form等),即”XSS Filter“。然后在存储之前进行过滤。

配置

go get -u github.com/dvwright/xss-mw

代码

(直接使用第三方库) 关键

var xssMdlwr xss.XssMw  
router.Use(xssMdlwr.RemoveXss())  

完整代码 router/router.go

package router  
  
import (  
   "github.com/dvwright/xss-mw"  
   "github.com/gin-gonic/gin"   "go_douyin/controller"   "go_douyin/utils/cors")  
  
func SetupRouter() *gin.Engine {  
   router := gin.Default()  
   var xssMdlwr xss.XssMw  
   router.Use(xssMdlwr.RemoveXss())  
   userController := controller.NewUserController()  
  
   v1 := router.Group("/douyin/user")  
   {  
      v1.POST("register", userController.Register)  
      v1.POST("login", userController.Login)  
      v1.GET("/", userController.GetInfo)  
   }  
   //允许跨域  
   router.Use(cors.Next())  
   return router  
}

使用MD5加密密码

MD5的全称是md5信息摘要算法(英文:MD5 Message-Digest Algorithm ),一种被广泛使用的密码散列函数,可以产生一个128位(16字节,1字节8位)的散列值(常见的是用32位的16进制表示,比如:0caa3b23b8da53f9e4e041d95dc8fa2c),用于确保信息传输的完整一致。MD5由MD4、MD3、MD2改进而来,主要增强算法复杂度和不可逆性。MD5算法因其普遍、稳定、快速的特点,仍广泛应用于普通数据的加密保护领域。

代码

utils/md5_encrypt/md5_encrypt.go

package md5_encrypt  
  
import (  
   "crypto/md5"  
   "encoding/base64"   
   "encoding/hex")  
  
func MD5(params string) string {  
   md5Ctx := md5.New()  
   md5Ctx.Write([]byte(params))  
   return hex.EncodeToString(md5Ctx.Sum(nil))  
}  
  
//先base64,然后MD5  
func Base64Md5(params string) string {  
   return MD5(base64.StdEncoding.EncodeToString([]byte(params)))  
}

service/user/curd/userService.go(关键代码)

func (h *UserService) Register(user model.User) bool {  
   user.CreateTime = time.Now()  
   // 预先处理密码MD5加密  
   user.Password = md5_encrypt.Base64Md5(user.Password)  
   row := h.userMapper.Add(user)  
   if row > 0 {  
      return true  
   } else {  
      return false  
   }  
}  
  
func (h *UserService) Login(username string, password string) (bool, model.User, string) {  
   var user model.User = h.userMapper.Login(username, md5_encrypt.Base64Md5(password))  
   // 比较结构体是否为空  
   if reflect.DeepEqual(user, model.User{}) { //判断是否为空值  
      //fmt.Println("user is empty")  
      return false, user, ""  
   } else {  
      // 生成JWT  
      userTokenFactory := JWT.CreateUserFactory()  
      if userToken, err := userTokenFactory.GenerateToken(user.UserID, user.Username, 28800); err == nil {  
         return true, user, userToken  
      } else {  
         return false, user, ""  
      }  
  
   }  
}