Skip to content

Commit

Permalink
Prevent replay attacks on old request files from other users.
Browse files Browse the repository at this point in the history
We also purge each request file after processing to reduce the risk.
  • Loading branch information
LTLA committed Apr 13, 2024
1 parent 0a75509 commit 5424098
Showing 1 changed file with 34 additions and 8 deletions.
42 changes: 34 additions & 8 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"strconv"
"io/fs"
"syscall"
"sync"
)

func dumpJsonResponse(w http.ResponseWriter, status int, v interface{}, path string) {
Expand All @@ -33,18 +34,15 @@ func dumpJsonResponse(w http.ResponseWriter, status int, v interface{}, path str
}
}

func dumpErrorResponse(w http.ResponseWriter, status int, message string, path string) {
log.Printf("failed to process %q; %s\n", path, message)
dumpJsonResponse(w, status, map[string]interface{}{ "status": "ERROR", "reason": message }, path)
}

func dumpHttpErrorResponse(w http.ResponseWriter, err error, path string) {
status_code := http.StatusInternalServerError
var http_err *httpError
if errors.As(err, &http_err) {
status_code = http_err.Status
}
dumpErrorResponse(w, status_code, err.Error(), path)
message := err.Error()
log.Printf("failed to process %q; %s\n", path, message)
dumpJsonResponse(w, status_code, map[string]interface{}{ "status": "ERROR", "reason": message }, path)
}

func checkRequestFile(path, staging string) (string, error) {
Expand Down Expand Up @@ -107,6 +105,9 @@ func main() {
}
}

var mut sync.Mutex
in_action := map[string]bool{}

// Creating an endpoint to trigger jobs.
http.HandleFunc("POST /new/{path}", func(w http.ResponseWriter, r *http.Request) {
path := r.PathValue("path")
Expand All @@ -118,6 +119,23 @@ func main() {
return
}

// Prevent replay attack of a currently-being-processed request file.
err = func() error {
mut.Lock()
defer mut.Unlock()
_, ok := in_action[path]
if !ok {
in_action[path] = true
return nil
} else {
return newHttpError(http.StatusBadRequest, errors.New("path is already being processed"))
}
}()
if err != nil {
dumpHttpErrorResponse(w, err, path)
return
}

var reportable_err error
payload := map[string]interface{}{}
reqtype := strings.TrimPrefix(path, "request-")
Expand Down Expand Up @@ -160,10 +178,18 @@ func main() {
} else if strings.HasPrefix(reqtype, "health_check-") { // TO-BE-DEPRECATED, see /check below.
reportable_err = nil
} else {
dumpErrorResponse(w, http.StatusBadRequest, "invalid request type", reqpath)
return
reportable_err = newHttpError(http.StatusBadRequest, errors.New("invalid request type"))
}

// Purge the request file once it's processed, to reduce
// the potential for replay attacks.
func() {
mut.Lock()
defer mut.Unlock()
delete(in_action, path)
os.Remove(reqpath)
}()

if reportable_err == nil {
payload["status"] = "SUCCESS"
dumpJsonResponse(w, http.StatusOK, &payload, path)
Expand Down

0 comments on commit 5424098

Please sign in to comment.