From 3d734188d44a55d7c6389f1ed1f4df111ce8fe0c Mon Sep 17 00:00:00 2001 From: Umputun Date: Sun, 25 Mar 2018 16:42:05 -0500 Subject: [PATCH] hash ip with hmac --- app/main.go | 2 +- app/rest/api/rest_test.go | 2 +- app/store/comment.go | 11 +++++----- app/store/service.go | 5 +++-- app/store/service_test.go | 46 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 57 insertions(+), 9 deletions(-) diff --git a/app/main.go b/app/main.go index 85ba3d16..48e2b4be 100644 --- a/app/main.go +++ b/app/main.go @@ -93,7 +93,7 @@ func main() { log.Printf("[WARN] running in dev mode") } - dataService := store.Service{Interface: dataStore, EditDuration: 5 * time.Minute} + dataService := store.Service{Interface: dataStore, EditDuration: 5 * time.Minute, Secret: opts.ServerCommand.StoreKey} sessionStore := func() sessions.Store { sess := sessions.NewFilesystemStore(opts.ServerCommand.SessionStore, []byte(opts.ServerCommand.StoreKey)) sess.Options.HttpOnly = true diff --git a/app/rest/api/rest_test.go b/app/rest/api/rest_test.go index fb247948..fa110ddb 100644 --- a/app/rest/api/rest_test.go +++ b/app/rest/api/rest_test.go @@ -97,7 +97,7 @@ func TestServer_CreateAndGet(t *testing.T) { assert.Equal(t, "

test 123 http://radio-t.com

", comment.Text) assert.Equal(t, store.User{Name: "developer one", ID: "dev", Picture: "/api/v1/avatar/remark.image", - Profile: "https://remark42.com", Admin: true, Blocked: false, IP: "4b84b15bff6ee5796152495a230e45e3d7e947d9"}, + Profile: "https://remark42.com", Admin: true, Blocked: false, IP: "ae12fe3b5f129b5cc4cdd2b136b7b7947c4d2741"}, comment.User) t.Logf("%+v", comment) } diff --git a/app/store/comment.go b/app/store/comment.go index e2ce04c0..c00cce82 100644 --- a/app/store/comment.go +++ b/app/store/comment.go @@ -1,10 +1,11 @@ package store import ( + "crypto/hmac" "crypto/sha1" "fmt" - "hash/crc64" "html/template" + "log" "strconv" "strings" "time" @@ -98,16 +99,16 @@ func (c *Comment) sanitize() { } // hashIP replace sensitive fields with hashes -func (u *User) hashIP() { +func (u *User) hashIP(secret string) { hashVal := func(val string) string { if _, err := strconv.ParseUint(val, 16, 64); err == nil || val == "" { return val // already hashed } - h := sha1.New() + key := []byte(secret) + h := hmac.New(sha1.New, key) if _, err := h.Write([]byte(val)); err != nil { - // fail back to crc64 - return fmt.Sprintf("%x", crc64.Checksum([]byte(val), crc64.MakeTable(crc64.ECMA))) + log.Printf("[WARN] can't hash ip, %s", err) } return fmt.Sprintf("%x", h.Sum(nil)) } diff --git a/app/store/service.go b/app/store/service.go index 728f7b16..e69e1e45 100644 --- a/app/store/service.go +++ b/app/store/service.go @@ -11,6 +11,7 @@ import ( type Service struct { Interface EditDuration time.Duration + Secret string } // Create prepares comment and forward to Interface.Create @@ -27,8 +28,8 @@ func (s *Service) Create(comment Comment) (commentID string, err error) { comment.Votes = make(map[string]bool) } - comment.sanitize() // clear potentially dangerous js from all parts of comment - comment.User.hashIP() // replace ip by hash + comment.sanitize() // clear potentially dangerous js from all parts of comment + comment.User.hashIP(s.Secret) // replace ip by hash return s.Interface.Create(comment) } diff --git a/app/store/service_test.go b/app/store/service_test.go index b10f57bc..f3455c14 100644 --- a/app/store/service_test.go +++ b/app/store/service_test.go @@ -9,6 +9,52 @@ import ( "github.com/stretchr/testify/assert" ) +func TestService_CreateFromEmpty(t *testing.T) { + defer os.Remove(testDb) + b := Service{Interface: prep(t), Secret: "secret 123"} + comment := Comment{ + Text: "text", + User: User{IP: "192.168.1.1", ID: "user"}, + Locator: Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, + } + id, err := b.Create(comment) + assert.NoError(t, err) + assert.True(t, id != "", id) + + res, err := b.Get(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, id) + assert.NoError(t, err) + t.Logf("%+v", res) + assert.Equal(t, "text", res.Text) + assert.True(t, time.Since(res.Timestamp).Seconds() < 1) + assert.Equal(t, "user", res.User.ID) + assert.Equal(t, "9f41a4b2dca0c826f1aa2c69246347758a43eac1", res.User.IP) + assert.Equal(t, map[string]bool{}, res.Votes) +} + +func TestService_CreateFromPartial(t *testing.T) { + defer os.Remove(testDb) + b := Service{Interface: prep(t), Secret: "secret 123"} + comment := Comment{ + Text: "text", + Timestamp: time.Date(2018, 3, 25, 16, 34, 33, 0, time.UTC), + Votes: map[string]bool{"u1": true, "u2": false}, + User: User{IP: "192.168.1.1", ID: "user"}, + Locator: Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, + } + id, err := b.Create(comment) + assert.NoError(t, err) + assert.True(t, id != "", id) + + res, err := b.Get(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, id) + assert.NoError(t, err) + t.Logf("%+v", res) + assert.Equal(t, "text", res.Text) + assert.Equal(t, comment.Timestamp, res.Timestamp) + assert.Equal(t, "user", res.User.ID) + assert.Equal(t, "9f41a4b2dca0c826f1aa2c69246347758a43eac1", res.User.IP) + assert.Equal(t, comment.Votes, res.Votes) +} + func TestService_Vote(t *testing.T) { defer os.Remove(testDb) b := Service{Interface: prep(t)}