Webhook Security

Shared Secret

Complexity
  • Very Low
Pros
  • Authentication with Low Complexity
Caveats
  • No message integrity and confidentiality controls
  • Requires HTTPS to keep credentials secret
Examples

In our research, 10% of the webhook providers use Shared Secrets — in the form of Basic Authentication, shared credentials, bearer tokens, or a verification token — for authentication. In this method, the webhook provider and listener share a common secret used exclusively to authenticate webhook requests:

  1. On webhook requests, the provider sends a webhook notification containing the message plus a shared secret in a pre-defined header variable or the Authorization header using the Basic Auth format ( Authorization: Basic <"username:password" in base64> ):

    // Compare secrets in constant time to avoid leaking them through response timing
    const safeEqual = (a, b) => {
      const bufA = Buffer.from(a || '', 'utf8')
      const bufB = Buffer.from(b || '', 'utf8')
      return bufA.length === bufB.length && crypto.timingSafeEqual(bufA, bufB)
    }
    
    
    app.post('/webhook', (req, res) => {
      
      // get authorization header (Basic <value>)
      const b64auth = (req.headers.authorization || '').split('Basic ')[1] || ''
      // Get login and password
      const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':')
    
    
      // Verify login and password are present and correct
      if (login && password && 
          safeEqual(login, process.env.WEBHOOK_LOGIN) && 
          safeEqual(password, process.env.WEBHOOK_PW)) {
        // Webhook Authenticated 
        // process and respond...
        res.json({ message: "Success" })
      }else{
        res.status(401).send('Authentication required.')
      }
    })
    
  2. The webhook listener validates the value in the request versus the shared secret. Only requests with the correct secret are processed.

Basic Authentication, or shared credentials, or verification token on webhook requests

This security method addresses only the webhook service authentication and does not implement any control on message integrity. Even with HTTPS, the shared secret is processed in clear text, increasing the risk of the secret getting compromised.

Therefore, their use should be avoided in production or used only with compensatory controls — such as IP Restrictions and callback requests — to mitigate risks. See Best Practices for Webhook Consumers.

Providers are moving away from shared secrets

Several providers that started with shared secrets have since added HMAC signatures:

  • GitLab sends the configured secret in the X-Gitlab-Token header, but now says the secret token is "not recommended for new webhooks". Its signing tokens follow the Standard Webhooks specification, and both can be configured at once during migration.
  • Zoom replaced its plaintext verification token with HMAC-SHA256 signatures and a URL validation challenge — see Zoom.
  • Slack sends a token with its URL verification handshake, but now marks it deprecated in favor of signing secrets.

Note

Some services offer basic auth as a quick option for development and unit testing, alongside more robust security controls for production usage. DocuSign, for example, offers basic authentication, Request Signatures with HMAC and Mutual TLS, and encourages the use of HMAC and mTLS in production use-cases.

Previous
← One Time Verification