【Go知识点】Gorm Hook 无侵入实现 数据表防篡改

news2025/1/15 8:13:05

在这里插入图片描述

一、前言

Hi,开门见山的说,这次给大家带来的是关于 Gorm Hook 机制的落地场景,笔者也是在Gorm官方文档中了解到有Hook机制的存在,不过一直没有找到过太多合适的场景来使用。

最近刚好在做一块新业务的设计,因为涉及到金融相关属性,对于非业务逻辑层面的数据库篡改可能会导致的资损问题进行了重新分析,之前的签名操作是直接hard code编码在代码中的,存在诸多问题:

  • 手动挨个方法添加签名和验签处理,容易遗漏导致故障
  • 秘钥管理粗暴,配置文件各种传递,没有集中进行管理
  • 密钥安全性存在风险,研发人员可以轻易打印获取密钥

所以就有了今天的文章内容

  • Gorm Hook官方文档:https://gorm.io/docs/hooks.html
  • 内存密钥保护库:https://github.com/awnumar/memguard

二、Gorm Hook 机制设计

在开始动代码之前先要解决的问题和要达到的目标:

  • 无侵入 对于正常业务开发无需关心其中逻辑
  • 密钥无法再程序中直接获取明文
  • 可支持扩展签名KYC密文初始化,全过程不暴露密钥
  • 可扩展对内存密钥进行保护

在过程中不止是需要使用Gorm Hook的能力,还需要对于密钥管理进行一定的约束

在实现过程中首先考虑的是密钥的初始化,后续再Hook中按照表的维度来使用不同的密钥来参与加验签过程:

这里实现的是基于Hmac256的密钥管理模块,可以其中引入 “memguard” 来加固内存,和对key进行解密操作等;


var keys sync.Map
var keyInit sync.Once

// SetTableSignHmac256Key 设置Hmac256密钥
func SetTableSignHmac256Key(table schema.Tabler, key string) error {
	keyInit.Do(func() {
		keys = sync.Map{}
	})

	_, ok := keys.Load(table.TableName())
	if ok {
		return errors.New("the table sign key cannot be set repeatedly")
	}
	keys.Store(table.TableName(), key)
	return nil
}

// SignDataHmac256 数据签名
func SignDataHmac256(table schema.Tabler, values ...string) (string, error) {
	signValue := ""
	for _, v := range values {
		signValue += v
	}
	if signValue == "" {
		return "", errors.New("no valid value")
	}

	key, ok := keys.Load(table.TableName())
	if !ok {
		return "", errors.New("sign error no available key exists")
	}
	return codec.HmacBase64([]byte(cast.ToString(key)), signValue), nil
}

// CheckSignDataHmac256 校验密钥
func CheckSignDataHmac256(table schema.Tabler, sign string, values ...string) (bool, error) {
	hmac256, err := SignDataHmac256(table, values...)
	if err != nil {
		return false, err
	}
	return hmac256 == sign, nil
}

三、代码实现

当把密钥做好管理之后,后面就比较简单了,在逻辑代码中增加 Gorm Hook 方法即可

初始化密钥:


    // 在项目初始化的时候初始化密钥
		err := ugorm.SetTableSignHmac256Key(&model.User{},Key)
	if err != nil {
		logx.Must(errors.New("not config CustodyWalletRecordsSignKey"))
	}

在具体测model方法中增加 Hook,此时在对数据库做读写操作之前都会对密钥进行加签验签 以达到最终的目的。

	type User struct {
		ID           int64      `json:"id" gorm:"column:id;primary_key;type:bigint AUTO_INCREMENT"`
		UserName     string     `json:"user_name" gorm:"column:user_name;type:varchar(255) NOT NULL;default:'';comment:用户名;"`
		Email        string     `json:"email" gorm:"column:email;type:varchar(255) NOT NULL;default:'';comment:邮箱;index:email_phone"`
		CountryShort string     `json:"country_short" gorm:"column:country_short;type:varchar(64) NOT NULL;default:'';comment:手机区号;index"`
		Phone        string     `json:"phone" gorm:"column:phone;type:varchar(64) NOT NULL;default:'';comment:手机号;index"`
		Password     string     `json:"password" gorm:"column:password;type:varchar(64) NOT NULL;default:'';comment:密码;"`
		Language     string     `json:"language" gorm:"column:language;type:varchar(64) NOT NULL;default:'';comment:语言;"`
		Status       int64      `json:"status" gorm:"column:status;type:tinyint(4)  NOT NULL;default:0 ;comment:1:启用,2停用;"`
		Sign            string          `gorm:"column:sign;type:varchar(255);not null" json:"sign"` 
		UpdatedAt    time.Time  `json:"updated_at" gorm:"column:updated_at;NOT NULL;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;type:TIMESTAMP"`
		CreatedAt    time.Time  `json:"created_at" gorm:"column:created_at;NOT NULL;default:CURRENT_TIMESTAMP;type:TIMESTAMP;index"`
		DeletedAt    *time.Time `json:"deleted_at" gorm:"column:deleted_at;type:DATETIME"`
	}

