mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-10 11:24:39 +03:00
Added secret handling for webhooks (#13980)
closes: https://github.com/TryGhost/Team/issues/1203 refs: https://github.com/TryGhost/Ghost/issues/9942 - Ensures that the webhook secret is validated and saved in Ghost admin - Then makes use of this value by optionally adding an X-Ghost-Signature header that effectively signs the webhooks - This allows for verifying the source of a webhook coming from Ghost is truly Ghost. - Uses the same pattern as GitHub uses: https://docs.github.com/en/developers/webhooks-and-events/webhooks/securing-your-webhooks Co-authored-by: Hannah Wolfe <github.erisds@gmail.com>
This commit is contained in:
parent
c3fb0ef578
commit
36d9ae36ae
@ -954,3 +954,4 @@ add|ember-template-lint|no-passed-in-event-handlers|176|44|176|44|dcb4785647a508
|
||||
add|ember-template-lint|no-passed-in-event-handlers|186|44|186|44|70487c008d7dda453fef82f0140699ee93c0055c|1660521600000|1670893200000|1676077200000|app/components/modal-tier.hbs
|
||||
add|ember-template-lint|style-concatenation|303|54|303|54|23293f0c3838b23432d2b2daaf04b34504896d91|1660608000000|1670979600000|1676163600000|app/components/modal-tier.hbs
|
||||
remove|ember-template-lint|no-nested-interactive|23|28|23|28|5cf783a5684dda036706ff7438472e99c60a88e7|1658102400000|1668474000000|1673658000000|app/templates/whatsnew.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|72|20|72|20|e639a281bd34eefbe403ddf46501154b89a1f477|1661212800000|1671584400000|1676768400000|app/components/modal-webhook-form.hbs
|
||||
|
@ -69,7 +69,7 @@
|
||||
<label for="webhook-secret" class="fw6">Secret</label>
|
||||
<GhTextInput
|
||||
@value={{readonly this.webhook.secret}}
|
||||
@oninput={{action (mut this.webhook.secret) value="target.value"}}
|
||||
@input={{action (mut this.webhook.secret) value="target.value"}}
|
||||
@focus-out={{action "validate" "secret" target=this.webhook}}
|
||||
@id="webhook-secret"
|
||||
@name="secret"
|
||||
|
@ -3,7 +3,7 @@ import validator from 'validator';
|
||||
import {isBlank} from '@ember/utils';
|
||||
|
||||
export default BaseValidator.create({
|
||||
properties: ['name', 'event', 'targetUrl'],
|
||||
properties: ['name', 'event', 'targetUrl', 'secret'],
|
||||
|
||||
name(model) {
|
||||
if (isBlank(model.name)) {
|
||||
@ -39,5 +39,13 @@ export default BaseValidator.create({
|
||||
if (model.errors.has('targetUrl')) {
|
||||
this.invalidate();
|
||||
}
|
||||
},
|
||||
|
||||
secret(model) {
|
||||
if (!isBlank(model.secret) && !validator.isLength(model.secret, 0, 191)) {
|
||||
model.errors.add('secret', 'Secret is too long, max 191 chars');
|
||||
model.hasValidated.pushObject('secret');
|
||||
this.invalidate();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
const debug = require('@tryghost/debug')('services:webhooks:trigger');
|
||||
const logging = require('@tryghost/logging');
|
||||
const ghostVersion = require('@tryghost/version');
|
||||
const crypto = require('crypto');
|
||||
|
||||
class WebhookTrigger {
|
||||
/**
|
||||
@ -85,13 +86,21 @@ class WebhookTrigger {
|
||||
|
||||
const reqPayload = JSON.stringify(hookPayload);
|
||||
const url = webhook.get('target_url');
|
||||
const secret = webhook.get('secret') || '';
|
||||
|
||||
const headers = {
|
||||
'Content-Length': Buffer.byteLength(reqPayload),
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Version': `v${ghostVersion.safe}`
|
||||
};
|
||||
|
||||
if (secret !== '') {
|
||||
headers['X-Ghost-Signature'] = `sha256=${crypto.createHmac('sha256', secret).update(reqPayload).digest('hex')}, t=${Date.now()}`;
|
||||
}
|
||||
|
||||
const opts = {
|
||||
body: reqPayload,
|
||||
headers: {
|
||||
'Content-Length': Buffer.byteLength(reqPayload),
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Version': `v${ghostVersion.safe}`
|
||||
},
|
||||
headers,
|
||||
timeout: 2 * 1000,
|
||||
retry: 5
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user