From 5b764e222f7a634fa72aeb3d6413da4c5e4e2f97 Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Wed, 11 Aug 2021 10:56:37 +0200 Subject: [PATCH] v0.1.2: Honeypot and typos * Added honeypot anti-spam feature * Corrected typos --- README.md | 6 ++++-- apirequest/sendform.go | 10 +++++++++- form/form.go | 4 ++-- main.go | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c48a27b..4296bb6 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,11 @@ simple API that can be accessed via JavaScript `Fetch()` or `XMLHttpRequest`. * Only display form-fields that are configured in for the form in the resulting mail * Check for required form fields * Anti-SPAM functionality via built-in, auto-expiring and single-use security token feature +* Anti-SPAM functionality via honeypot fields * Limit form access to specific domains * Per-form mail server configuration ### Planed features -* [ ] Anti-SPAM functionality via honeypot fields * [ ] Form field-type validation * [ ] Form body templates (possibly HTML) * [ ] hCaptcha/gCaptcha support @@ -77,7 +77,8 @@ Again the JSON syntax of the form configuration is very simple, yet flexible. "content": { "subject": "New message through the www.example.com contact form", "fields": ["name", "email", "message"], - "required_fields": ["name", "email"] + "required_fields": ["name", "email"], + "honeypot": "street" }, "server": { "host": "mail.example.com", @@ -97,6 +98,7 @@ Again the JSON syntax of the form configuration is very simple, yet flexible. * `subject (type: string)`: Subject for the mail notification of the form submission * `fields (type: []string)`: List of field names that should show up in the mail notification * `required_fields (type: []string)`: List of field names that are required to submitted + * `honeypot (type: string)`: Name of the honeypot field, that is expected to be empty (Anti-SPAM) * `server (type: struct)`: The struct for the forms mail server configuration * `host (type: string)`: Hostname of the sending mail server * `port (type: uint32)`: Port to connect to on the sending mail server diff --git a/apirequest/sendform.go b/apirequest/sendform.go index 2b26560..e5d5970 100644 --- a/apirequest/sendform.go +++ b/apirequest/sendform.go @@ -35,7 +35,7 @@ func (a *ApiRequest) SendFormParse(r *http.Request) (int, error) { return 0, nil } -// SendFormValidate validates that all requirement are fullfilled and returns an error +// SendFormValidate validates that all requirement are fulfilled and returns an error // if the validation failed func (a *ApiRequest) SendFormValidate(r *http.Request) (int, error) { l := log.WithFields(log.Fields{ @@ -77,6 +77,7 @@ func (a *ApiRequest) SendFormValidate(r *http.Request) (int, error) { missingFields := []string{} for _, f := range formObj.Content.RequiredFields { if r.Form.Get(f) == "" { + l.Warnf("Form includes a honeypot field which is not empty. Denying request") missingFields = append(missingFields, f) } } @@ -85,6 +86,13 @@ func (a *ApiRequest) SendFormValidate(r *http.Request) (int, error) { return 400, fmt.Errorf("Required fields missing: %s", strings.Join(missingFields, ", ")) } + // Anti-SPAM honeypot handling + if formObj.Content.Honeypot != nil { + if r.Form.Get(*formObj.Content.Honeypot) != "" { + return 400, fmt.Errorf("Invalid form data") + } + } + // Check the token reqOrigin := r.Header.Get("origin") if reqOrigin == "" { diff --git a/form/form.go b/form/form.go index 6a033b5..1977728 100644 --- a/form/form.go +++ b/form/form.go @@ -19,7 +19,7 @@ type Form struct { Subject string Fields []string RequiredFields []string `fig:"required_fields"` - Honeypot string `fig:"honeypot"` + Honeypot *string `fig:"honeypot"` } Server struct { Host string `fig:"host" validate:"required"` @@ -32,7 +32,7 @@ type Form struct { } // NewForm returns a new Form object to the caller. It fails with an error when -// the form is question wasn't found or does not fullfill the syntax requirements +// the form is question wasn't found or does not fulfill the syntax requirements func NewForm(c *config.Config, i string) (Form, error) { l := log.WithFields(log.Fields{ "action": "form.NewForm", diff --git a/main.go b/main.go index 034a55a..2433da6 100644 --- a/main.go +++ b/main.go @@ -33,7 +33,7 @@ func serve(c *config.Config) { } }() - // Initalize the Api request object + // Initialize the Api request object apiReq := &apirequest.ApiRequest{ Cache: cacheObj, Config: c,