Skip to content

fix(security): use HMAC with SECRET_KEY for webhook hash#443

Open
onovy wants to merge 5 commits intoredimp:mainfrom
onovy:fix/sec-17-webhook-hash
Open

fix(security): use HMAC with SECRET_KEY for webhook hash#443
onovy wants to merge 5 commits intoredimp:mainfrom
onovy:fix/sec-17-webhook-hash

Conversation

@onovy
Copy link
Copy Markdown
Contributor

@onovy onovy commented Mar 30, 2026

WARNING: This is breaking change and old tokens will stop to work!

@redimp
Copy link
Copy Markdown
Owner

redimp commented Apr 1, 2026

Thanks for submitting this PR! I'm questioning whether a strong secret is truly needed here, given the limited attack surface. Perhaps I'm missing something?

More importantly, this PR appears incomplete; it only covers the update of the hook validation and is missing to update to the generation of the hash in the admin interface.

@onovy
Copy link
Copy Markdown
Contributor Author

onovy commented Apr 1, 2026

An attacker who knows (or guesses) the remote URL can compute the hash and trigger spurious pulls — but pulls only come from the already-configured remote, so the damage is limited to triggering unnecessary git operations.
Attack surface is small/limited, but this can be used to:

  • DoS Otterwiki instance (git pull is not cheap)
  • Reflection DDoS, when many public Otter wiki instances can DDoS same git source (for example github.com)

Ad generation in admin: That's thing I was missing, "where is it generated for user?" :). Sorry for mistake, working on fix now.

@redimp
Copy link
Copy Markdown
Owner

redimp commented Apr 1, 2026

I thought about the DoS aspect of the pull webhook and discussed it briefly with @deseven.

I'm not as much concerned about an attacker abusing the webhook than accidental calls to the webhook. So this could be improved by only allowing a webhook pull every 60s and returning a 429 else.

@onovy
Copy link
Copy Markdown
Contributor Author

onovy commented Apr 1, 2026

in my POV rate limiting is just workaround. Problem is still there: (Almost) anyone, without authentication can trigger "git pull". Webhook secret (by definition and it's name :) should be secret. Current implementation is just security by obscurity, hash check is useless now.

@onovy
Copy link
Copy Markdown
Contributor Author

onovy commented Apr 1, 2026

ad rate limit: This could break it more. For example two commits done to external repository in short period = two web hook trigger, second one fails => inconsistency.

Another idea, let's make it backward compatible:

  • generate only new tokens
  • accept old and new tokens
  • add config option which disables accepting of old tokens - default false

@redimp
Copy link
Copy Markdown
Owner

redimp commented Apr 1, 2026

I would assume anything triggering the webhook url has a proper retry mechanism .. and a 429 is exactly made for this. It tells the client to retry.

@onovy onovy marked this pull request as draft April 1, 2026 20:21
@deseven
Copy link
Copy Markdown
Contributor

deseven commented Apr 1, 2026

Another idea, let's make it backward compatible:

* generate only new tokens

* accept old and new tokens

* add config option which disables accepting of old tokens - default false

Could be handled more gracefully:

  • add /-/api/v2/pull/<string:secure_webhook_hash> route, keep the v1 one intact with current logic
  • generate new v2 links in the admin panel using the new logic

The generated link in the admin panel will no longer match the link used in previously configured setups, but the old links will still work.

UPD: Ah, well, in that case it will be still possible to use the old route :D Maybe we just need to add a special property to the remotes that are using the new logic? Like GIT_REMOTE_PULL_URL_SECURE=true which would be false/undefined on existing setups and would be automatically set to true if user changes the preferences in the admin panel. We can also write a warning there that the webhook URL will be changed on save. Still feels a bit better than some env param that everyone will forget to change.

@onovy onovy marked this pull request as ready for review April 2, 2026 08:46
@onovy
Copy link
Copy Markdown
Contributor Author

onovy commented Apr 2, 2026

Now it's completely backward compatible.

const hash = await generateWebhookHash(remoteUrl);
const webhookUrl = window.location.origin + '/-/api/v1/pull/' + hash;
webhookUrlInput.value = webhookUrl;
webhookUrlInput.value = '... save first to show new pull webhook URL ...';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of this personally, maybe better to hide the field completely and instead write a nicely looking note? We can also write here some warning when GIT_REMOTE_PULL_URL_SECURE is not true so the user will be aware that their current configuration needs to be updated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, fixed in last commit

- Add 'Regenerate secure webhook URL' checkbox to admin panel that
  upgrades GIT_REMOTE_PULL_URL_SECURE to True on save without
  requiring the user to change the remote URL
- Add warning alert when webhook uses legacy insecure hash
- Hide webhook URL field (show note instead) when URL changes in JS
- Add tests for checkbox upgrade behavior and warning visibility
@redimp
Copy link
Copy Markdown
Owner

redimp commented Apr 8, 2026

Hey @onovy, thanks for the effort. Will review and check later this week.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants