Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
forked fromgo-gitea/gitea

Commit415c7d1

Browse files
SnowballXueQiuhiifongwxiaoguang
authored andcommitted
Fix Feishu webhook signature verification (go-gitea#34788)
# Fix Feishu Webhook Signature VerificationThis PR implements proper signature verification for Feishu (Lark)webhooks according to the [officialdocumentation](https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot).## Changes- Implemented the `GenSign` function based on Feishu's official Gosample code- Modified the webhook request creation to include timestamp andsignature in the payload when a secret is configured- Fixed the signature generation algorithm to properly use HMAC-SHA256with the correct string format## Implementation DetailsThe signature verification works as follows:1. When a webhook secret is provided, a timestamp is generated2. The signature string is created using `timestamp + "\n" + secret`3. The HMAC-SHA256 algorithm is applied to an empty string using thesignature string as the key4. The result is Base64 encoded to produce the final signature5. Both timestamp and signature are added to the payloadAccording to Feishu's documentation, the timestamp must be within 1 hour(3600 seconds) of the current time to be considered valid.## Security NoteFeishu emphasizes the importance of keeping webhook URLs secure. Do notdisclose them on GitHub, blogs, or any public sites to preventunauthorized use.## References- [Feishu Custom BotDocumentation](https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot)---------Co-authored-by: hiifong <i@hiif.ong>Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
1 parentacd4e10 commit415c7d1

File tree

3 files changed

+39
-6
lines changed

3 files changed

+39
-6
lines changed

‎services/webhook/feishu.go‎

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ package webhook
55

66
import (
77
"context"
8+
"crypto/hmac"
9+
"crypto/sha256"
10+
"encoding/base64"
811
"fmt"
912
"net/http"
1013
"strings"
14+
"time"
1115

1216
webhook_model"code.gitea.io/gitea/models/webhook"
1317
"code.gitea.io/gitea/modules/git"
@@ -16,10 +20,12 @@ import (
1620
)
1721

1822
type (
19-
// FeishuPayload represents
23+
// FeishuPayload represents the payload for Feishu webhook
2024
FeishuPayloadstruct {
21-
MsgTypestring`json:"msg_type"`// text / post / image / share_chat / interactive / file /audio / media
22-
Contentstruct {
25+
Timestampint64`json:"timestamp,omitempty"`// Unix timestamp for signature verification
26+
Signstring`json:"sign,omitempty"`// Signature for verification
27+
MsgTypestring`json:"msg_type"`// text / post / image / share_chat / interactive / file /audio / media
28+
Contentstruct {
2329
Textstring`json:"text"`
2430
}`json:"content"`
2531
}
@@ -178,9 +184,29 @@ func (feishuConvertor) WorkflowJob(p *api.WorkflowJobPayload) (FeishuPayload, er
178184
returnnewFeishuTextPayload(text),nil
179185
}
180186

187+
// feishuGenSign generates a signature for Feishu webhook
188+
// https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot
189+
funcfeishuGenSign(secretstring,timestampint64)string {
190+
// key="{timestamp}\n{secret}", then hmac-sha256, then base64 encode
191+
stringToSign:=fmt.Sprintf("%d\n%s",timestamp,secret)
192+
h:=hmac.New(sha256.New, []byte(stringToSign))
193+
returnbase64.StdEncoding.EncodeToString(h.Sum(nil))
194+
}
195+
181196
funcnewFeishuRequest(_ context.Context,w*webhook_model.Webhook,t*webhook_model.HookTask) (*http.Request, []byte,error) {
182-
varpcpayloadConvertor[FeishuPayload]=feishuConvertor{}
183-
returnnewJSONRequest(pc,w,t,true)
197+
payload,err:=newPayload(feishuConvertor{}, []byte(t.PayloadContent),t.EventType)
198+
iferr!=nil {
199+
returnnil,nil,err
200+
}
201+
202+
// Add timestamp and signature if secret is provided
203+
ifw.Secret!="" {
204+
timestamp:=time.Now().Unix()
205+
payload.Timestamp=timestamp
206+
payload.Sign=feishuGenSign(w.Secret,timestamp)
207+
}
208+
209+
returnprepareJSONRequest(payload,w,t,false/* no default headers */)
184210
}
185211

186212
funcinit() {

‎services/webhook/feishu_test.go‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ func TestFeishuJSONPayload(t *testing.T) {
168168
URL:"https://feishu.example.com/",
169169
Meta:`{}`,
170170
HTTPMethod:"POST",
171+
Secret:"secret",
171172
}
172173
task:=&webhook_model.HookTask{
173174
HookID:hook.ID,
@@ -183,10 +184,13 @@ func TestFeishuJSONPayload(t *testing.T) {
183184

184185
assert.Equal(t,"POST",req.Method)
185186
assert.Equal(t,"https://feishu.example.com/",req.URL.String())
186-
assert.Equal(t,"sha256=",req.Header.Get("X-Hub-Signature-256"))
187187
assert.Equal(t,"application/json",req.Header.Get("Content-Type"))
188188
varbodyFeishuPayload
189189
err=json.NewDecoder(req.Body).Decode(&body)
190190
assert.NoError(t,err)
191191
assert.Equal(t,"[test/repo:test]\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1",body.Content.Text)
192+
assert.Equal(t,feishuGenSign(hook.Secret,body.Timestamp),body.Sign)
193+
194+
// a separate sign test, the result is generated by official python code, so the algo must be correct
195+
assert.Equal(t,"rWZ84lcag1x9aBFhn1gtV4ZN+4gme3pilfQNMk86vKg=",feishuGenSign("a",1))
192196
}

‎services/webhook/payloader.go‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,10 @@ func newJSONRequest[T any](pc payloadConvertor[T], w *webhook_model.Webhook, t *
9292
iferr!=nil {
9393
returnnil,nil,err
9494
}
95+
returnprepareJSONRequest(payload,w,t,withDefaultHeaders)
96+
}
9597

98+
funcprepareJSONRequest[Tany](payloadT,w*webhook_model.Webhook,t*webhook_model.HookTask,withDefaultHeadersbool) (*http.Request, []byte,error) {
9699
body,err:=json.MarshalIndent(payload,""," ")
97100
iferr!=nil {
98101
returnnil,nil,err

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp