{"record":{"id":"7cf3d1972254b5b0","repo":"knadh/listmonk","slug":"invalid-azure-event-grid-shared-secret","errorCode":null,"errorMessage":"invalid azure event grid shared secret","messagePattern":"invalid azure event grid shared secret","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/bounce/webhooks/azure.go","lineNumber":170,"sourceCode":"\tif req == nil {\n\t\treturn errors.New(\"missing azure event grid request context\")\n\t}\n\n\tquerySecret := strings.TrimSpace(req.URL.Query().Get(querySecretParam))\n\tif secretsEqual(a.sharedSecret, querySecret) {\n\t\treturn nil\n\t}\n\n\theaderName := a.sharedSecretHeader\n\tif headerName == \"\" {\n\t\theaderName = defaultSecretHeader\n\t}\n\theaderSecret := strings.TrimSpace(req.Header.Get(headerName))\n\tif secretsEqual(a.sharedSecret, headerSecret) {\n\t\treturn nil\n\t}\n\n\treturn errors.New(\"invalid azure event grid shared secret\")\n}\n\nfunc secretsEqual(expected, given string) bool {\n\tif expected == \"\" || given == \"\" {\n\t\treturn false\n\t}\n\treturn subtle.ConstantTimeCompare([]byte(expected), []byte(given)) == 1\n}\n\nfunc parseAzureEvents(b []byte) ([]azureEvent, error) {\n\tvar raws []json.RawMessage\n\tif err := json.Unmarshal(b, &raws); err != nil {\n\t\treturn nil, fmt.Errorf(\"error unmarshalling azure notification array: %v\", err)\n\t}\n\n\tevents := make([]azureEvent, 0, len(raws))\n\tfor _, raw := range raws {\n\t\tev := azureEvent{RawData: raw}","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/internal/bounce/webhooks/azure.go#L152-L188","documentation":"verifyAuth compares the configured shared secret against the value supplied in the webhook request — either the query parameter (querySecretParam) or the configured header. This error is returned when neither location carries a value equal to the configured shared secret, so the Azure Event Grid delivery is rejected as unauthenticated.","triggerScenarios":"A POST arrives at the Azure webhook while a shared secret is configured, but the request carries no secret or a wrong secret in both the expected query parameter and header.","commonSituations":"The Azure Event Grid subscription was created without appending the secret to the webhook URL query string; the secret was rotated in the app config but the subscription still uses the old URL; a proxy strips custom headers or the query string; the endpoint is being probed by unauthorized traffic.","solutions":["Re-register the Event Grid subscription with the secret appended, e.g. https://host/webhooks/azure?secret=<sharedSecret>, or configure the matching header on delivery.","Verify the configured shared secret in the app matches the one embedded in the Event Grid subscription URL/header; rotate both together.","Check that any reverse proxy or WAF in front of the app forwards the query parameter and custom header intact.","If this error appears from unknown scanners, it is working as intended — consider rate-limiting/blocking the source IP."],"exampleFix":"// before\naz bg create --topic ... --endpoint https://host/webhooks/azure\n// after\naz bg create --topic ... --endpoint \"https://host/webhooks/azure?secret=YOUR_SHARED_SECRET\"","handlingStrategy":"try-catch","validationCode":"u, _ := url.Parse(webhookURL)\nif u.Query().Get(\"secret\") == \"\" {\n\t// subscription URL lacks the shared secret; fix before registering with Event Grid\n}","typeGuard":"func azureWebhookURLHasSecret(raw string) bool {\n\tu, err := url.Parse(raw)\n\treturn err == nil && strings.TrimSpace(u.Query().Get(\"secret\")) != \"\"\n}","tryCatchPattern":"bounces, err := azure.ProcessBounce(body, req)\nif err != nil {\n\tif strings.Contains(err.Error(), \"invalid azure event grid shared secret\") {\n\t\t// return 401; alert if the source IP is not an Azure datacenter\n\t\treturn echo.NewHTTPError(http.StatusUnauthorized)\n\t}\n\treturn err\n}","preventionTips":["Embed the secret in the webhook URL query (or header) when creating the Event Grid subscription.","Rotate the secret in the app and re-register the subscription together.","Ensure reverse proxies forward query params and custom headers unchanged.","Monitor this error for scanner traffic and rate-limit offenders."],"tags":["azure","event-grid","webhook","auth","hmac"],"backgroundTag":"webhook-signature-mismatch","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}