From 3cebbc3425d09588271bf4755d3c5dfd50b0855a Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Sat, 3 Jun 2023 21:51:07 -0700 Subject: [PATCH] Detect vote or response replay/caching --- candidate.go | 10 ++++++++++ voter.go | 18 ++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/candidate.go b/candidate.go index 52f255c..64fd6ad 100644 --- a/candidate.go +++ b/candidate.go @@ -142,6 +142,16 @@ func (c *Candidate) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + if time.Since(v.VoteSent).Abs() > 15*time.Second { + http.Error( + w, + fmt.Sprintf("excessive time difference (%.1f seconds); delay, replay, or clock skew", time.Since(v.VoteSent).Seconds()), + http.StatusBadRequest, + ) + } + + c.resp.ResponseSent = time.Now().UTC() + js = lo.Must(json.Marshal(c.resp)) w.Header().Set("Content-Type", "application/json") diff --git a/voter.go b/voter.go index 3f995fb..d4a98c3 100644 --- a/voter.go +++ b/voter.go @@ -21,18 +21,18 @@ type Voter struct { } type vote struct { - VoterID string `json:"voterID"` - LastSeenCandidateID string `json:"lastSeenCandidateID"` - NumPollsSinceChange int `json:"numPollsSinceChange"` - // TODO: Add timestamp + VoterID string `json:"voterID"` + LastSeenCandidateID string `json:"lastSeenCandidateID"` + NumPollsSinceChange int `json:"numPollsSinceChange"` + VoteSent time.Time `json:"voteSent"` // Used internally by Candidate received time.Time } type voteResponse struct { - CandidateID string `json:"candidateID"` - // TODO: Add timestamp + CandidateID string `json:"candidateID"` + ResponseSent time.Time `json:"responseSent"` } func NewVoter(url string, signingKey string) *Voter { @@ -105,6 +105,8 @@ func (v *Voter) poll(update <-chan time.Duration, t *time.Ticker) bool { } func (v *Voter) sendVote() { + v.vote.VoteSent = time.Now().UTC() + for _, c := range v.candidates { c.voteIfNo(&v.vote) } @@ -152,6 +154,10 @@ func (v *Voter) sendVote() { return } + if time.Since(vr.ResponseSent).Abs() > 15*time.Second { + v.log("excessive time difference (%.1f seconds); delay, replay, or clock skew", time.Since(vr.ResponseSent).Seconds()) + } + if vr.CandidateID == v.vote.LastSeenCandidateID { v.vote.NumPollsSinceChange++ } else {