Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Tell HN: GitHub might have been leaking your webhook secrets. Check your emails.
36 points by ssiddharth 1 day ago | hide | past | favorite | 12 comments
Got an email from Github a few minutes back asking me to rotate my webhook secrets, the relevant portions of it below.

We're writing to let you know that between September 2025 and January 2026, webhook secrets for webhooks you are responsible for were inadvertently included in an HTTP header on webhook deliveries. This means that any system receiving webhook payloads during this window could have logged the webhook secret from the request headers. Webhook deliveries are encrypted in transit via TLS, so the header containing the secret was only accessible to the receiving endpoint in a base64-encoded format. We have no evidence to suggest your secrets were intercepted. This issue was fixed on January 26, 2026. Please read on for more information.

User privacy and security are essential for maintaining trust, and we want to remain as transparent as possible about events like these. GitHub itself did not experience a compromise or data breach as a result of this event.

What happened?

On January 26, 2026, GitHub identified a bug in a new version of the webhook delivery platform where webhook secrets were included in an `X-Github-Encoded-Secret` HTTP header sent with webhook payloads. This header was not intended to be part of the delivery and made the webhook secret available to the receiving endpoint in a base64-encoded format. Webhook secrets are used to verify that deliveries are genuinely from GitHub, and should only be known to GitHub and the webhook owner.

The bug was limited to only a subset of webhook deliveries that were feature flagged to use this new version of the webhooks platform. The bug was present between September 11, 2025, and December 10, 2025, and briefly on January 5, 2026. The bug was fixed on January 26, 2026.

What information was involved?

The webhook secret for each affected webhook was included in HTTP request headers during the window that the bug was present. The webhook payload content itself was delivered normally and was not additionally affected. No other credentials or tokens were affected. Webhook deliveries are encrypted in transit via TLS, so the header containing the secret was only accessible to the receiving endpoint.

If the receiving system logged HTTP request headers, the webhook secret may be present in those logs. The webhook secret is used to compute the `X-Hub-Signature-256` HMAC signature on deliveries — if compromised, an attacker who knows the secret could forge webhook payloads to make them appear to come from GitHub.

 help



Assuming I've understood correctly...

Checking the listed impacted web hooks I noticed that no repo's listed twice, even though some have multiple hooks defined... That makes me think the report may have a bug.

Also, many of the listed hooks show as `This hook has never been triggered`; which if correct means it wouldn't have had an issue as the compromised payload's never been sent to it.

I'll write a powershell script (well, AI will write it and then I'll tweak) to help check which webhooks have been called in this time window (or at least, say they were most recently triggered after the issue first began) to help get a more accurate report of what's concerning. If that proves useful, I'll share here.


Powershell Script to get all webhooks that have actually run (we can't filter by date; but this may wipe out a significant number):

    # Authenticate via `gh auth login -s admin:enterprise` before running this script
    # Save this script as c:\\temp\GHWebookAudit.ps1; then navigate to c:\\temp\ and invoke via `.\GHWebookAudit.ps1 -Orgs @("MyExampleOrg", "MyOtherExampleOrg") -InformationAction Continue`
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string[]]$Orgs
    )

    $results = foreach ($org in $Orgs) {
        Write-Information "Fetching ALL repositories for org [$org]..."
        
        # gh api --paginate fetches every page and outputs a stream of JSON arrays
        # We use -Raw to ensure we capture the full stream before converting
        $reposJson = gh api --paginate "orgs/$org/repos?per_page=1000"
        
        # ConvertFrom-Json can handle multiple JSON arrays in the stream
        $repos = $reposJson | ConvertFrom-Json

        # Sometimes the pagination returns a single array or a list of arrays; 
        # Ensure we are iterating over the objects themselves.
        $repoList = if ($repos.GetType().IsArray -and $repos[0].GetType().IsArray) { 
            $repos | ForEach-Object { $_ } 
        } else { 
            $repos 
        }

        Write-Information "Found $($repoList.Count) repositories. Starting audit..." 

        foreach ($repoObj in $repoList) {
            $repoName = $repoObj.name
            $fullRepo = "$org/$repoName"
            Write-Information "Checking: $fullRepo"

            # Get all hooks for the repository
            $hooksJson = gh api "repos/$fullRepo/hooks" 2>$null
            if (-not $hooksJson) { continue }
            
            $hooks = $hooksJson | ConvertFrom-Json

            foreach ($hook in $hooks) {
                $hookId = $hook.id
                $hookUrl = $hook.config.url

                # Get the most recent delivery... we could pontentially check for activity between the impacted dates, but that would need additional filters/commplexity; for now keeping it relatively simple
                # correction: this only goes back 3 days; so doesn't work... https://docs.github.com/en/webhooks/testing-and-troubleshooting-webhooks/viewing-webhook-deliveries
                # # $deliveriesJson = gh api "repos/$fullRepo/hooks/$hookId/deliveries" --limit 1 2>$null
                # instead use th eexisting hook response's last response status just to say if it has run; though that doesn't say when

                # Extract last response details
                $lastStatus = $hook.last_response.status
                $lastCode   = $hook.last_response.code
                $hasEverRun = ($lastStatus -eq 'active')  # active vs unused

                [PSCustomObject]@{
                    Organization = $org
                    Repository   = $repoName
                    HookID       = $hookId
                    Active       = $hook.active
                    URL          = $hookUrl
                    HasEverRun   = $hasEverRun
                    LastStatus   = $lastStatus
                    LastHTTPCode = $lastCode
                    UpdatedAt    = $hook.updated_at
                }
            }
        }
    }

    # Final Output
    if ($results.Count) {
        $results | Export-Csv -Path "./Full_Webhook_Audit_Report.csv" -NoTypeInformation
        Write-Information "Audit complete! $($results.Count) hooks found. Results saved to Full_Webhook_Audit_Report.csv"
    } else {
        Write-Information "No webhooks found across the organizations."
    }

ps. As for what to do with the findings.

Filter for `HasEverRun=true`; if it's not run, the secrets can't have been exposed.

Review the URL; this says who you're calling. Purists would say that if you've called any endpoints there's a risk. However for most companies I'd say you can trust services provided by folk like Microsoft (they host GitHub anyway) and Snyk (if you're relying on them for security scanning, you should be able to trust them), so if you see webhooks to Azure DevOps (dev.azure.com) or to Snyk (api.snyk.io) you can assume that anything exposed there isn't a concern. You don't need to worry about proxys on egress - since this is GitHub Cloud, so calls egress from GitHub rather than through your own network. So the only concerns would be in house or third party developed endpoints where you feel those companies (/their employees who have access to their ingress logs) may be a risk.


Got that too. My first reaction: Go to HN to understand what's going on. Where are the comments?

> webhook secrets for webhooks you are responsible for were inadvertently included in an HTTP header on webhook deliveries

LOL how does this even happen?


Same reaction of mine as well. I mean, how do you even fck up this way? ... I dont know why, but, this is giving me vibe-coded vibes.

Developer might have prompted to include some signature (definitely they didn't use this word, or else AI would not have messed this way) to verify the webhooks as being coming from legitimate source, and AI probably went ahead with the secret key itself :)


Waiting 3 months to disclose this is suboptimal.

I don't understand how this story is not in the front page yet.

Closing the barn door well after the horses are long gone.

How is this not a bigger deal on hn? Was expecting a lot more conversation

yeah, I was wondering the same.. kinda big deal that they first of all had the issue and second, that they fixed it in January and only now message about it

How come it took them so much time to send this notification? I'm so fed up with their bs.



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: