From ffe54c16c0d55ee248567b34ec28e1d4b2bbb9c8 Mon Sep 17 00:00:00 2001 From: Jonathan David Page Date: Tue, 2 Mar 2021 03:31:18 -0500 Subject: [PATCH] Use salt when generating ids from ips Implements #6 --- add-comment/add_comment.go | 1 + example-config.toml | 12 ++++++++++++ shared/shared.go | 40 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/add-comment/add_comment.go b/add-comment/add_comment.go index 036594c..229a243 100644 --- a/add-comment/add_comment.go +++ b/add-comment/add_comment.go @@ -167,6 +167,7 @@ func getId(ip string) string { ip = strings.ReplaceAll(ip, "_", ":") // Use real IP address, not sanitized h := sha256.New() h.Write([]byte(ip)) + shared.WriteIPSalt(h) // First 8 chars return fmt.Sprintf("%x", h.Sum(nil))[:8] } diff --git a/example-config.toml b/example-config.toml index 62ab4e5..bded244 100644 --- a/example-config.toml +++ b/example-config.toml @@ -7,6 +7,18 @@ data = "/home/username/.local/share/gemlikes" # It must be accessible to the user that the CGI script runs under. # Directories will be created as needed, don't create them yourself. +# Where to store IP salt: +ip_salt = "auto" # default +#ip_salt = "disabled" # disable IP salting (old behavior) +#ip_salt = "/usr/local/etc/gemlikes/ip_salt" # use existing salt +# If unset, the default is "auto", which places the salt in the data directory, +# generating it the first time if needed. "disabled" restores the old behavior, +# which can potentially expose IP addresses to sufficiently-motivated attackers. +# Specifying an absolute path uses the contents of the given file as a salt, +# which must already exist. This allows multiple instances of gemlikes (e.g. in +# a shared environment) to present identical identifiers for the same IP. The +# path should be somewhere the server won't serve. + # The list of directories where files exist that can be liked and commented on: dirs = ["/var/gemini", "/var/gemini/my_gemlog"] # IMPORTANT: There can't be any files with same name in any of these directories, diff --git a/shared/shared.go b/shared/shared.go index 217e9e1..526f839 100644 --- a/shared/shared.go +++ b/shared/shared.go @@ -2,8 +2,11 @@ package shared import ( + "crypto/rand" "errors" "fmt" + "io" + "io/ioutil" "net/url" "os" "path/filepath" @@ -15,6 +18,8 @@ import ( var ErrConfigDir = errors.New("config dir invalid or not set") var LikesDisabled = false +var ipSaltValue []byte + func PathExists(path string) bool { _, err := os.Stat(path) if err == nil { @@ -93,6 +98,12 @@ func SafeInit() error { if config.Get("disable_likes") != nil { LikesDisabled = config.Get("disable_likes").(bool) } + // Load IP->ID salt + ipSaltValue, err = loadIPSalt(config, data.(string)) + if err != nil { + return err + } + return nil } @@ -176,6 +187,35 @@ func GetTmpDir() string { return filepath.Join(getDataDir(), "tmp") } +func WriteIPSalt(w io.Writer) (int, error) { + return w.Write(ipSaltValue) +} + +func loadIPSalt(config *toml.Tree, dataDir string) ([]byte, error) { + ipSaltMethod := "auto" + if key := config.Get("ip_salt"); key != nil { + ipSaltMethod = key.(string) + } + + ipSaltPath := ipSaltMethod + switch ipSaltMethod { + case "disabled": + return nil, nil + case "auto": + ipSaltPath = filepath.Join(dataDir, "ip_salt") + f, err := os.OpenFile(ipSaltPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o644) + if err == nil { + // First time, create random data + defer f.Close() + if _, err := io.CopyN(f, rand.Reader, 16); err != nil { + return nil, err + } + } + } + + return ioutil.ReadFile(ipSaltPath) +} + func getConfigDir() string { e, _ := os.Executable() return filepath.Dir(e)