在PHP中散列JSON不会产生与Javascript中的unicode字符相同的结果

时间:2010-10-04 13:52:28

标签: php javascript json unicode

我的Web应用程序通过JSON协议与服务器通信。在从Web应用程序发送每个JSON消息之前,我在其上运行hmac-sha1函数(在已编码的对象上)并将生成的HMAC插入到JSON请求的标头中。

在服务器端,我使用PHP解码JSON消息,从对象中提取HMAC,取消设置HMAC,然后将对象编码回JSON并创建它的HMAC。

只要我不使用“ž,š,č”这样的字符,HMAC就会匹配。当我在消息中使用这些字符时,HMAC不再匹配。

在Web应用程序中,我使用jQuery.post()来传输已编码的JSON字符串。

如果我通过JSON编码的回复将我从Web应用程序获得的数据发回给它,应用程序将很好地显示“ž,č,š”。

如何让HMAC匹配?

更新: 这只是最新版Firefox和Opera的问题。它适用于IE8和Chrome。在以前的浏览器中,JSON字符串(在发送之前)是:

{"body":[{"name":"Žiga Kraljevič","email":"test@email.com","password":"secretpass"}],"header":{"apiID":"person-27jhfa83ha-js84sjj18dasjd","hmac":"e4259d6ef8f477c020d644409cc16dd9c42301e8"}}

虽然在后面的浏览器(IE8和Chrome,它的工作原理)如下:

{"body":[{"name":"\u017diga Kraljevi\u010d","email":"test@email.com","password":"secretpass"}],"header":{"apiID":"person-27jhfa83ha-js84sjj18dasjd","hmac":"e4e9e2d0d8d11728a2b4329ad6dacdb9409b1de1"}}

2 个答案:

答案 0 :(得分:2)

你可能遇到了多个问题。其中一个可能是客户端上使用的字符编码与服务器上使用的字符编码不同,值得确保它们是相同的(更多关于Joel's excellent essay中的字符编码)。另一种可能是有多种正确的方式来编码事物。编码器可能使用不同的方式。例如,您可以将字符串中的"编码为\"\u0022。两者都是有效的,它们是等价的,但是哈希值不匹配。同样地,我有点惊讶你在不使用重音字符时没有遇到麻烦,例如用空格。

答案 1 :(得分:1)

你的hmac-sha1功能是什么,它来自哪里?如果它以JSON String作为输入,那么这里有一个隐含的编码到字节步骤,因为SHA1操作字节,而不是像JS String那样的UTF-16代码单元。

我怀疑你的JS函数正在使用“每个字节n的一个代码单元n”编码类型,以便使用getCharCodeAt等工具轻松计算。这实际上与将字符串输入编码为ISO-8859-1的情况相同。如果您使用encodeURIComponent或通过XMLHttpRequest发布原始字符,则隐式编码为UTF-8。

您可以将JS {hmac-sha1函数的String转换为UTF-8字节存储为代码单元格式,这可能使其与PHP匹配。有一个偷偷摸摸的习惯用法:

var utf8= unescape(encodeURIComponent(s));
  

当POST JSON我base64并且urlencode它无论如何

网址编码应该足够了(使用encodeURIComponent,而不是escape这对于绝对所有是错误的,除了 UTF-8转换技巧的反向步骤上文)。

顺便说一句,这是什么目的?你知道它不会以任何方式保护浏览器和服务器之间的连接,是吗?

编辑:

  

我正在使用jssha.sourceforge.net作为sha1-hmac。在PHP中我使用hash_hmac。

适合我:

var data= '\u017E, \u010D, \u0161'; // 'ž, č, š' in a Unucode string
var utf8bytes= unescape(encodeURIComponent(data));
var hmac= new jsSHA(utf8bytes).getHMAC('foo', 'ASCII', 'SHA-1', 'HEX');
alert(hmac); // 5d15f0b9...
var form= 'message='+encodeURIComponent(data)+'&hmac='+encodeURIComponent(hmac);
xmlhttprequest.send(form);

...

$utf8bytes= $_POST['message']; // "\xc5\xbe, \xc4\x8d, \xc5\xa1"
                               // which is 'ž, č, š' as UTF-8 in byte string
$hmac= hash_hmac('sha1', $utf8bytes, 'foo');
echo $hmac; // 5d15f0b9...
echo strtolower($hmac)===strtolower($_POST['hmac']); // true

这使用二进制('ASCII'到jsSHA)键foo。如果您使用的是包含非ASCII字符的二进制密钥,则必须确保那些也正确编码,与数据的方式相同。

  

HMAC的密钥是服务器和客户端之间的共享密钥,之前已通过安全连接进行交换。

这不仅是您必须通过安全连接发送的密钥,而是整个页面及其中的所有脚本。否则,中间攻击的人可能会在前往浏览器的路上破坏您的脚本,以使用使用密钥签署虚假消息的版本替换它们。如果你有所有这些东西的HTTPS服务器,那很好。我不确定HMAC在这种情况下会做什么,但似乎有点涉及反XSRF方案。