{"record":{"id":"1caaa5b85678c0b0","repo":"JeffreySu/WeiXinMPSDK","slug":"encrypt","errorCode":null,"errorMessage":"开放硬件回调正文必须包含非空 encrypt 字段。","messagePattern":"开放硬件回调正文必须包含非空 encrypt 字段。","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"warning","filePath":"src/Senparc.Weixin.Work/Senparc.Weixin.Work/AdvancedAPIs/OpenHardware/OpenHardwareCallbackHandler.cs","lineNumber":59,"sourceCode":"        /// <exception cref=\"OpenHardwareCallbackCryptException\">验签、解密或接收方校验失败时抛出。</exception>\n        public static OpenHardwareCallbackParseResult DecryptAndParse(\n            string token, string encodingAesKey, string receiveId,\n            string msgSignature, string timestamp, string nonce,\n            string encryptedBody)\n        {\n            EnsureNotEmpty(token, nameof(token));\n            EnsureNotEmpty(encodingAesKey, nameof(encodingAesKey));\n            EnsureNotEmpty(receiveId, nameof(receiveId));\n            EnsureNotEmpty(msgSignature, nameof(msgSignature));\n            EnsureNotEmpty(timestamp, nameof(timestamp));\n            EnsureNotEmpty(nonce, nameof(nonce));\n            EnsureNotEmpty(encryptedBody, nameof(encryptedBody));\n\n            var envelope = JsonConvert\n                .DeserializeObject<OpenHardwareEncryptedCallbackRequest>(encryptedBody);\n            if (envelope == null || string.IsNullOrWhiteSpace(envelope.encrypt))\n            {\n                throw new ArgumentException(\n                    \"开放硬件回调正文必须包含非空 encrypt 字段。\",\n                    nameof(encryptedBody));\n            }\n\n            var plaintext = string.Empty;\n            var crypt = new WXBizMsgCrypt(token, encodingAesKey, receiveId);\n            var errorCode = crypt.DecryptJsonMsg(msgSignature, timestamp, nonce,\n                envelope.encrypt, ref plaintext);\n            if (errorCode != 0)\n            {\n                throw new OpenHardwareCallbackCryptException(errorCode);\n            }\n\n            return new OpenHardwareCallbackParseResult\n            {\n                tousername = envelope.tousername,\n                plaintext = plaintext,\n                message = ParsePlaintext(plaintext)","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/JeffreySu/WeiXinMPSDK/blob/be573f6f94bdbf718dd5f6cdecb137fbc7ff651e/src/Senparc.Weixin.Work/Senparc.Weixin.Work/AdvancedAPIs/OpenHardware/OpenHardwareCallbackHandler.cs#L41-L77","documentation":"OpenHardwareCallbackHandler.DecryptAndParse deserializes the callback body into OpenHardwareEncryptedCallbackRequest and requires a non-empty encrypt field, which holds the AES-encrypted payload. If the JSON body is null, unparseable, or lacks/empties 'encrypt', it throws ArgumentException so callers know the callback is malformed and cannot be decrypted.","triggerScenarios":"WeChat Work posts a callback whose body is not the expected {\"encrypt\":\"...\"} envelope — e.g. an empty GET-verification probe forwarded to this handler, an HTML error page body, or an attacker/fuzzer POSTing arbitrary JSON.","commonSituations":"Misconfigured callback URL receiving requests meant for another endpoint; load balancer error pages being passed through; verifying the URL handshake with a plaintext echo and mistakenly calling DecryptAndParse on it.","solutions":["Inspect the raw body: if it is the URL-verification handshake (GET with msg_signature/echostr), handle it via the verify path instead of DecryptAndParse.","Guard with a quick check before calling: body contains a non-empty \"encrypt\" field.","Return 400 for bodies without 'encrypt' so WeChat Work's console shows the misconfiguration.","Confirm the callback URL configured in the WeChat Work admin console points to the correct handler."],"exampleFix":"// before\nvar result = handler.DecryptAndParse(body, token, aesKey, receiveId); // throws if encrypt missing\n// after\nif (string.IsNullOrWhiteSpace(body)) return Results.BadRequest(\"empty body\");\nusing var doc = JsonDocument.Parse(body);\nif (!doc.RootElement.TryGetProperty(\"encrypt\", out var enc) || string.IsNullOrWhiteSpace(enc.GetString()))\n    return Results.BadRequest(\"missing encrypt field\");\nvar result = handler.DecryptAndParse(body, token, aesKey, receiveId);","handlingStrategy":"validation","validationCode":"using var doc = JsonDocument.Parse(body);\nbool hasEncrypt = doc.RootElement.ValueKind == JsonValueKind.Object\n    && doc.RootElement.TryGetProperty(\"encrypt\", out var e)\n    && !string.IsNullOrWhiteSpace(e.GetString());\nif (!hasEncrypt) return Results.BadRequest(\"missing encrypt field\");","typeGuard":"static bool TryGetEncrypt(string body, out string encrypt)\n{\n    encrypt = null;\n    try { var o = JsonConvert.DeserializeObject<OpenHardwareEncryptedCallbackRequest>(body); encrypt = o?.encrypt; }\n    catch (JsonException) { }\n    return !string.IsNullOrWhiteSpace(encrypt);\n}","tryCatchPattern":"try { var result = handler.DecryptAndParse(body, token, aesKey, receiveId); }\ncatch (ArgumentException ex) { logger.LogWarning(ex, \"Malformed open-hardware callback\"); return Results.BadRequest(); }","preventionTips":["Route GET URL-verification handshakes to a separate handler.","Reject empty/non-JSON bodies early with 400.","Verify the callback URL configured in the WeChat Work console matches this endpoint."],"tags":["callback","validation","open-hardware"],"backgroundTag":"empty-required-field","analyzedSha":"be573f6f94bdbf718dd5f6cdecb137fbc7ff651e","analyzedAt":"2026-09-12T10:01:50.733Z","contentChangedAt":"2026-09-12T10:01:50.733Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}