如默 发表于 2023-10-26 22:12:21

用Python实现Gitee的webhook出问题了

本帖最后由 如默 于 2023-10-26 22:15 编辑

如题,我用Python实现gitee的webhook,用的是flask,代码如下:
import os
import time
import hmac
import hashlib
import base64
import urllib
from flask import Flask, request

app = Flask(__name__)

SECRET = 'mySecret'# 替换为您的签名密钥

def generate_signature(timestamp):
    timestamp_str = str(timestamp)
    secret_enc = SECRET.encode('utf-8')
    string_to_sign = '{}\n{}'.format(timestamp_str, SECRET)
    string_to_sign_enc = string_to_sign.encode('utf-8')
    hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
    signature = urllib.parse.quote_plus(base64.b64encode(hmac_code))
    # signature = base64.b64encode(hmac_code).decode()
    # url_encoded_signature = urllib.parse.quote(signature, safe='')
    return signature

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    # 验证请求来源是否是Gitee
    if request.headers.get('User-Agent') == 'git-oschina-hook':
      # 验证签名
      timestamp = int(request.headers.get('X-Gitee-Timestamp'))
      actual_signature = request.headers.get('X-Gitee-Token')
      expected_signature = generate_signature(timestamp)
      if actual_signature != expected_signature:
            return '', 403# 返回拒绝访问状态码

      # 在这里编写处理Webhook请求的代码逻辑
      data = request.json
      print('Received webhook request:', data)
      # 执行 git pull、pnpm install 和 pnpm build 等操作
      # ...

      return '', 200# 返回成功状态码
    else:
      return '', 403# 返回拒绝访问状态码

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=3002)


放在服务器上运行之后,在gitee的后台测试,发现报403错误,gitee的返回的token信息如下:uinoZjLDU4UB97wNM1xNB1XCAuTA5Urk8BKYrSxc1g0=

下面是完整的返回的header信息

Request URL: https://xxx.com/webhook
Request Method: POST
X-Gitee-Token: uinoZjLDU4UB97wNM1xNB1XCAuTA5Urk8BKYrSxc1g0=
X-Gitee-Event: push_hooks
User-Agent: git-oschina-hook
X-Gitee-Timestamp: 1698328784458
X-Gitee-Ping: true
Content-Type: application/json
X-Git-Oschina-Event: push_hooks

查看flask的运行日志,得到的日志内容是:

127.0.0.1 - - "POST /webhook?sign=uinoZjLDU4UB97wNM1xNB1XCAuTA5Urk8BKYrSxc1g0%3D&timestamp=1698328784458 HTTP/1.1" 403 -

可以看到token是不一致的,差了一个=号,不知道为什么,官方文档的地址是:https://help.gitee.com/webhook/how-to-verify-webhook-keys

哪位大佬能帮忙看看啊,实在不知道啥问题了,唉

isdkz 发表于 2023-10-26 22:13:00

问题出在对生成的签名进行url编码时,使用了错误的方法。在代码中,使用了 urllib.parse.quote_plus 对签名进行url编码,而实际上应该使用 urllib.parse.quote 方法。



正确的代码如下:



import os

import time

import hmac

import hashlib

import base64

import urllib

from flask import Flask, request



app = Flask(__name__)



SECRET = 'mySecret'# 替换为您的签名密钥



def generate_signature(timestamp):

    timestamp_str = str(timestamp)

    secret_enc = SECRET.encode('utf-8')

    string_to_sign = '{}\n{}'.format(timestamp_str, SECRET)

    string_to_sign_enc = string_to_sign.encode('utf-8')

    hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()

    signature = urllib.parse.quote(base64.b64encode(hmac_code), safe='')

    return signature



@app.route('/webhook', methods=['POST'])

