WeChall - Training - RegexMini
Challenge
一个很短的 PHP/PCRE 正则训练。页面要求提交一个 username:它必须通过正则检查,但长度又要超过正则允许的范围。
核心逻辑可以简化成:
1 | if (!preg_match('/^[a-zA-Z]{1,16}$/', $username)) { |
看起来是矛盾的:正则只允许 1 到 16 个英文字母,后面却要求
strlen($username) > 16。
Solution
关键在 PCRE 里 $ 的语义。
在默认模式下,$
不只匹配字符串真正结尾,也可以匹配「字符串末尾换行符之前的位置」。也就是说:
1 | aaaaaaaaaaaaaaaa\n |
这串内容有 16 个 a,后面跟一个换行符。
正则 /^[a-zA-Z]{1,16}$/ 匹配时:
^[a-zA-Z]{1,16}吃掉 16 个a。$可以停在最后的\n前面。- 因此
preg_match()判定通过。
但 PHP 的 strlen() 会真实计算字节数,换行也算 1
字节:
1 | 16 个 a + 1 个 \n = 17 |
于是同一个输入同时满足:
1 | preg_match('/^[a-zA-Z]{1,16}$/', $username) === 1 |
用 curl 提交时要保留末尾的 literal newline,推荐交给
--data-urlencode:
1 | $ curl -sL \ |