// --------------------------- ↓↓↓ 签名 hook 方法 ↓↓↓ ---------------------------

func (c *User) toSignString() string {
	return c.UserName + c.Email + c.Phone + c.Password
}

// BeforeCreate 数据创建时进行签名
func (c *User) BeforeCreate(tx *gorm.DB) (err error) {
	hmac256, err := ugorm.SignDataHmac256(c, c.toSignString())
	if err != nil {
		return err
	}
	c.Sign = hmac256
	return
}

// BeforeUpdate 数据更新时进行验签
func (c *User) BeforeUpdate(tx *gorm.DB) (err error) {
	hmac256, err := ugorm.SignDataHmac256(c, c.toSignString())
	if err != nil {
		return err
	}
	c.Sign = hmac256
	return
}

// AfterFind 数据查询时进行验签
func (c *User) AfterFind(tx *gorm.DB) (err error) {
	ok, err := ugorm.CheckSignDataHmac256(c, c.Sign, c.toSignString())
	if err != nil {
		return err
	}
	if !ok {
		return errors.New(cast.ToString(c.ID) + ": signature verification failure")
	}
	return
}

// --------------------------- ↑↑↑ 签名 hook 方法 ↑↑↑ ---------------------------

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/633880.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PL2303HXA自2012已停产,请联系供货商的解决办法

