2014-09-10 9 views
6

Sto utilizzando un webhook GitHub per eseguire il pipe degli eventi su una mia applicazione (un'istanza di Hubot di GitHub) ed è protetto con un segreto sha1.GitHub Webhook Secret Never Validates

Sto utilizzando il seguente codice per validare gli hash su webhooks in arrivo

crypto = require('crypto') 
signature = "sha1=" + crypto.createHmac('sha1', process.env.HUBOT_GITHUB_SECRET).update(new Buffer request.body).digest('hex') 
unless request.headers['x-hub-signature'] is signature 
    response.send "Signature not valid" 
    return 

L'intestazione X-Hub-Signature attraversato nel webhook assomiglia a questo

X-Hub-Signature : sha1 = 1cffc5d4c77a3f696ecd9c19dbc2575d22ffebd4

sto passando nella chiave e dati con precisione come da documentazione di GitHub, ma l'hash finisco sempre s up different.

Ecco la documentazione di GitHub. https://developer.github.com/v3/repos/hooks/#example

e questa è la sezione che sto molto probabilmente interpretando male

segreto: Una stringa opzionale che è passato con le richieste HTTP come un'intestazione X-Hub-Signature. Il valore di questa intestazione è calcolato come il digest esadecimale HMAC del corpo, utilizzando il segreto come chiave.

Qualcuno può vedere dove sto andando male?

risposta

8

Sembra non funzionare con un buffer, ma JSON.stringify(); Ecco il mio codice di lavoro:

var 
    hmac, 
    calculatedSignature, 
    payload = req.body; 

hmac = crypto.createHmac('sha1', config.github.secret); 
hmac.update(JSON.stringify(payload)); 
calculatedSignature = 'sha1=' + hmac.digest('hex'); 

if (req.headers['x-hub-signature'] === calculatedSignature) { 
    console.log('all good'); 
} else { 
    console.log('not good'); 
} 
+2

non sembra funzionare per me – ditoslav

+2

molto importante ricordare che il Hookshot GitHub * deve * disponga di un Content-Type impostato a 'application/json'. Troverai queste impostazioni nella pagina di configurazione del webhook. https://github.com/MY_ORG/MY_REPO/settings/hooks/HOOK_ID#delivery-response –

+0

Come gli stati di @MrClean, usa application/json! – Mattis

1

In aggiunta a Patrick's risposta. È opportuno utilizzare crypo.timingSafeEqual per confrontare i digest HMAC oi valori segreti. Ecco come:

const blob = JSON.stringify(req.body); 
const hmac = crypto.createHmac('sha1', process.env.GITHUB_WEBHOOK_SECRET); 
const ourSignature = `sha1=${hmac.update(blob).digest('hex')}`; 

const theirSignature = req.get('X-Hub-Signature'); 

const bufferA = Buffer.from(ourSignature, 'utf8'); 
const bufferB = Buffer.from(theirSignature, 'utf8'); 

const safe = crypto.timingSafeEqual(bufferA, bufferB); 

if (safe) { 
    console.log('Valid signature'); 
} else { 
    console.log('Invalid signature'); 
} 

Per saperne di più la differenza tra un sicuro confronto come timingEqual e un semplice === controllare questa discussione here.

crypto.timingSafeEqual è stato aggiunto in Node.js v6.6.0

2

aggiungendo anche alla risposta di Patrick, mi consiglia di utilizzare espresso insieme con il suo corpo-parser. Completa l'esempio qui sotto. Funziona con Express 4.x, Node 8.x (ultimo di scrittura).

Si prega di sostituire YOUR_WEBHOOK_SECRET_HERE e fare qualcosa nella funzione authorizationSuccessful.

// Imports 
const express = require('express'); 
const bodyParser = require('body-parser'); 
const crypto = require('crypto'); 

const app = express(); 
// The GitHub webhook MUST be configured to be sent as "application/json" 
app.use(bodyParser.json()); 

// Verification function to check if it is actually GitHub who is POSTing here 
const verifyGitHub = (req) => { 
    if (!req.headers['user-agent'].includes('GitHub-Hookshot')) { 
    return false; 
    } 
    // Compare their hmac signature to our hmac signature 
    // (hmac = hash-based message authentication code) 
    const theirSignature = req.headers['x-hub-signature']; 
    const payload = JSON.stringify(req.body); 
    const secret = 'YOUR_WEBHOOK_SECRET_HERE'; // TODO: Replace me 
    const ourSignature = `sha1=${crypto.createHmac('sha1', secret).update(payload).digest('hex')}`; 
    return crypto.timingSafeEqual(Buffer.from(theirSignature), Buffer.from(ourSignature)); 
}; 

const notAuthorized = (req, res) => { 
    console.log('Someone who is NOT GitHub is calling, redirect them'); 
    res.redirect(301, '/'); // Redirect to domain root 
}; 

const authorizationSuccessful =() => { 
    console.log('GitHub is calling, do something here'); 
    // TODO: Do something here 
}; 

app.post('*', (req, res) => { 
    if (verifyGitHub(req)) { 
    // GitHub calling 
    authorizationSuccessful(); 
    res.writeHead(200, { 'Content-Type': 'text/plain' }); 
    res.end('Thanks GitHub <3'); 
    } else { 
    // Someone else calling 
    notAuthorized(req, res); 
    } 
}); 

app.all('*', notAuthorized); // Only webhook requests allowed at this address 

app.listen(3000); 

console.log('Webhook service running at http://localhost:3000'); 
+0

Nota: corretto un errore nel caso in cui 'req.body' fosse stato modificato due volte. –

Problemi correlati