- commit
- 1e40160
- parent
- 94d3e85
- author
- Eric Bower
- date
- 2024-03-25 20:12:50 +0000 UTC
chore(analytics): hmac ip address
8 files changed,
+44,
-7
+16,
-0
1@@ -0,0 +1,16 @@
2+package main
3+
4+import (
5+ "crypto/rand"
6+ "encoding/hex"
7+ "fmt"
8+)
9+
10+func main() {
11+ secret := make([]byte, 64)
12+ _, err := rand.Read(secret)
13+ if err != nil {
14+ panic(err)
15+ }
16+ fmt.Println(hex.EncodeToString(secret))
17+}
+2,
-2
1@@ -220,7 +220,7 @@ func (h *AssetHandler) handle(w http.ResponseWriter, r *http.Request) {
2 )
3 // track 404s
4 ch := shared.GetAnalyticsQueue(r)
5- view, err := shared.AnalyticsVisitFromRequest(r, h.UserID)
6+ view, err := shared.AnalyticsVisitFromRequest(r, h.UserID, h.Cfg.Secret)
7 if err == nil {
8 view.ProjectID = h.ProjectID
9 view.Status = http.StatusNotFound
10@@ -279,7 +279,7 @@ func (h *AssetHandler) handle(w http.ResponseWriter, r *http.Request) {
11 if finContentType == "text/html" {
12 // track visit
13 ch := shared.GetAnalyticsQueue(r)
14- view, err := shared.AnalyticsVisitFromRequest(r, h.UserID)
15+ view, err := shared.AnalyticsVisitFromRequest(r, h.UserID, h.Cfg.Secret)
16 if err == nil {
17 view.ProjectID = h.ProjectID
18 ch <- view
+5,
-0
1@@ -23,6 +23,10 @@ func NewConfigSite() *shared.ConfigSite {
2 minioPass := shared.GetEnv("MINIO_ROOT_PASSWORD", "")
3 dbURL := shared.GetEnv("DATABASE_URL", "")
4 useImgProxy := shared.GetEnv("USE_IMGPROXY", "1")
5+ secret := shared.GetEnv("PICO_SECRET", "")
6+ if secret == "" {
7+ panic("must provide PICO_SECRET environment variable")
8+ }
9
10 intro := "To create an account, enter a username.\n"
11 intro += "After that, go to https://pico.sh/getting-started#next-steps"
12@@ -32,6 +36,7 @@ func NewConfigSite() *shared.ConfigSite {
13 SubdomainsEnabled: subdomains == "1",
14 CustomdomainsEnabled: customdomains == "1",
15 UseImgProxy: useImgProxy == "1",
16+ Secret: secret,
17 ConfigCms: config.ConfigCms{
18 Domain: domain,
19 Email: email,
+2,
-2
1@@ -259,7 +259,7 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
2
3 // track visit
4 ch := shared.GetAnalyticsQueue(r)
5- view, err := shared.AnalyticsVisitFromRequest(r, user.ID)
6+ view, err := shared.AnalyticsVisitFromRequest(r, user.ID, cfg.Secret)
7 if err == nil {
8 ch <- view
9 } else {
10@@ -409,7 +409,7 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
11 }
12
13 // track visit
14- view, err := shared.AnalyticsVisitFromRequest(r, user.ID)
15+ view, err := shared.AnalyticsVisitFromRequest(r, user.ID, cfg.Secret)
16 if err == nil {
17 view.PostID = post.ID
18 ch <- view
+5,
-0
1@@ -22,6 +22,10 @@ func NewConfigSite() *shared.ConfigSite {
2 useImgProxy := shared.GetEnv("USE_IMGPROXY", "1")
3 maxSize := uint64(500 * shared.MB)
4 maxImgSize := int64(10 * shared.MB)
5+ secret := shared.GetEnv("PICO_SECRET", "")
6+ if secret == "" {
7+ panic("must provide PICO_SECRET environment variable")
8+ }
9
10 intro := "To get started, enter a username.\n"
11 intro += "To learn next steps go to our docs at https://pico.sh/prose\n"
12@@ -31,6 +35,7 @@ func NewConfigSite() *shared.ConfigSite {
13 SubdomainsEnabled: subdomains == "1",
14 CustomdomainsEnabled: customdomains == "1",
15 UseImgProxy: useImgProxy == "1",
16+ Secret: secret,
17 ConfigCms: config.ConfigCms{
18 Domain: domain,
19 Email: email,
1@@ -1,6 +1,9 @@
2 package shared
3
4 import (
5+ "crypto/hmac"
6+ "crypto/sha256"
7+ "encoding/hex"
8 "errors"
9 "fmt"
10 "log/slog"
11@@ -13,6 +16,13 @@ import (
12 "github.com/x-way/crawlerdetect"
13 )
14
15+func hmacString(secret, data string) string {
16+ hmacer := hmac.New(sha256.New, []byte(secret))
17+ hmacer.Write([]byte(data))
18+ dataHmac := hmacer.Sum(nil)
19+ return hex.EncodeToString(dataHmac)
20+}
21+
22 func trackableRequest(r *http.Request) error {
23 agent := r.UserAgent()
24 // dont store requests from bots
25@@ -65,7 +75,7 @@ func cleanReferer(ref string) (string, error) {
26
27 var ErrAnalyticsDisabled = errors.New("owner does not have site analytics enabled")
28
29-func AnalyticsVisitFromRequest(r *http.Request, userID string) (*db.AnalyticsVisits, error) {
30+func AnalyticsVisitFromRequest(r *http.Request, userID string, secret string) (*db.AnalyticsVisits, error) {
31 dbpool := GetDB(r)
32 if !dbpool.HasFeatureForUser(userID, "analytics") {
33 return nil, ErrAnalyticsDisabled
34@@ -91,7 +101,7 @@ func AnalyticsVisitFromRequest(r *http.Request, userID string) (*db.AnalyticsVis
35 UserID: userID,
36 Host: host,
37 Path: path,
38- IpAddress: ipAddress,
39+ IpAddress: hmacString(secret, ipAddress),
40 UserAgent: cleanUserAgent(r.UserAgent()),
41 Referer: referer,
42 Status: http.StatusOK,
1@@ -32,6 +32,7 @@ type ConfigSite struct {
2 CustomdomainsEnabled bool
3 SendgridKey string
4 UseImgProxy bool
5+ Secret string
6 }
7
8 type CreateURL struct {
1@@ -5,7 +5,7 @@ CREATE TABLE IF NOT EXISTS analytics_visits (
2 post_id uuid,
3 host varchar(253),
4 path varchar(2048),
5- ip_address varchar(46),
6+ ip_address varchar(256),
7 user_agent varchar(1000),
8 referer varchar(253),
9 status int4,