• 记第一次辛苦卓绝的lua代码经验

    背景

    Web服务器Nginx采用了开源的waf项目方案强化安全性能,但是在使用过程遇到了这样一个问题,waf自带的白名单功能只支持精确的IP地址却不支持IP地址段:

    如下,是白名单的功能函数,我们看到只有精确的等于匹配,并没有IP地址段的匹配功能:

    这样对于运维的维护来说是一个不小的成本,每次内网IP段内的web服务器IP地址有变更,都得更新这个白名单,于是决定加个简单的功能函数,给此函数添加IP地址段的识别功能。

    过程

    说干就干,但是前提还需要交待清楚:

    • 这是本人第一次接触lua代码
    • 此功能必须具有较高的效率值,因为web服务器的访问每次都会经过这个判断
    • 此功能必须精确,如果判断失效,将会导致web服务器误拦截,后果严重

    虽然是第一次写lua代码,但因为实现的功能很简单,所以思路也很清晰,一气呵成:

    上面的代码思路很清晰,看起来也没什么问题,执行结果貌似也都OK,于是我沾沾自喜的开始检测一些边界值和特殊情况以避免bug,当我做出如下检测时,我发现了令我十分费解的问题:

    如上,我分别检测了两组IP和IP段,发现结果不一致,从打印出来的结果来看,第二组IP转换为整形的结果竟然是负数。 机智的我转念一想,应该是字符串溢出问题,即:

    我在将192.168.12.65 转换为整形的时候相当于进行了如下操作:

    注: bit模块使用可参考 http://bitop.luajit.org/api.html

    那找到问题了,简单啊,将有符号的整形换做无符号整形或者长整形不就得了 ? 结果问题来了,我查了好久,竟然没有找到luajit以及lua5.1可以支持这两种类型的(或许是我没查到,但我真的查了挺久)。

    后来,查到luajit可以通过ffi 库来调用 C 程序,我一想,那么好办了,C语言里面可是什么类型都有的啊,于是就先简单学习接触了一下luajit的这个ffi库(具体内容可参考网上的这篇博文),于是我写出了如下的程序:

    运行后发现并没有什么卵用:

    貌似我每次只要执行下面这一步之后,ipRangeMin就变成负数了。

    我很郁闷,最后发现原因在于,0xFFFFFFFF左移动24位的结果是负的,并且在左移之后变成了64位:

     

    这让我突然觉得束手无策了,我开始怀疑luajit的bit库的处理逻辑了,于是我做了如下实验:

    无语,也就是说luajit的这个bit库它只能运算32位的操作数,而且当你使用它的左移函数时,如果移动的结果将最高位变成1,它会在前面再补32个1,且这些1没有实际的算术意思。

    唉,既然luajit它本身的bit库不好用,那我用C语言的位操作可好?  但是又想到C语言的左移和右移是符号操作,没有实际的函数载体,而且即使我可以用那样代码写起来可用性也不高了。

    于是我退而求其次,不使用位操作了,单纯的使用lua语言来实现这一功能,只是抛弃了IP地址段的最小值验证功能,如下:

    但是,这样写其实有一个问题,就是IP地址段的写法必须标准,比如:

    到此,这个函数的功能才勉强实现了,感觉心好累,可能是我自己第一次接触Lua,上面的分析或者思路或许会有漏洞或错误,但今天确实没有找到其他方法了。

    总结来说,其实这个功能算法很简单,但是耗费了我很多精力的原因是对Lua的不熟悉,以及对代码的掌控和分析能力较弱,后续还得提高。