一、概述 PL2303 是Prolific 公司生产的一种高度集成的接口转换器,可提供一个RS232 全双工异步串行通信装置与USB 功能接口便利连接的解决方案。PL2303具有多个历史版本,早期的版本是PL2303HX, 近年有PL2303HXA、PL2303HXC、PL2303HXD(D版本…

SpringCloud01:SpringCloud介绍、服务提供者、服务消费者

SpringCloud和SpringBoot的关系 SpringBoot专注于快速、方便地开发单个个体微服务,SpringCloud关注全局的治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:管理配置、服务发现、断路器、路由…

GO-slice详解

GO-slice详解 简介 slice(切片)是go中常见和强大的类型,这篇文章不是slice使用简介,从源码角度来分析slice的实现,slice的一些迷惑的使用方式,同时也讲清楚一些问题。 slice的底层实现是数组&#xff0c…

(转载)基于蚁群算法的三维路径规划(matlab实现)

1 理论基础 1.1 三维路径规划问题概述 三维路径规划指在已知三维地图中,规划出一条从出发点到目标点满足某项指标最优,并且避开了所有三维障碍物的三维最优路径。现有的路径规划算法中,大部分算法是在二维规划平面或准二维规划平面中进行路…

微服务框架

流量入口Nginx 在上图中可以看到,Nginx作为整个架构的流量入口,可以理解为一个外部的网关,它承担着请求的路由转发、负载均衡、动静分离等功能。作为一个核心入口点,Nginx肯定要采用多节点部署,同时通过keepalived来实…

(八)CSharp-泛型类和参数约束(1)

一、C# 中的泛型 泛型(generic)特性可以让多个类型共享一组代码。 泛型类型不是类型,而是类型的模板。 C# 提供了5种类型:类、结构、接口、委托和方法。 泛型类 泛型的主要优点: 性能 类型转换时,非泛型的…

2018~2019 学年第二学期《信息安全》考试试题(B 卷)

北京信息科技大学 2018 ~2019 学年第 2 学期 《信息安全》课程期末考试试卷 B 课程所在学院:计算机学院 适用专业班级:计科 1601-06,重修 考试形式:(闭卷) 一. 选择题(本题满分 10 分,共含 10 道小题,每小题 1 分) 网络中存在的安全漏洞主…

虚拟环境创建、配置及激活

虚拟环境创建、配置及激活 前言 一、虚拟环境是什么? 虚拟环境(Virtual Environment)是在计算机上使用特定版本的编程语言(如python 3.9)和其所需包及依赖项的一种方法(如pandas 2.4),它可以被看作是一个隔…

基于html+css的图展示121

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

chatgpt赋能python:Python中如何快速删除字符串

Python中如何快速删除字符串 在Python编程中,字符串操作是非常常见的。有时候我们需要从字符串中删除一些无用的字符,以便更方便地处理数据。在本文中,将介绍Python如何快速删除字符串。 删除特定字符 Python中可以使用replace()函数快速替…

【深度学习炼丹大杀器——mlrunner初体验(以mmdetection为例)】

深度学习炼丹大杀器——mlrunner初体验(以mmdetection为例) 自动化炼丹,告别手动运行的烦恼~ 0.引言 了解深度学习的人都知道,炼丹是一种很玄学的事,并且还存在以下问题: 效率:在训练模型时&…

Seata服务端的启动过程 学习记录

1.ServerRunner ServerRunner类实现了CommandLineRunner与DisposableBean接口,将会在Spring容器启动和关闭的时间,分别执行 run 和 destory 方法。 而seata服务端的启动过程,都藏在run方法中 2.整体流程 io.seata.server.Server#start pu…

基于html+css的图展示120

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

【JMeter压力测试】通过jmeter压测surging

目录 前言 环境 下载配置源码 JMeter和JDK下载 JDKJmeter安装 Jmeter非GUI运行压测 结尾 前言 surging是异构微服务引擎,提供了模块化RPC请求通道,引擎在RPC服务治理基础之上还提供了各种协议,并且还提供了stage组件,以便针…

最新版CleanMyMac X4.13.4中文版Mac清理软件

cleanmymac是一款强大的Mac系统垃圾清理工具,可以清除Mac系统多余的语言包,系统缓存,应用程序!可智能清理mac磁盘垃圾和多余语言安装包,快速释放电脑内存,轻松管理和升级Mac上的应用。同时CleanMyMac X可以强力卸载恶意软件,修复系统漏洞&…

EXCEL函数2(统计函数,逻辑函数及其余函数)

统计函数 1、COUNT(单元格范围): 计算单元格范围的行数,比如用光标选中一定范围内的单元格,那么只要单元格里面有值,那么count函数便会将有值的单元格的数量统计出来 2、COUNTA(单元格范围&am…

msf渗透测试学习-与永恒之蓝漏洞案例

MSF是Metasploit Framework的缩写,是一款广泛使用的渗透测试工具,具有强大的攻击功能。它提供了一个模块化的平台,通过将各种攻击载荷、漏洞利用和辅助工具组装在一起,可用于模拟各种攻击,测试系统安全性,也…

Task Add-in Sample (C#)

下例显示了用 C# 编写Task Add-in 的完整源代码。 使用 C# 类库 (.NET Framework) 创建 Visual Studio 中的项目。实现 IEdmAddIn5。在“任务属性”对话框中创建自定义页。自定义任务详细信息页面。 注意: 若要填充下面的 GUID 属性&#x…

【linux】登录root账户时报错Sorry, that didn‘t work. Please try again.抱歉,这不管用,请再试一次

一、问题背景 登录其他普通账户的GUI桌面,发现都很正常,但是登录管理员账户root的桌面,重启之后一段时间正常,过一段时间就会出现登录报错Sorry, that didn’t work. Please try again. 二、解决办法——配置文件的解析 下面给出…

由于找不到msvcp120.dll丢失的解决方法,计算机丢失msvcp120.dll修复教程

在打开游戏或者软件的时候,计算机提示由于找不到msvcp120.dll,无法继续执行此代码怎么办呢?msvcp120.dll是一个动态链接库(DLL)文件,其作用是提供一些常用的C函数和类库,以便在Windows操作系统上…