什么叫内容泄露

内容泄露指的是:使用buffer值的一方通过某种非标准的方式,得到了本不该得到的内容

存在的场景和导致的原因

场景

  • 通过调用Buffer值的某个用于读取内容的方法,得到一部分未读内容 A
  • 当Buffer值又有一些新的内容之后,可通过上一步得到的结果值 A 直接获取新的内容

原因

  • 通过切片可以直接访问和操纵它的底层数据
  • 不论切片是基于某个数组得到的,还是通过另一个切片操作来获得的,上一结论都成立

bytes 例子

代码

  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. )
  6. func main() {
  7. contents := "ab"
  8. buffer1 := bytes.NewBufferString(contents)
  9. fmt.Printf("The capacity of new buffer with contents %q: %d\n", contents, buffer1.Cap()) // 8
  10. fmt.Println()
  11. unreadBytes := buffer1.Bytes()
  12. fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes) // [97, 98]
  13. fmt.Println()
  14. contents = "cdefg"
  15. fmt.Printf("Write contents %q ...\n", contents)
  16. buffer1.WriteString(contents)
  17. fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap())
  18. fmt.Println()
  19. unreadBytes = unreadBytes[:cap(unreadBytes)]
  20. fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes) // [97 98 99 100 101 102 103 0]
  21. fmt.Println()
  22. value := byte('X')
  23. fmt.Printf("Set a byte in the unread bytes to %v ...\n", value)
  24. unreadBytes[len(unreadBytes) - 2] = value
  25. fmt.Printf("The unread bytes of the buffer: %v\n", buffer1.Bytes()) // 发生内容泄露 [97 98 99 100 101 102 88]
  26. fmt.Println()
  27. contents = "hijklmn"
  28. fmt.Printf("Write contents %q ..\n", contents)
  29. buffer1.WriteString(contents)
  30. fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap()) // 23
  31. fmt.Println()
  32. unreadBytes = unreadBytes[:cap(unreadBytes)]
  33. // 扩容代码就只能再创建一个新的内容容器,并把原有容器中的未读内容拷贝进去,最后再用新的容器替换掉原有的容器
  34. fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes) // The unread bytes of the buffer: [97 98 99 100 101 102 88 0]
  35. fmt.Println()
  36. }

解析

上述代码在写入 “hijklmn” 后,获取的unreadBytes还是与写入前一样?
先要了解 bytes.Buffer 的扩容策略:

  • Buffer 即可以手动扩容,也可以自动扩容
  • 如果当前容量的一半,仍然大于或等于其现有长度再加上另需字节数的和,将未读内容拷贝到它的头部位置(即原未读内容和新内容将已读内容覆盖掉)
  • 如果当前内容容器的容量小于新长度的二倍,扩容策略会建立一个新的内容容器,并把原有容器中的未读内容拷贝进去,最后用新的容器代替原有的容器

根据bytes.Buffer的扩容策略,上述切片访问和操纵它的底层数据仍然是原容器的内容。

Bufio 示例

代码

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "strings"
  6. )
  7. func main() {
  8. comment := "Package bufio implements buffered I/O. " +
  9. "It wraps an io.Reader or io.Writer object, " +
  10. "creating another object (Reader or Writer) that " +
  11. "also implements the interface but provides buffering and " +
  12. "some help for textual I/O."
  13. basicReader := strings.NewReader(comment)
  14. fmt.Printf("The size of basic reader: %d\n", basicReader.Size()) // 213
  15. size := len(comment)
  16. fmt.Printf("New a buffered reader with size %d ...\n", size) // 213
  17. reader4 := bufio.NewReaderSize(basicReader, size)
  18. fmt.Println()
  19. peekNum := 7
  20. fmt.Printf("Peek %d bytes ...\n", peekNum) // 7
  21. bytes, err := reader4.Peek(peekNum)
  22. if err != nil {
  23. fmt.Printf("error: %v\n", err)
  24. }
  25. fmt.Printf("Peeked contents(%d): %q\n", len(bytes), bytes) // Peeked contents(7): "Package"
  26. fmt.Println()
  27. bytes = bytes[:cap(bytes)] // 存在内容泄露
  28. fmt.Printf("The all of the contents in the buffer:\n%q\n", bytes) // 获取到所有的值
  29. fmt.Println()
  30. blank := byte(' ') // 存在内容泄露,通过bytes值而修改到原bufio值
  31. fmt.Printf("Set blanks into the contents in the buffer ...")
  32. for _, i := range []int{55, 65, 57, 58, 66, 67, 68} {
  33. bytes[i] = blank
  34. }
  35. fmt.Println()
  36. peekNum = size
  37. fmt.Printf("Peek %d bytes ...\n", peekNum)
  38. bytes, err = reader4.Peek(peekNum)
  39. if err != nil {
  40. fmt.Printf("error: %v\n", err)
  41. }
  42. fmt.Printf("Peeked contents(%d):\n%q\n", len(bytes), bytes) //"Package bufio implements buffered I/O. It wraps an io.R a r or i iter object, creating another object (Reader or Writer) that also implements the interface but provides buffering and some help for textual I/O."
  43. fmt.Println()
  44. }

解析

  • 通过bufio包的peek函数读取到的是切片, 切片的底层是bufio的值
  • 通过切片操作底层bufio的值
  • bufio 类型中的Peek方法、ReadSlice方法和ReadLine方法都存在类似的问题