{"record":{"id":"aadffb021e79cfc1","repo":"knadh/listmonk","slug":"failed-to-verify-captcha-solution-w","errorCode":null,"errorMessage":"failed to verify captcha solution: %w","messagePattern":"failed to verify captcha solution: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/captcha/captcha.go","lineNumber":190,"sourceCode":"\t}\n\n\tvar r hCaptchaResp\n\tif err := json.Unmarshal(body, &r); err != nil {\n\t\treturn err, true\n\t}\n\n\tif !r.Success {\n\t\treturn fmt.Errorf(\"hCaptcha failed: %s\", strings.Join(r.ErrorCodes, \",\")), false\n\t}\n\n\treturn nil, true\n}\n\n// verifyAltcha verifies an Altcha response.\nfunc (c *Captcha) verifyAltcha(payload string) (error, bool) {\n\tvalid, err := altcha.VerifySolution(payload, c.altcha.HMACKey, true)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to verify captcha solution: %w\", err), false\n\t}\n\n\tif !valid {\n\t\treturn fmt.Errorf(\"captcha verification failed\"), false\n\t}\n\n\t// Disallow token reuse.\n\tif _, err := tmptokens.Check(payload); err == nil {\n\t\treturn fmt.Errorf(\"captcha token already used\"), false\n\t}\n\ttmptokens.Set(payload, 5*time.Minute, nil)\n\n\treturn nil, true\n}\n","sourceCodeStart":172,"sourceCodeEnd":205,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/internal/captcha/captcha.go#L172-L205","documentation":"verifyAltcha wraps an error returned by altcha.VerifySolution when the submitted Altcha payload cannot even be processed — malformed base64/JSON payload, signature check error, or other altcha internal failure. This differs from a mere incorrect solution (which returns 'captcha verification failed'); here the payload itself could not be evaluated.","triggerScenarios":"Calling Verify with ProviderAltcha where the payload string is not a valid base64-encoded signed challenge response — truncated payloads, tampered payloads, payloads generated against a different HMAC key, or non-base64 garbage submitted by clients.","commonSituations":"Client JS version producing a payload format the server's altcha library can't parse (version mismatch); attacker probing the endpoint with junk payloads; HMAC key rotated server-side while old pages still hold challenges signed with the previous key.","solutions":["Unwrap the error to see whether it is a base64/JSON parse error or a signature error.","Ensure server and client use the same altcha algorithm version and HMAC key.","Reject with 400 immediately — malformed payloads indicate bots or broken clients, not retryable server issues.","After rotating the HMAC key, invalidate old challenges (they fail signature checks)."],"exampleFix":"// before: same handling for parse errors and wrong solutions\nvalid, err := altcha.VerifySolution(payload, c.altcha.HMACKey, true)\n// after: distinguish client error from verification failure\nif _, ok := err.(*base64.CorruptInputError); ok {\n  return errors.New(\"malformed captcha payload\"), false // 400\n}","handlingStrategy":"validation","validationCode":"payload := r.PostFormValue(\"altcha\")\nif payload == \"\" {\n  return errors.New(\"altcha payload missing\")\n}\ndecoded, err := base64.StdEncoding.DecodeString(payload)\nif err != nil {\n  return errors.New(\"altcha payload is not valid base64\")\n}\nvar p map[string]any\nif err := json.Unmarshal(decoded, &p); err != nil {\n  return errors.New(\"altcha payload is not valid JSON\")\n}","typeGuard":"func isWellFormedAltchaPayload(payload string) bool {\n  decoded, err := base64.StdEncoding.DecodeString(payload)\n  if err != nil {\n    return false\n  }\n  var probe struct {\n    Algorithm string `json:\"algorithm\"`\n    Challenge string `json:\"challenge\"`\n    Salt      string `json:\"salt\"`\n    Signature string `json:\"signature\"`\n  }\n  return json.Unmarshal(decoded, &probe) == nil && probe.Challenge != \"\" && probe.Signature != \"\"\n}","tryCatchPattern":"if err, ok := captcha.Verify(payload); !ok {\n  if err != nil && strings.Contains(err.Error(), \"failed to verify captcha solution\") {\n    // unparseable payload: client error, not retryable server-side\n    log.Warn(\"malformed altcha payload (possible bot or version mismatch)\", \"err\", err)\n    http.Error(w, \"invalid captcha payload\", http.StatusBadRequest)\n    return\n  }\n}","preventionTips":["Keep the client altcha widget and server altcha library versions in sync","Pre-validate base64/JSON shape of the payload before calling Verify","Coordinate HMAC key rotations with frontend deployments","Rate-limit the verification endpoint — malformed payloads usually come from bots"],"tags":["go","captcha","altcha","verification","signature"],"backgroundTag":"captcha-payload-invalid","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}