Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
<?php
/**
* GitHub webhook handler template.
*
* @see https://developer.github.com/webhooks/
* @author Miloslav Hůla (https://github.com/milo)
*/
$hookSecret = 's.e.c.r.e.t'; # set NULL to disable check
set_error_handler(function($severity, $message, $file, $line) {
throw new \ErrorException($message, 0, $severity, $file, $line);
});
set_exception_handler(function($e) {
header('HTTP/1.1 500 Internal Server Error');
echo "Error on line {$e->getLine()}: " . htmlSpecialChars($e->getMessage());
die();
});
$rawPost = NULL;
if ($hookSecret !== NULL) {
if (!isset($_SERVER['HTTP_X_HUB_SIGNATURE'])) {
throw new \Exception("HTTP header 'X-Hub-Signature' is missing.");
} elseif (!extension_loaded('hash')) {
throw new \Exception("Missing 'hash' extension to check the secret code validity.");
}
list($algo, $hash) = explode('=', $_SERVER['HTTP_X_HUB_SIGNATURE'], 2) + array('', '');
if (!in_array($algo, hash_algos(), TRUE)) {
throw new \Exception("Hash algorithm '$algo' is not supported.");
}
$rawPost = file_get_contents('php://input');
if ($hash !== hash_hmac($algo, $rawPost, $hookSecret)) {
throw new \Exception('Hook secret does not match.');
}
};
if (!isset($_SERVER['HTTP_CONTENT_TYPE'])) {
throw new \Exception("Missing HTTP 'Content-Type' header.");
} elseif (!isset($_SERVER['HTTP_X_GITHUB_EVENT'])) {
throw new \Exception("Missing HTTP 'X-Github-Event' header.");
}
switch ($_SERVER['HTTP_CONTENT_TYPE']) {
case 'application/json':
$json = $rawPost ?: file_get_contents('php://input');
break;
case 'application/x-www-form-urlencoded':
$json = $_POST['payload'];
break;
default:
throw new \Exception("Unsupported content type: $_SERVER[HTTP_CONTENT_TYPE]");
}
# Payload structure depends on triggered event
# https://developer.github.com/v3/activity/events/types/
$payload = json_decode($json);
switch (strtolower($_SERVER['HTTP_X_GITHUB_EVENT'])) {
case 'ping':
echo 'pong';
break;
// case 'push':
// break;
// case 'create':
// break;
default:
header('HTTP/1.0 404 Not Found');
echo "Event:$_SERVER[HTTP_X_GITHUB_EVENT] Payload:\n";
print_r($payload); # For debug only. Can be found in GitHub hook log.
die();
}
@dereckson

This comment has been minimized.

Copy link

dereckson commented Nov 21, 2015

Use the hash_equals method instead of == in PHP 5.6+ to compare strings. That will prevent theorical timing attacks to guess the secret.

@madchops1

This comment has been minimized.

Copy link

madchops1 commented Mar 24, 2016

This is awesome dude!

@aapis

This comment has been minimized.

Copy link

aapis commented Apr 19, 2016

Awesome. 👍

@victorsmirnov

This comment has been minimized.

Copy link

victorsmirnov commented Apr 29, 2016

Why do you use $_SERVER['HTTP_CONTENT_TYPE'] instead of $_SERVER['CONTENT_TYPE']? In my setup (PHP 5.6.4 and Apache 2) only the last is available but the first is not defined even when the header exists.

Thank you!

@lhermann

This comment has been minimized.

Copy link

lhermann commented Aug 11, 2016

Also had to change $_SERVER['HTTP_CONTENT_TYPE'] to $_SERVER['CONTENT_TYPE'] like @victorsmirnov, otherwise perfect.
Thank you!

@piperone

This comment has been minimized.

Copy link

piperone commented Jun 13, 2017

Cool. Thanks!

@mnolte

This comment has been minimized.

Copy link

mnolte commented Sep 23, 2017

Nice one! Thanks

@u01jmg3

This comment has been minimized.

Copy link

u01jmg3 commented Oct 19, 2017

Cheers! For me I had to change HTTP_CONTENT_TYPE to CONTENT_TYPE and I used hash_equals() as per the suggestion of @dereckson.

@yulinzhihou

This comment has been minimized.

Copy link

yulinzhihou commented Jun 25, 2019

nice good job

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.