前后端分离项目遇字符转义大坑! 特殊字符咋传递?

最近在做一个前后端分离的项目,前端用JS,后端用PHP。本来以为就是个普通的CRUD,结果在字符转义这块栽了个大跟头。今天就把这个坑分享出来,顺便聊聊怎么在JS和PHP之间优雅地传递特殊字符。

先说说我遇到的场景:前端用JS生成了一段包含特殊字符的JSON,要通过AJAX传给PHP处理。看起来很简单对?结果PHP那边收到的数据总是莫名其妙少几个字符,或者多几个反斜杠。就像你精心准备的约会对象,见面时发现完全不是同一个人。

JS的escape/unescape已经被废弃了,现在主流用encodeURIComponent/decodeURIComponent。但是注意了,这俩函数处理空格时会变成加号(+),而PHP的urldecode默认会把加号解码为空格。这就导致了一个经典问题:

var data = "hello world";

var encoded = encodeURIComponent(data); // 结果是"hello+world"

// PHP收到后用urldecode解码,完美还原

// 但是如果data里本来就有加号?

var data = "1+1=2";

var encoded = encodeURIComponent(data); // 结果是"1%2B1%3D2"

// PHP解码后得到"1+1=2",看起来没问题

// 但是如果前端用escape?

var escaped = escape(data); // 结果是"1+1%3D2"

// PHP用urldecode解码后得到"1+1=2",似乎也没问题

// 但是当字符串里有Unicode字符时...

var data = "中文";

var escaped = escape(data); // "%u4E2D%u6587"

// PHP的urldecode根本不认识%u开头的编码

看到没?这就是为什么escape被废弃了。现代JS应该统一用encodeURIComponent,然后在PHP端用urldecode处理。但是等等,这还没完...

PHP那边也有自己的转义函数,比如htmlspecialchars和addslashes。htmlspecialchars会把<、>、"等字符转成HTML实体,防止XSS攻击。addslashes则是在特定字符前加反斜杠,主要用于数据库查询。这两个函数经常被滥用,比如:

$input = $_POST['data'];

$safe_input = addslashes(htmlspecialchars($input));

// 然后直接拼接到SQL查询里

$sql = "INSERT INTO table VALUES ('$safe_input')";

// 你以为这样安全了?太天真!

htmlspecialchars应该在输出到HTML时使用,而不是在存储数据时。其次,addslashes根本不能防止SQL注入,应该用预处理语句。正确的做法是:

// 存储原始数据

$stmt = $pdo->prepare("INSERT INTO table VALUES (?)");

$stmt->execute([$input]);

// 输出时再转义

echo htmlspecialchars($input, ENT_QUOTES, 'UTF-8');

再来说说JSON。JS和PHP都有JSON解析器,但它们的实现细节有些差异。比如PHP的json_encode默认会把斜杠(/)转义成\/,而JS的JSON.parse能正确处理这种情况。但是如果你用字符串拼接的方式构造JSON,就可能出问题:

// JS端

var data = {

"message": ""

};

var json = JSON.stringify(data); // 正确结果是{"message":""}

// 但如果用字符串拼接

var badJson = '{"message":""}'; // 这会导致XSS漏洞

// 正确的做法是统一用JSON.stringify

PHP端也有类似问题:

$data = ["message" => ""];

$json = json_encode($data); // 正确结果{"message":"<\/script>"}

// 如果用字符串拼接

$badJson = '{"message":""}'; // 危险!

// 更糟的是有些开发者会这样做:

$badJson = "{'message':''}"; // 这不是合法的JSON

说到转义,不能不提正则表达式。JS和PHP的正则语法很像,但转义规则有差异。比如在PHP中匹配一个反斜杠需要写四个反斜杠:

// PHP

preg_match("/\\\\/", $string); // 匹配单个反斜杠

// 等价于JS的

/\\/.test(string); // 只需要两个反斜杠

这是因为PHP字符串本身需要转义,然后正则引擎又需要转义。这种多层转义经常把人搞晕。我的建议是:在PHP中用单引号字符串可以减少一层转义:

preg_match('/\\\/', $string); // 现在只需要三个反斜杠了

最后说说Base64编码,这经常被用来传递二进制数据。JS有btoa和atob,PHP有base64_encode和base64_decode。看起来很简单,但有个坑:JS的btoa不能直接处理Unicode字符串:

btoa(data); // 报错:字符串包含非Latin1字符

// 需要先转UTF-8

btoa(encodeURIComponent(data)); // 可行但结果很长

// 更好的方法是:

btoa(String.fromCharCode.apply(null,

new TextEncoder().encode(data))); // 需要现代浏览器支持

PHP那边相对简单:

$data = "中文";

$encoded = base64_encode($data); // 直接工作

// 但要注意解码时要确保原始编码一致

$decoded = base64_decode($encoded);

// 如果用在JSON中,记得去掉末尾的等号(=)

$forJson = rtrim($encoded, '=');

总结一下JS和PHP字符转义的最佳实践:

1. 前后端通信统一使用encodeURIComponent/decodeURIComponent

2. JSON处理永远用内置的JSON.stringify/parse和json_encode/json_decode

3. HTML转义只在输出时做,用htmlspecialchars

4. 数据库查询永远用预处理语句,不要手动转义

5. 正则表达式注意PHP的多层转义特性

6. Base64处理注意Unicode字符的问题

记住,转义就像穿衣服 - 脱的时候要和穿的时候顺序相反。如果你在某个环节多加了一层转义,后面就得记得去掉。最好的办法是尽量减少手动转义的次数,让标准库去做这些脏活。

最后的最后,如果你在凌晨三点调试字符转义问题,发现代码里全是反斜杠,眼睛已经花了...这时候最好的解决方案是:关掉IDE,去睡觉。明天早上用fresh eyes来看,问题往往一目了然。别问我怎么知道的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值