def handle_webhook():

    # 验证请求来源是否是Gitee

    if request.headers.get('User-Agent') == 'git-oschina-hook':

      # 验证签名

      timestamp = int(request.headers.get('X-Gitee-Timestamp'))

      actual_signature = request.headers.get('X-Gitee-Token')

      expected_signature = generate_signature(timestamp)

      if actual_signature != expected_signature:

            return '', 403# 返回拒绝访问状态码



      # 在这里编写处理Webhook请求的代码逻辑

      data = request.json

      print('Received webhook request:', data)

      # 执行 git pull、pnpm install 和 pnpm build 等操作

      # ...



      return '', 200# 返回成功状态码

    else:

      return '', 403# 返回拒绝访问状态码



if __name__ == '__main__':

    app.run(host='0.0.0.0', port=3002)





通过使用 urllib.parse.quote 方法对签名进行url编码,并指定 safe 参数为空,可以正确地生成签名字符串。这样,Gitee的返回的token信息与生成的签名就能匹配上了,从而解决了403错误的问题。

isdkz 发表于 2023-10-26 22:13:43

如默 发表于 2023-10-26 22:17:09

isdkz 发表于 2023-10-26 22:13
问题出在对生成的签名进行url编码时,使用了错误的方法。在代码中,使用了 urllib.parse.quote_plus 对签名 ...

我试试

如默 发表于 2023-10-26 22:22:34

isdkz 发表于 2023-10-26 22:13
问题出在对生成的签名进行url编码时,使用了错误的方法。在代码中,使用了 urllib.parse.quote_plus 对签名 ...

唉,还是不行啊,这是代码:
import os
import time
import hmac
import hashlib
import base64
import urllib
from flask import Flask, request

app = Flask(__name__)

SECRET = 'mySecret'# 替换为您的签名密钥

def generate_signature(timestamp):
    timestamp_str = str(timestamp)
    secret_enc = SECRET.encode('utf-8')
    string_to_sign = '{}\n{}'.format(timestamp_str, SECRET)
    string_to_sign_enc = string_to_sign.encode('utf-8')
    hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
    signature = urllib.parse.quote(base64.b64encode(hmac_code), safe='')
    # signature = base64.b64encode(hmac_code).decode()
    # url_encoded_signature = urllib.parse.quote(signature, safe='')
    return signature

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    # 验证请求来源是否是Gitee
    if request.headers.get('User-Agent') == 'git-oschina-hook':
      # 验证签名
      timestamp = int(request.headers.get('X-Gitee-Timestamp'))
      actual_signature = request.headers.get('X-Gitee-Token')
      expected_signature = generate_signature(timestamp)
      if actual_signature != expected_signature:
            return '', 403# 返回拒绝访问状态码

      # 在这里编写处理Webhook请求的代码逻辑
      data = request.json
      print('Received webhook request:', data)
      # 执行 git pull、pnpm install 和 pnpm build 等操作
      # ...

      return '', 200# 返回成功状态码
    else:
      return '', 403# 返回拒绝访问状态码

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=3002)


这是flask日志:
127.0.0.1 - - "POST /webhook?sign=%200CTC8AsNbKn%20SZde1kyMYsnIP0b6nJ8IWoO7pqn754%3D&timestamp=1698330008081 HTTP/1.1" 403 -

这是Gitee后台header信息:
Request URL: https://xxx.com/webhook
Request Method: POST
X-Gitee-Token: +0CTC8AsNbKn+SZde1kyMYsnIP0b6nJ8IWoO7pqn754=
X-Gitee-Event: push_hooks
User-Agent: git-oschina-hook
X-Gitee-Timestamp: 1698330008081
X-Gitee-Ping: true
Content-Type: application/json
X-Git-Oschina-Event: push_hooks

如默 发表于 2023-10-26 22:23:24

isdkz 发表于 2023-10-26 22:13
问题出在对生成的签名进行url编码时,使用了错误的方法。在代码中,使用了 urllib.parse.quote_plus 对签名 ...

才发现,你说的这个我之前就试过了,代码里面注释了,唉,难顶

isdkz 发表于 2023-10-26 23:49:38

如默 发表于 2023-10-26 22:22
唉,还是不行啊,这是代码:




import os
import time
import hmac
import hashlib
import base64
import urllib
from flask import Flask, request

app = Flask(__name__)

