T09 · Insecure Skill Coding Practices
- Location
references/代码示例/Node.js/回调处理/回调处理.md:34- Finding
Payment callback signature verification is unconditionally bypassed
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This WeChat Pay helper is not deceptive, but it needs Review because its copy-paste payment callback and refund examples include unsafe financial-handling patterns.
Review carefully before installing or using. Treat the examples as reference material only, not production-ready code. Before deployment, replace callback verification with real WeChat Pay platform certificate/public-key verification over the raw request body, fail closed on verification or processing errors, use durable database-backed idempotency and order/amount/currency validation, protect merchant keys and APIv3 secrets, remove unsafe dependency commands, and test all payment/refund flows with controlled credentials.
references/代码示例/Node.js/回调处理/回调处理.md:34Payment callback signature verification is unconditionally bypassed
references/代码示例/Node.js/回调处理/回调处理.md:100Node.js callback verification reconstructs JSON instead of using the signed raw body
references/代码示例/Node.js/回调处理/回调处理.md:162Node.js callback processing failures are acknowledged as successful
references/代码示例/Python/回调处理/回调处理.md:239Python callback processing failures are acknowledged as successful
references/代码示例/Python/回调处理/回调处理.md:39Python callback verification cannot succeed because platform-key retrieval is unimplemented
references/代码示例/Node.js/回调处理/回调处理.md:107Payment callback examples use non-durable idempotency and omit mandatory order validation
references/代码示例/Node.js/JSAPI支付/JSAPI支付.md:7Dependency instructions install Node.js core-module names from the public npm registry
The primary sample claims to receive, verify, decrypt, and process WeChat Pay callbacks, but its verifySignature function always returns true. That means any attacker who can reach the callback endpoint can forge payment or refund notifications and potentially trigger business logic such as marking orders paid or issuing goods without a real WeChat-signed message.
The document's security guidance says signature verification is mandatory, but the main sample contradicts that guidance by not performing real verification. In a developer-focused payment integration skill, this inconsistency is dangerous because users are likely to copy the first complete example and assume it is safe, leading to insecure callback handling in production.
The exception path always returns a success response even when signature verification fails or business processing throws an error. In payment callbacks, acknowledging failed verification or failed processing can suppress legitimate retries, causing missed payments, inconsistent order state, and making forged or malformed notifications harder to detect and recover from.
The callback handler explicitly notes that signature verification is required, but the code proceeds to decrypt and process refund notifications without verifying the WeChat Pay signature or certificate serial. An attacker who can reach the callback endpoint could submit forged notifications that trigger false refund status updates, causing fraudulent state changes or financial reconciliation errors.
The skill advertises behaviors that require reading local reference files and using network access (for example via curl), but it does not declare any explicit tool scope such as permissions or allowed-tools. In an agent environment, this creates an authorization gap: the runtime or reviewer cannot clearly constrain what files may be read or what outbound requests are permitted, increasing the risk of unintended data exposure or misuse of network capabilities.
The document establishes a global rule at L044-L066 that all capabilities must first obtain explicit user answers and consent before continuing, especially before analysis or execution. L157 then directly contradicts that by stating Python/Node.js users can be given complete code examples '无需额外确认', which reverses the mandated confirmation flow for that capability.
The documentation shows HTTP requests to WeChat Pay and later posts openid, order details, and payment amounts from frontend to backend, but it does not disclose that user and transaction data will be transmitted to external services. For markdown files, user-facing warnings should mention network transmission of user data when privacy-relevant behavior is described.
This markdown file provides runnable sample code that loads a merchant private key from disk and shows API credential fields, but the surrounding documentation does not warn users that they must handle these secrets securely and avoid exposing real credentials in source control or logs. Because this is a markdown file, SQP-2 applies to missing warnings in the skill description about behaviors affecting privacy or system integrity.
Returning SUCCESS/200 on exceptions acknowledges the callback even when processing failed, preventing legitimate retries from WeChat. This can cause lost payment state transitions, unrecorded orders, inconsistent ledgers, or missed fulfillment recovery, especially when errors occur after decryption or during business processing.
The refund callback handler also returns SUCCESS when an exception occurs, which suppresses legitimate retries and can hide failed refund-state updates. That can leave refund records inconsistent with WeChat's state, causing accounting discrepancies, customer support issues, or incorrect refund fulfillment logic.
This markdown file includes front-end code that creates an order and calls wx.requestPayment, which can trigger a real financial transaction. The surrounding documentation explains how to perform the payment flow but does not include any warning to users or implementers about ensuring explicit user consent and verifying amounts before initiating payment.
This document presents runnable refund code without a prominent warning that it performs real refund operations against the live WeChat Pay API when configured with production credentials. In a payment integration skill, that omission increases the risk of accidental execution causing real financial transactions, especially because the examples look copy-paste ready.
The sample passes result.refundId into queryRefund, but queryRefund constructs the endpoint for querying by outRefundNo, not refund_id. This mismatch can cause status checks to fail or query the wrong identifier, leading developers to mis-handle refund state and potentially issue unnecessary retries or inconsistent business actions.
The route example contradicts the documented idempotency requirement by always generating a fresh outRefundNo from the order number and current timestamp. On retry after a timeout or ambiguous failure, this can submit a second distinct refund request instead of safely reusing the original refund identifier, creating duplicate or unintended refunds.
This markdown file embeds code that performs HTTP requests to an external payment endpoint and sends order details plus the user's openid. While the document explains how to use the API, it does not explicitly warn readers that running the sample will transmit payment-related and user-linked data to a third-party service.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
self.appid = appid
self.api_v3_key = api_v3_key
# 微信支付平台证书下载URL
self.platform_cert_url = "https://api.mch.weixin.qq.com/v3/certificates"
def _get_platform_public_key(self, serial_no):
"""
The code reads the callback amount and directly processes the order without comparing it to the locally stored order amount and currency. If an attacker can trigger processing for a valid but mismatched payment record, or if business logic errors occur, the system may mark orders as paid for the wrong amount, leading to financial loss or fulfillment of underpaid orders.
The example prints sensitive payment-related data including openid, session_key, order results, and client payment signature parameters without any warning against production logging. These values can leak through application logs, observability pipelines, or shared debugging output and may expose user identifiers, session material, and payment metadata that should be tightly controlled.
The entire skill documentation and embedded examples are presented only in Chinese, including headings, comments, and usage guidance, with no indication that the skill is region-specific or that users may choose another language. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"Content-Type": "application/json"
}
resp = requests.post(url, headers=headers, json=body)
return resp.json()
def refund(self, out_trade_no, out_refund_no, total_fee, refund_fee,
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"Content-Type": "application/json"
}
resp = requests.post(url, headers=headers, json=body)
return resp.json()
def refund(self, out_trade_no, out_refund_no, total_fee, refund_fee,
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"Content-Type": "application/json"
}
resp = requests.post(url, headers=headers, json=body)
return resp.json()
def refund(self, out_trade_no, out_refund_no, total_fee, refund_fee,
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"Content-Type": "application/json"
}
resp = requests.post(url, headers=headers, json=body)
return resp.json()
def refund(self, out_trade_no, out_refund_no, total_fee, refund_fee,
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"Content-Type": "application/json"
}
resp = requests.post(url, headers=headers, json=body)
return resp.json()
def refund(self, out_trade_no, out_refund_no, total_fee, refund_fee,
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
换openid接口:
GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=CODE&grant_type=authorization_code