Github apps: Securing webhooks with Python in Flask

Posted on Wed 15 January 2020 in github apps

Once you have your Github webhooks working you will want to add a bit of security to make sure that the requests are actually coming from Github. One way to do this is to verify the signature Github sends matches the digest of the message.

Here is an example of how this can be done:

@app.route("/git-providers/1/events/", methods=['POST'])
@csrf.exempt
def events():
    # get the Github signature from the request header
    header_signature = request.headers.get('X-Hub-Signature')

    # pass request data and signature to verify function
    verify_signature(request.get_data(), header_signature)

    json = request.get_json()
    return jsonify({'message': 'success'}), 200


def verify_signature(request_data, header_signature):
    # do not store your secret key in your code, pull from environment variable
    secret_key = os.environ.get('GITHUB_WEBHOOK_SECRET')

    if not header_signature:
        return jsonify({'message': 'failure'}), 404

    # separate the signature from the sha1 indication
    sha_name, signature = header_signature.split('=')
    if sha_name != 'sha1':
        return jsonify({'message': 'failure'}), 501

    # create a new hmac with the secret key and the request data
    mac = hmac.new(secret_key.encode(), msg=request_data, digestmod='sha1')

    # verify the digest matches the signature
    if not hmac.compare_digest(mac.hexdigest(), signature):
        return jsonify({'message': 'failure'}), 404