SECRET = 'mySecret'# 替换为您的签名密钥

def generate_signature(timestamp):
    timestamp_str = str(timestamp)
    secret_enc = SECRET.encode('utf-8')
    string_to_sign = '{}\n{}'.format(timestamp_str, SECRET)
    string_to_sign_enc = string_to_sign.encode('utf-8')
    hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
    signature = base64.b64encode(hmac_code).decode()
    return signature

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    # 验证请求来源是否是Gitee
    if request.headers.get('User-Agent') == 'git-oschina-hook':
      # 验证签名
      timestamp = int(request.headers.get('X-Gitee-Timestamp'))
      actual_signature = request.headers.get('X-Gitee-Token')
      expected_signature = generate_signature(timestamp)
      if actual_signature != expected_signature:
            return '', 403# 返回拒绝访问状态码

      # 在这里编写处理Webhook请求的代码逻辑
      data = request.json
      print('Received webhook request:', data)
      # 执行 git pull、pnpm install 和 pnpm build 等操作
      # ...

      return '', 200# 返回成功状态码
    else:
      return '', 403# 返回拒绝访问状态码

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=3002)

如默 发表于 2023-10-27 09:02:40

isdkz 发表于 2023-10-26 23:49


感谢,这个代码跑成功了,还是想请教一下,我看后台的日志,输出的内容还是和之前一样的,但这次就返回200,想请教一下为什么

isdkz 发表于 2023-10-27 09:10:56

如默 发表于 2023-10-27 09:02
感谢,这个代码跑成功了,还是想请教一下,我看后台的日志,输出的内容还是和之前一样的,但这次就返回20 ...

在请求头的内容没有url编码,所以我把url编码去掉了

如默 发表于 2023-10-27 10:52:53

isdkz 发表于 2023-10-27 09:10
在请求头的内容没有url编码,所以我把url编码去掉了

那这官方文档误人子弟啊,文档上写的要urlencode,结果他自己的请求头没有urlencode,导致我匹配不上。

isdkz 发表于 2023-10-27 10:56:48

如默 发表于 2023-10-27 10:52
那这官方文档误人子弟啊,文档上写的要urlencode,结果他自己的请求头没有urlencode,导致我匹配不上。

看你是从哪里取他传的签名密钥了,如果你是从 url 参数那里取的就要 url 编码,如果你是从请求头那里取出来的就不用

如默 发表于 2023-10-27 11:04:01

本帖最后由 如默 于 2023-10-27 11:07 编辑

isdkz 发表于 2023-10-27 10:56
看你是从哪里取他传的签名密钥了,如果你是从 url 参数那里取的就要 url 编码,如果你是从请求头那里取出 ...

gitee后台的日志啊,


她后台返回的这个数据,显然是请求头的,而我给她发的数据是拼在url里的,所以对不上,因为她的这个没有url编码,很无语。

isdkz 发表于 2023-10-27 11:09:23

如默 发表于 2023-10-27 11:04
gitee后台的日志啊,




他在请求头和url参数都传了签名的,flask的运行日志中 sign 参数就是他在 url 中传的签名

如默 发表于 2023-10-27 11:26:08

isdkz 发表于 2023-10-27 11:09
他在请求头和url参数都传了签名的,flask的运行日志中 sign 参数就是他在 url 中传的签名

那之前为什么不对呢,一直返回403,这不是代码校验的时候错误吗?{:10_266:}

如默 发表于 2023-12-12 16:45:28

剩下的回帖奖励呢,怎么没人要了

陈勃 发表于 2023-12-13 09:57:19

{:10_275:}{:10_275:}{:10_275:}

陈勃 发表于 2023-12-13 09:58:09

哇喔,{:5_106:}

陈勃 发表于 2023-12-13 09:58:17

{:10_257:}

陈勃 发表于 2024-1-9 09:27:00

育碧{:10_254:}

陈勃 发表于 2024-1-9 09:27:30

{:10_298:}{:10_298:}{:10_298:}
页: [1] 2 3
查看完整版本: 用Python实现Gitee的webhook出问题了