异或字符串:JS与PHP

4

我尝试在PHP和JS中对两个字符串进行异或运算,结果不同:

PHP函数

function xh($a, $b) {
  $res = ""; $i = strlen($a); $j = strlen($b);
  while($i-->0 && $j-->0) {
    $res.= $a[$i] ^ $b[$j];
  }
  return base64_encode($res);
}

JS函数

function xh(a, b) {
  var res = "", i = a.length, j = b.length;
  while (i-->0 && j-->0) {
    res+= String.fromCharCode(a.charCodeAt(i) ^ b.charCodeAt(j));
  }
  return btoa(res);
}

我检查了字节,并发现PHP函数中的第六个字节始终为零,因此我更新了JS函数以生成相同的结果。 对应于PHP函数的JS函数
function xh2(a, b) {
  var res = "", i = a.length, j = b.length;
  while (i-->0 && j-->0) {
    res+= String.fromCharCode((a.charCodeAt(i) ^ b.charCodeAt(j)) & 95);
  }
  return btoa(res);
}

那个位是发生了什么?

示例输入/输出:

string a: 5D41402ABC4B2A76B9719D911017C592
string b: FE2D010308A6B3799A3D9C728EE74244
PHP says: Bg0HVwBUVQkDDgcAVQRYWw8AUlBUVVtSUgIBBFUGAVM=
 JS says: Bg0HdwB0dQkDDgcAdQR4ew8AcnB0dXtycgIBBHUGAXM=
JS2 says: Bg0HVwBUVQkDDgcAVQRYWw8AUlBUVVtSUgIBBFUGAVM=

在这个例子中的第一个区别:

C: 0x43  = 0100 0011
4: 0x34  = 0011 0100
C^4 (JS) = 0111 0111 = 0x77 (correct)
C^4 (PHP)= 0101 0111 = 0x57
             ^
             sixth bit wrong

输入是MD5哈希值,使用默认编码,我的OEM字符集为CP1250,语言环境为cs-cz,文件以UTF-8编码存储,如果以下任何一项有影响,则页面通过HTTP头text/html;charset=UTF-8和meta标签UTF-8生成。
我的Web服务器是Mongoose 6.7,带有捆绑的php 5.6(cgi)。我还尝试了最新的7.3版(x86和x64),但结果相同,但是评论中的@apokryfos在测试中第六位是正确的。

3
这是什么黑魔法? - Dominic
2
@Dominic (i--)>0 - Jan Turoň
2
它的意思是 (i--)> 0 - LF00
@GordonM 我添加了一些关于我的系统的额外信息。 - Jan Turoň
1
首先尝试直接将字符串作为字面量传递,而不涉及Mongoose,看看是否有帮助。 - apokryfos
显示剩余7条评论
2个回答

1
问题的根源在于大小写敏感性:似乎一些有缺陷的MD5实现没有将md5输出转换为小写。客户端和服务器端使用了两个不同的库。
'A' starts at 0x41 = 0100 0001
'a' starts at 0x61 = 0110 0001
                       ^
                       here is the sixth bit

0

对于JS,请使用缓冲区或类型化数组,而不是字符串。否则,您需要一些二进制安全的字符串编码。

在PHP中,您可以完整地对两个字符串进行异或运算:$a ^ $b(不要忘记长度检查)。

请参见:https://developer.mozilla.org/en-US/docs/Web/API/DOMString/Binary

我使用您的代码从PHP得到了Bg0HdwB0dQkDDgcAdQR4ew8AcnB0dXtycgIBBHUGAXM=,因此可能发生了其他事情。

您能提供PHP版本和构建/源吗?


在问题中查看更新的示例,这没有任何区别。 - Jan Turoň
我确定PHP不能像你这样在字符串上使用XOR。chr(ord($a[$i]) ^ ord($b[$j]));应该更接近JS的输出。但是JS的输出也会有所不同,因为PHP字符串是字节数组,而JS字符串是Unicode字符数组。你不能像在PHP中那样安全地使用JS字符串进行二进制操作。但是,你可以使用mbstring或类似的工具使PHP与JS相同。你可以使用字节数组使JS与PHP匹配。 - jgmjgm
话说,在 PHP 中似乎可以对字符串字节执行异或操作,这让我想知道是否可以对整个字符串执行异或操作而不是循环? - jgmjgm
可能需要 & 255? - jgmjgm
请查看最新的编辑。这个问题比它看起来的更奇怪。 - jgmjgm
感谢您的努力,问题的根源在其他地方,详见我的回答。我的错,抱歉 - 我使用了非常相似的大小写字体。 - Jan Turoň

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接