侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

在go语言中安全问题之内存管理场景示例

Go  /  管理员 发布于 1年前   304

go语言安全问题之内存管理场景

场景1:切片长度校验

在对 slice 进行操作时,必须判断长度是否合法,防止程序 panic

// bad: 未判断data的长度,可导致 index out of rangefunc decode(data []byte) bool {
 if data[0] == 'F' && data[1] == 'U' && data[2] == 'Z' && data[3] == 'Z' && data[4] == 'E' && data[5] == 'R' { fmt.Println("Bad") return true } return false}
// bad: slice bounds out of range
func foo() {
 var slice = []int{0, 1, 2, 3, 4, 5, 6} fmt.Println(slice[:10])}
// good: 使用data前应判断长度是否合法
func decode(data []byte) bool {
 if len(data) == 6 { if data[0] == 'F' && data[1] == 'U' && data[2] == 'Z' && data[3] == 'Z' && data[4] == 'E' && data[5] == 'R' { fmt.Println("Good") return true } } return false}


场景2:nil 指针判断

进行指针操作时,必须判断该指针是否为 nil,防止程序 panic,尤其在进行结构体 Unmarshal 时

type Packet struct {
 PackeyType    uint8 PackeyVersion uint8 Data          *Data}
type Data struct {
 Stat uint8 Len  uint8 Buf  [8]byte}
func (p *Packet) UnmarshalBinary(b []byte) error {
 if len(b) < 2 { return io.EOF }
 p.PackeyType = b[0] p.PackeyVersion = b[1]
 // 若长度等于2,那么不会new Data
 if len(b) > 2 { p.Data = new(Data) } return nil}
// bad: 未判断指针是否为nil
func main() {
 packet := new(Packet) data := make([]byte, 2) if err := packet.UnmarshalBinary(data); err != nil { fmt.Println("Failed to unmarshal packet") return }
 fmt.Printf("Stat: %v\n", packet.Data.Stat)}
// good: 判断Data指针是否为nil
func main() {
 packet := new(Packet) data := make([]byte, 2)
 if err := packet.UnmarshalBinary(data); err != nil { fmt.Println("Failed to unmarshal packet") return }
 if packet.Data == nil { return }
 fmt.Printf("Stat: %v\n", packet.Data.Stat)}


场景3:整数安全

在进行数字运算操作时,需要做好长度限制,防止外部输入运算导致异常:

确保无符号整数运算时不会反转

确保有符号整数运算时不会出现溢出

确保整型转换时不会出现截断错误

确保整型转换时不会出现符号错误


以下场景必须严格进行长度限制:

作为数组索引

作为对象的长度或者大小

作为数组的边界(如作为循环计数器)

// bad: 未限制长度,导致整数溢出
func overflow(numControlByUser int32) {
 var numInt int32 = 0 numInt = numControlByUser + 1 // 对长度限制不当,导致整数溢出
 fmt.Printf("%d\n", numInt) // 使用numInt,可能导致其他错误
}
func main() {
 overflow(2147483647)}
// good
func overflow(numControlByUser int32) {
 var numInt int32 = 0 numInt = numControlByUser + 1 if numInt < 0 { fmt.Println("integer overflow") return } fmt.Println("integer ok")}
func main() {
 overflow(2147483647)}


场景4:make 分配长度验证

在进行 make 分配内存时,需要对外部可控的长度进行校验,防止程序 panic。

// bad
func parse(lenControlByUser int, data []byte) {
 size := lenControlByUser // 对外部传入的size,进行长度判断以免导致panic
 buffer := make([]byte, size) copy(buffer, data)}
// good
func parse(lenControlByUser int, data []byte) ([]byte, error) {
 size := lenControlByUser // 限制外部可控的长度大小范围
 if size > 64*1024*1024 { return nil, errors.New("value too large") } buffer := make([]byte, size) copy(buffer, data) return buffer, nil}


场景5:禁止 SetFinalizer 和指针循环引用同时使用

当一个对象从被 GC 选中到移除内存之前,runtime.SetFinalizer () 都不会执行,即使程序正常结束或者发生错误。由指针构成的 “循环引用” 虽然能被 GC 正确处理,但由于无法确定 Finalizer 依赖顺序,从而无法调用 runtime.SetFinalizer (),导致目标对象无法变成可达状态,从而造成内存无法被回收。

// bad
func foo() {
 var a, b Data a.o = &b b.o = &a
 // 指针循环引用,SetFinalizer()无法正常调用
 runtime.SetFinalizer(&a, func(d *Data) { fmt.Printf("a %p final.\n", d) }) runtime.SetFinalizer(&b, func(d *Data) { fmt.Printf("b %p final.\n", d) })}
func main() {
 for { foo() time.Sleep(time.Millisecond) }}


场景6:禁止重复释放 channel

重复释放一般存在于异常流程判断中,如果恶意攻击者构造出异常条件使程序重复释放 channel,则会触发运行时 panic,从而造成 DoS 攻击。

// bad
func foo(c chan int) {
 defer close(c) err := processBusiness() if err != nil { c <- 0 close(c) // 重复释放channel
 return } c <- 1}
// good
func foo(c chan int) {
 defer close(c) // 使用defer延迟关闭channel
 err := processBusiness() if err != nil { c <- 0 return } c <- 1}


场景7:确保每个协程都能退出

启动一个协程就会做一个入栈操作,在系统不退出的情况下,协程也没有设置退出条件,则相当于协程失去了控制,它占用的资源无法回收,可能会导致内存泄露。

// bad: 协程没有设置退出条件
func doWaiter(name string, second int) {
 for { time.Sleep(time.Duration(second) * time.Second) fmt.Println(name, " is ready!") }}


场景8:不使用 unsafe 包

由于 unsafe 包绕过了 Golang 的内存安全原则,一般来说使用该库是不安全的,可导致内存破坏,尽量避免使用该包。若必须要使用 unsafe 操作指针,必须做好安全校验。

// bad: 通过unsafe操作原始指针
func unsafePointer() {
 b := make([]byte, 1) foo := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(0xfffffffe))) fmt.Print(*foo + 1)}
// [signal SIGSEGV: segmentation violation code=0x1 addr=0xc100068f55 pc=0x49142b]


场景9:不使用 slice 作为函数入参

slice 在作为函数入参时,函数内对 slice 的修改可能会影响原始数据

 // bad // slice作为函数入参时包含原始数组指针
 func modify(array []int) { array[0] = 10 // 对入参slice的元素修改会影响原始数据
 }   func main() {
 array := []int{1, 2, 3, 4, 5}       modify(array)
 fmt.Println(array) // output:[10 2 3 4 5]
 }
 // good // 数组作为函数入参,而不是slice
 func modify(array [5]int) { array[0] = 10 }
 func main() { // 传入数组,注意数组与slice的区别
 array := [5]int{1, 2, 3, 4, 5}       modify(array)
 fmt.Println(array) }

  • 上一条:
    在go语言中安全问题之并发保护场景示例
    下一条:
    在go语言中安全问题之文件操作、系统接口、通信安全场景示例
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 近期文章
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2017-09
    • 2020-03
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-12
    • 2021-01
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-03
    • 2022-04
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    • 2024-08
    • 2024-11
    • 2025-02
    • 2025-04
    • 2025-05
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客