Mutating immutable strings in Go

As most Go programmers probably know, strings are immutable in Go. This has its advantages, but there are a few reasons why one would want to make changes to strings. One good reason is security. Having sensitive information like passwords lingering around in memory isn’t great, as they could end up on disk if the OS decides to write a piece of your program’s memory to disk to free up some RAM.

Using reflection, we can obtain the underlying StringHeader struct of strings and modify them to suit our needs. There are a few caveats though. This doesn’t work on strings that are directly defined in the source code, as those are typically stored in read-only memory. Trying to modify a string located in read-only memory would result in a segfault. Another downside is the fact that the ‘unsafe’ package we’ll be using is not covered by the Go 1 compatibility guarantee, so keep that in mind.

In the following code example we obtain the StringHeader of a string, pretend it’s a byte slice, zero it out and set its length to 0:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func zero(s *string) {
    str := (*reflect.StringHeader)(unsafe.Pointer(s))
    slice := reflect.SliceHeader{
        Data: str.Data,
        Len: str.Len,
        Cap: str.Len,
    }

    copy(*(*[]byte)(unsafe.Pointer(&slice)), make([]byte, slice.Len))
    str.Len = 0
}

func main() {
    password := string([]byte("supersecretpassword"))

    fmt.Printf("value: %q\n", password)
    zero(&password)
    fmt.Printf("value: %q\n", password)
}

You can test this yourself here. Here’s the output:

1
2
3
4
$ go run main.go
value: "supersecretpassword"
value: ""

Of course, you’ll also want to make sure you only pass your sensitive strings as pointers. Otherwise you might be leaving copies of it around in memory.