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

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

Go  /  管理员 发布于 1个月前   54

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语言多项目批量更新依赖及自动调用jenkins构建流程步骤(0个评论)
    • 在go语言中实现数学pow(x^y 的幂次)代码示例(0个评论)
    • 在go语言中实现从http响应中解码JSON数据(0个评论)
    • go语言中如何使用JSON网络令牌(JWT)在控制器之间传递数据(0个评论)
    • 在go语言中的单测工具推荐:Go Convey(0个评论)
    • 近期文章
    • Laravel 10.24版本发布(0个评论)
    • go语言多项目批量更新依赖及自动调用jenkins构建流程步骤(0个评论)
    • 在go语言中实现数学pow(x^y 的幂次)代码示例(0个评论)
    • Laravel应用程序性能监控 (APM) 工具:Scout (0个评论)
    • 推荐一款针对git、diff和grep输出的语法高亮显示的扩展包:Git Delta(0个评论)
    • laravel框架中以公共函数方式实现job异步化执行封装代码示例(0个评论)
    • 在go语言中实现从http响应中解码JSON数据(0个评论)
    • go语言中如何使用JSON网络令牌(JWT)在控制器之间传递数据(0个评论)
    • 计算机网络知识详解(0个评论)
    • Laravel HTTP 测试与Symfony的DomCrawler(0个评论)
    • 近期评论
    • 路人 在

      php中使用hyperf框架调用讯飞星火大模型实现国内版chatgpt功能示例中评论 教程很详细,如果加个前端chatgpt对话页面就完美了..
    • 博主 在

      科学上网翻墙之v2rayN-Core客户端免费公益节点使用教程中评论 @ mashrdn 多切换几个节点测试,免费ssr是没那么稳..
    • mashrdn 在

      科学上网翻墙之v2rayN-Core客户端免费公益节点使用教程中评论 V2rayn免费节点添加上去了,youtobe无法打开网页,是怎么回事..
    • 张伟 在

      科学上网翻墙之v2rayN-Core客户端免费公益节点使用教程中评论 3q!有用,不过免费节点隔天就要去git上复制新的导进去..
    • 博主 在

      科学上网翻墙访问Google , 上外网神器佛跳墙VPN(永久免费)使用流程步骤中评论 该篇教程已不能用了,告知大家,免的老有老铁问我!..
    • 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
    Top

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

    侯体宗的博客