我正在尝试使用PHP验证Microsoft Teams自定义Bot,遵循Microsoft instructions并阅读de C#示例代码。
Microsoft Intructions步骤:
1.从消息的请求主体生成hmac。大多数平台上都有标准库。 Microsoft Teams使用标准 SHA256 HMAC密码学。您需要将正文转换为一个字节 UTF8中的数组 2.要计算哈希值,请提供共享密钥的字节数组 3.使用UTF8编码将哈希值转换为字符串 4.将生成的哈希的字符串值与HTTP请求中提供的值进行比较。
我写了一个小的PHP脚本来测试本地:
<?php
//Function to generate C# byte[] equivalent
function unpak_str($val){
$b = unpack('C*', $val);
foreach ($b as $key => $value)
$byte_a .= $value;
return $byte_a;
}
//multi test outputs
function hasher($values=[], &$output){
//my secret share
$secret="ejWiKHgsKY1ZfpJwJ+wIiN4+bgsFad/lkpu9/MWNXgM=";
//diferent test
$secret_64=base64_decode($secret);
$secret_b=unpak_str($secret);
$secret_b_64=unpak_str(base64_decode($secret));
foreach($values as $msg){
$hs = hash_hmac("sha256",$msg,$secret, true);
$hs_64 = hash_hmac("sha256",$msg,$secret_64, true);
$hs_b = hash_hmac("sha256",$msg,$secret_b, true);
$hs_b_64 = hash_hmac("sha256",$msg,$secret_b_64, true);
$output.=base64_encode($hs)." <BR>";
$output.=base64_encode($hs_64)." <BR>";
$output.=base64_encode($hs_b)." <BR>";
$output.=base64_encode($hs_b_64)." <BR>";
}
}
//Get data
$data=file_get_contents('php://input');
//real data request content for test
$data ='{type":"message","id":"1512376018086","timestamp":"2017-12-04T08:26:58.237Z","localTimestamp":"2017-12-04T09:26:58.237+01:00","serviceUrl":"https://smba.trafficmanager.net/emea-client-ss.msg/","channelId":"msteams","from":{"id":"29:1aq6GCrC6lM9dv3YkAYi1gxTPiLnojGFgVr0_Th-2x6DhqmHAOhFwQHFzSyDy5RruXY4_FZjJebKHU7bpxfHpXA","name":"ROBERTO ALONSO FERNANDEZ","aadObjectId":"1e0dc7a0-9d5e-488b-bcf2-7e39c84076b8"},"conversation":{"isGroup":true,"id":"19:9e1c52275dfb4d0b873ddf34eb9f4979@thread.skype;messageid=1512376018086","name":null},"recipient":null,"textFormat":"plain","attachmentLayout":null,"membersAdded":[],"membersRemoved":[],"topicName":null,"historyDisclosed":null,"locale":null,"text":"<at>PandoBot</at> fff","speak":null,"inputHint":null,"summary":null,"suggestedActions":null,"attachments":[{"contentType":"text/html","contentUrl":null,"content":"<div><span itemscope=\"\" itemtype=\"http://schema.skype.com/Mention\" itemid=\"0\">PandoBot</span> fff</div>","name":null,"thumbnailUrl":null}],"entities":[{"type":"clientInfo","locale":"es-ES","country":"ES","platform":"iOS"}],"channelData":{"teamsChannelId":"19:9e1c52275dfb4d0b873ddf34eb9f4979@thread.skype","teamsTeamId":"19:1e04f564ce5e4596bf2f266dbcff439e@thread.skype","channel":{"id":"19:9e1c52275dfb4d0b873ddf34eb9f4979@thread.skype"},"team":{"id":"19:1e04f564ce5e4596bf2f266dbcff439e@thread.skype"},"tenant":{"id":"9744600e-3e04-492e-baa1-25ec245c6f10"}},"action":null,"replyToId":null,"value":null,"name":null,"relatesTo":null,"code":null}';
//generate HMAC hash with diferent $data formats
$test = [$data, unpak_str($data), base64_encode($data), unpak_str(base64_encode($data))];
hasher($test, $output);
//microsoft provided HMAC
$output.="<HR>EW2993goL1q7nGhytIb3jKmV6luXLz15Bq2aYwuCeiE=";
echo $output;
/*
Calculates:
0HsKoHza/QBvdz+nZw9tOti/eSWjyMMt/U77bfDqiE8=
3jSq3I0HNQkjB9QfnnsxC1c3pF5PjqweHlSVcicrShY=
bTQcGVTHX8/Gh4xovnN0WiJUiNaOQwvUZnwyFfiCaJE=
qHBT2Y2ITyoxz2gmBbG8P1CrClvETus6dTffET3bAR8=
8BcrXEQDDi77qgxCZLYyb/6ez8p9Qg2ZhTyZPWkdn/g=
+8RSU5SSJKxqRLKkI+NkTE01xwu6PwPkKKMuvyyUvlo=
PdL5ZpEwcN6Fe5kfX7zeAZLJvt0uLNTzu7lhuoOcr2o=
s6M5pYruEgWeNMEOFfQRjVKQqtPBVaW3TJb2MzObF2c=
xOTLhddbAwczQVneuTDQhPzmoIXGQljpf27c+hlhQII=
aUMm5b2sKfmwGZOglfiu228fWqoLlwjc7z1QRdIbakE=
5a7bAj9tzqhP9l85OvfVasURW0GSV5rykRutFFPO2fk=
kwg6P2LoDL9rc3SSwJxQeoYJzZYlh+FHFefe38UokBM=
eHeAzI7TV6vYDzxTxwyKWxMeVKFiFlIffWRiIMAk6fk=
ZCyj2UppacQOTXogLPMFLDeMArQg03rhhlIwhynDvng=
uQYK+7u9fppb62zXqtVYfkNK9wVawB3g+BlTyu4dc74=
vjOFA3fqpwUx/VO9dQv3XviNhpjTNQsUwaJIwH4JjdY=
------------ MS PROVIDED HMAC ---------------
EW2993goL1q7nGhytIb3jKmV6luXLz15Bq2aYwuCeiE=
*/
我没有哈希匹配...
答案 0 :(得分:1)
我不是PHP专家,你覆盖所有案例的逻辑有点复杂,但我很确定你的问题是你在计算之前没有从UTF8转换消息($ data) HMAC。
这是Node中一个简单的自定义echo bot,它显示了如何计算和验证HMAC:
const util = require('util');
const crypto = require('crypto');
const sharedSecret = "+ZaRRMC8+mpnfGaGsBOmkIFt98bttL5YQRq3p2tXgcE=";
const bufSecret = Buffer(sharedSecret, "base64");
var http = require('http');
var PORT = process.env.port || process.env.PORT || 8080;
http.createServer(function(request, response) {
var payload = '';
request.on('data', function (data) {
// console.log("Chunk size: %s bytes", data.length)
payload += data;
});
request.on('end', function() {
try {
// Retrieve authorization HMAC information
var auth = this.headers['authorization'];
// Calculate HMAC on the message we've received using the shared secret
var msgBuf = Buffer.from(payload, 'utf8');
var msgHash = "HMAC " + crypto.createHmac('sha256', bufSecret).update(msgBuf).digest("base64");
console.log("Computed HMAC: " + msgHash);
console.log("Received HMAC: " + auth);
response.writeHead(200);
if (msgHash === auth) {
var receivedMsg = JSON.parse(payload);
var responseMsg = '{ "type": "message", "text": "You typed: ' + receivedMsg.text + '" }';
} else {
var responseMsg = '{ "type": "message", "text": "Error: message sender cannot be authenticated." }';
}
response.write(responseMsg);
response.end();
}
catch (err) {
response.writeHead(400);
return response.end("Error: " + err + "\n" + err.stack);
}
});
}).listen(PORT);
console.log('Listening on port %s', PORT);
答案 1 :(得分:1)
经过大量的试验,它让我疯狂,并决定用一个新秘密开始一个新的机器人。现在工作正常。我是人类,而MS团队没有...我认为这是我的复制/粘贴的错,但是这是一个非常奇怪的事情,而另一方面老机器人失败了很多次没有回应和最新的没有
PHP for Microsoft Teams Custom Bot的完整示例验证HMAC:
<?php
//The secret share with Microsoft Teams
$secret="jond3021g9imMkrt8txF5AVPIwPFouNV/I72cQFii18=";
//get headers
$a = getallheaders();
$provided_hmac=substr($a['Authorization'],5);
//Get data from request
$data=file_get_contents('php://input');
//json decode into array
$json=json_decode($data, true);
//hashing
$hash = hash_hmac("sha256",$data,base64_decode($secret), true);
$calculated_hmac = base64_encode($hash);
//start log var
$log = "\n========".date("Y-m-d H:i:s")."========\n".$provided_hmac."\n".$calculated_hmac."\n";
try{
//compare hashs
if(!hash_equals($provided_hmac,$calculated_hmac))
throw new Exception("No hash matching");
//response text
$txt="Hi {$json["from"]["name"]} welcome to your custom bot";
echo '{
"type": "message",
"text": "'.$txt.'"
}';
$log .= "Sended: {$txt}";
}catch (Exception $e){
$log .= $e->getMessage();
}
//write log
$fp = fopen("log.txt","a");
fwrite($fp, $log . PHP_EOL);
fclose($fp);
答案 2 :(得分:0)
您不需要unpack()
或unpak_str()
函数(它也被破坏,因为它只是用下一个字节覆盖每个字节,而不是附加它们)。
字节数组不是PHP中的东西 - 该语言没有不同的字符串类型;如何解释字符串完全取决于使用它们的函数。也就是说,您的共享秘密应该只是base64_encode($secret)
的结果。