Insecure temporary file creation

Insecure temporary file creation

Description

Predictable filenames or excessive permissions in shared directories such as /tmp can expose temporary files. Another user may create the name first or place a symbolic link there, causing a program to overwrite an unintended file. Other users may also read or modify inadequately protected contents.

Potential impact

  • File precreation or symbolic links may redirect writes to configuration or log files.
  • Excessive permissions may expose sensitive temporary data.
  • A privileged application may be tricked into overwriting protected system files.

Remediation

  • Use os.CreateTemp or the older ioutil.TempFile, which delegates to it, to create files with randomized names.
  • Avoid fixed names such as /tmp/myfile; supply a prefix and let the library generate the rest.
  • os.CreateTemp creates files with 0o600 permissions before the umask. Ensure the directory and file permissions prevent modification by other users.
  • Remove files with os.Remove as soon as they are no longer needed.
  • Avoid temporary filesystem storage of passwords, tokens, or personal data where possible; use memory or appropriate secure storage.

Examples

Before

go
package main

import (
    "fmt"
    "os"
)

func unsafeTemp() {
    // Before: use a fixed filename under /tmp
    path := "/tmp/report.log"

    // Another user or process may create this filename first
    // or place a symbolic link at that path
    f, err := os.Create(path)
    if err != nil {
        fmt.Println("failed to create temp file:", err)
        return
    }
    defer f.Close()

    // Default permissions, such as 0644, may allow
    // other users to read the contents
    if _, err := f.WriteString("user session token: abc123\n"); err != nil {
        fmt.Println("failed to write:", err)
        return
    }
}

func main() {
    unsafeTemp()
}

After

go
package main

import (
    "fmt"
    "os"
)

func safeTemp() {
    // Create a temporary file in /tmp using the report- prefix
    // A random suffix reduces filename collisions
    f, err := os.CreateTemp("/tmp", "report-")
    if err != nil {
        fmt.Println("failed to create temp file:", err)
        return
    }
    // Save the name and schedule removal with defer
    tempName := f.Name()
    defer os.Remove(tempName)
    defer f.Close()

    // Restrict permissions for sensitive data, for example to 0600
    if err := os.Chmod(tempName, 0o600); err != nil {
        fmt.Println("failed to chmod temp file:", err)
        return
    }

    if _, err := f.WriteString("temporary processing data\n"); err != nil {
        fmt.Println("failed to write:", err)
        return
    }

    fmt.Println("temp file created:", tempName)
}

func main() {
    safeTemp()
}

Explanation:

  • Before: The fixed /tmp/report.log name allows precreation, symbolic-link substitution, or collisions between processes. Default permissions may also expose the file to other users.
  • After: os.CreateTemp creates a unique file with a randomized name and 0o600 permissions from the start. The example's os.Chmod reapplies that mode. Deferred cleanup removes the file after use, reducing the chance that sensitive temporary data remains on disk.

References