{"record":{"id":"086b746c4b96b27f","repo":"yikart/AiToEarn","slug":"1-086b74","errorCode":"-1","errorMessage":"ChannelWebhookInvalidSignature","messagePattern":"ChannelWebhookInvalidSignature","errorType":"http","errorClass":null,"httpStatus":401,"severity":"warning","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/bilibili/bilibili-webhook.provider.ts","lineNumber":41,"sourceCode":"} from './bilibili.interface'\n\ntype PersistedPublishDataOption = PublishRecord['dataOption']\n\n@Injectable()\nexport class BilibiliWebhookProvider implements PlatformWebhookHandler {\n  private readonly logger = new Logger(BilibiliWebhookProvider.name)\n\n  constructor(\n    private readonly config: BilibiliConfig,\n    @Optional() private readonly publishRecordRepo?: PublishRecordRepository,\n    @Optional() private readonly stateService?: PublishStateService,\n  ) {}\n\n  async handle(request: Request, response: Response): Promise<void> {\n    const verified = this.verify(request)\n    this.logger.log({ platform: AccountType.Bilibili, verified }, 'Bilibili webhook received')\n    if (!verified) {\n      response.status(401).json({ code: -1, message: getCodeMessage(ResponseCode.ChannelWebhookInvalidSignature, undefined, getLocale()) })\n      return\n    }\n\n    const body = this.parseBilibiliBody(request.body)\n    if (!body) {\n      response.status(200).json({ code: 0, message: 'ok' })\n      return\n    }\n    if (body.event === BilibiliWebhookEvent.VerifyWebhooks) {\n      response.status(200).json({ data: body.content.data })\n      return\n    }\n\n    await this.applyBilibiliPublishResult(body)\n    response.status(200).json({ code: 0, message: 'ok' })\n  }\n\n  private verify(request: RawBodyRequest): boolean {","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/bilibili/bilibili-webhook.provider.ts#L23-L59","documentation":"The Bilibili webhook provider rejects incoming webhook HTTP requests whose signature verification fails by responding 401 with code -1 and message ChannelWebhookInvalidSignature. The provider's verify(request) checked the request signature against the configured Bilibili webhook secret and it did not match, so the payload is not processed.","triggerScenarios":"POST to the Bilibili webhook endpoint where the signature header is missing, computed with a different secret than the server's config, or the raw body was re-serialized (altering bytes) before HMAC verification.","commonSituations":"Webhook secret rotated on Bilibili's side but not in server env config; a proxy/gateway re-encoding the JSON body so the signed bytes differ; misconfigured per-environment secret (cn vs intl); replayed or forged requests.","solutions":["Confirm the webhook secret configured on the server matches the one registered with Bilibili","Ensure signature verification reads the exact raw request body (no proxy re-serialization); configure the proxy to pass the body untouched","Check that the correct environment's secret/env var is loaded (aitoearn.cn vs aitoearn.ai)","Re-register the webhook URL/secret on the Bilibili side if it was rotated"],"exampleFix":"// before\n// secret loaded from generic env\nthis.secret = process.env.WEBHOOK_SECRET\n// after\n// platform/env specific secret with startup validation\nthis.secret = process.env.BILIBILI_WEBHOOK_SECRET\nif (!this.secret)\n  throw new Error('BILIBILI_WEBHOOK_SECRET is required')","handlingStrategy":"validation","validationCode":"const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex')\nif (req.headers['x-signature'] !== expected) return res.status(401).json({ code: -1 })","typeGuard":"function hasValidSignature(header: string | undefined, raw: Buffer, secret: string): boolean {\n  if (!header) return false\n  return crypto.timingSafeEqual(Buffer.from(header), crypto.createHmac('sha256', secret).update(raw).digest())\n}","tryCatchPattern":"app.post('/webhooks/bilibili', express.raw({ type: 'application/json' }), (req, res) => {\n  if (!hasValidSignature(req.headers['x-signature'], req.body, secret)) {\n    return res.status(401).json({ code: -1, message: 'ChannelWebhookInvalidSignature' })\n  }\n  // process payload\n})","preventionTips":["Store the Bilibili webhook secret in env per environment and validate at startup","Configure proxies to pass the raw body byte-for-byte to the webhook route","Rotate the secret on both Bilibili and server config simultaneously","Log signature presence (not value) on failures for diagnostics"],"tags":["webhook","signature","security","bilibili"],"backgroundTag":"webhook-signature-verification-failed","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}