Discuz!基础的代码安全和代码规范
<br /><strong>变量<br /></strong>所有漏洞都来源于变量,因此变量首先要做的就是定义初始化。用任何一个变量前一定要先定义,初始化它<br />虽然现在Discuz!X来说,GPC不会被全局覆盖了,但是大家写插件的过程中也不要忽视了<br />因为在服务器php.ini的配置中 global on 时<br />所有的GET POST 都会变成变量<br />$_GET['xxx'] 如果存在<br />就会变成 $xxx 而产生在程序里<br />因此,你自己要用的变量,一定要初始化<br /><br /><strong><font color="red">第一点,变量的初始化<br /></font></strong>无论你要怎么利用变量,一定要初始化<br />不管是Discuz!还是Discuz!X<br />由于PHP的历史原因<br />你不能相信任何一个服务器<br />只因为php有个global on 的参数<br />他会让GPC变量直接变成全局变量<br />因此,你自己的变量一定要初始化<br /><br /><strong><font color="red">第二点,认清变量的类型</font></strong><br />整刚才说到,变量一定要初始化,那么初始化成什么类型<br />如果你要用于数组<br /><br />初始化$a = array();<br />字串 $a ='';<br />数字 $a = 0 <br /><br />这样子,这里说说数组,这里又存在一个php历史问题,这历史延续至今。<br /><br />$s = '123456';<br />echo $s;<br />等于什么?<br /><br />[]大家都知,这是数组的用法,但php太自由了 $s 的数组用法,竟然可以用在字串里,这个在大家认为很自然。<br />但是,很多漏洞会从此诞生,这就是认清自己的变量类型的道理所在。<br />$s 是数组的用法,可以用在字串中,但是我不推荐大家用,黑客会这么用在GET中更改你的变量类型。<br /><br /><strong>举例</strong><br />echo $s;<br />简单这一句,你认为这是输出数组的一个项,还是字串?<br />大多数人会认为这明显是输出数组的一个项目,但是刚才也看到了,它输出字串的一个字节也可以。<br />那,我们反过来思考,如果程序的某逻辑需要输出字串的某字节,但是,如果你没明确告知变量类型。那么有可能会让这一个字节变成一个字串 xxx.php?s=hello<br /><br />初始化+认清自己的变量类型<br />特别是数组和字串,如果你要取字串的某一位,安全的方法,可能还是 substr($s, 2, 1)<br />切忌[]的用法,一定要$a = array() 不要让他和字串类型的变量产生互用理中<br /><br /><font color="red"><strong>第三点、不要相信任何一个即将入库的变量</strong></font><br />进很多SQL注入都是从变量开始,要仔细看每一个SQL语句中可能出现的变量,如,整数一定要intval;字串一定要addslashes处理,说到addslashes,说说Discuz!的特点,国外某些论坛是,所有变量都无需addslashes,addslashes只在SQL数据库类中统一处理,但Discuz!不是,Discuz!会统一给GPC变量自动addslashes。<br /><br />注意,是只给GPC变量加addslashes,其他的都没加。所以,你要注意两件事:<br />1. GPC变量你想用于显示?<br />那么别忘记stripslashes 后在显示,否则万一遇到有带有 ' 的,那就会多一个\,I'm 变成 I\'m<br />htmlspecial只处理<>这些,和单引号无关<br /><br />2. 再次,刚才说过DZ只处理GPC。因此,GPC之外的所有变量一定要自己addslashes,特别是有些时候时,把A库的东西读出来后,直接复制到B库的情况。有人说A库的东西都入进去了,直接入B库还不安全吗?这可不一定,I'm 被A读取出来后 直接入B库,肯定是sql error,必须addslashes。如果要 serialize ,那么 serialize前无需addslashes,serialize后要,从数据库中去出来的肯定是I'm,不是I'\m,serialize是好东西,但不要把addslashes后的也给serialize进去。<br /><br />那么,我再继续深一步,刚才说到了数据库。<br />我们保证了入库前的所有变量必须是addslashes,但是,如果你不做下面的一件事。那么你再addslashes也是白搭<br />哪件事呢?<br />select * from table where id=$id<br />select * from table where id=I\'m<br />看,依然sql错误,而且还被注入<br /><br />我举例子 <br />没错,单引号封闭,无论你是什么类型的字段<br />在Discuz!的规范里,必须都加单引号<br />select * from table where id='$id'<br />WHERE后面的所有条件<br />变量必须加单引号,这是规范。也许你少加一个,并不会产生漏洞,也只是也许。<br /><br />但,有些隐形的漏洞就是在七拐八拐中产生的。你少写一个,那些黑客就会用七拐八拐的方式去分析,看看可否利用行中。<br />intval是必须的,规则也是必须遵守的,然后是html的问题,不要漏掉任何一个字串类型。<br />所有字串类型,如果你不希望他们显示html,入库前一定要htmlspecialchars后再入库,或者strip_tags下<br /><br />数字类型<br />大家都知道intval,字串是htmlspecialchars,当然,后台无所谓!后台比较安全,但是,最好也有,一个习惯,仅作参考。<br /><br />$a = '12345';<br />$a_en = htmlspecialchars($a)<br />$a_ad = addslashes($a);<br /><br />这样,入库的时候sql查询语句里应该都是_ad结尾的<br />html模版中的应该都是_en结尾的<br />我说不出很多黑客的那些漏洞属于<br />但是刚才说的那些如果都做到了,代码安全不成问题<br /><br />然后说说Discuz!内置的一些变量<br /><br />Discuz!X中,理论上你要用 $_G ($_G['gp_*'] 除外) 里的变量,都要考虑htmlspecialchars和addslashes<br />$_G['gp_*'] 是DX里的GPC<br />GP<br />GET POST 都经过了 addslashes后存放到了$_G['gp_*]中<br />但_G中的其他,均没addslashes<br />GPC都addslashes过<br />dhtmlspecialchars 支持数组<br />其他同理<br />如果你有一个良好的代码规范,也会让代码安全性提高<br />很多规范大家可以给自己定,像刚才的$a_en $a_ad<br /><br /><font color="red"><strong>第四点,下面简单说说代码规范</strong></font><br />大家阅读代码的时候也许都看到了<br />$a = 1;<br />赋值语句前后空格<br />if(.........<br />if后面没空格,这些细节涉及不到安全,但有一点,这些也有安全影响?<br /><br />SQL<br />良好的代码规范可增加自己的可读性<br />DB::query("UPDATE ".DB::table('common_member_count')." set $giftunit = $remaining where uid = $_G");<br /><br />Discuz!规定sql语句中的SQL关键词必须大写<br />DB::query("UPDATE ".DB::table('common_member_count')." SET $giftunit='$remaining' WHERE uid='$_G'");<br />修正过的<br /><br />$giftunit = $remaining <br />没引号,而且大小写不明,阅读起来很累,规范的不规范的写在一起<br />虽然站长,不会看代码。但是,一个漂亮代码文档,就像一篇好作文。<br /><br />有一个SQL上的细节 notifications=notifications+1<br />这种SQL语句,理论上我们不推荐,虽然这么写本身是正确的<br />如果a字段是unsigned类型,那么会产生数暴增<br />产生溢出<br /><br />a=a+'-1'<br />就不会<br /><br />mysql好奇怪的<br />这点大家通过pma试试就知道了<br /><br />插件如有减分操作的时候<br />当a=0的时候<br />1、a=a-1<br />2、a=a+'-1'<br />3、a=a+(-1)<br />三种方法,你看哪个ok<br /><br />下面说最后一点<br />涉及安全的,代码的流程,你的程序,如果只在你的流程下运作<br />允许100、1000次也许都不会出错。但是,在别人那里,不一定<br /><br />举例<br />foreach($array as $k => $v)<br />嗯,$array是数组,并且存在的时候。。。<br /><br />没错,是,你自己调试的时候,这数组肯定有<br />但是在用户那里,有可能就没了<br />这时候,这语句有错<br /><br />如果$array不是数组的情况下,这句就错了<br />你可以if(is_array($array))<br />也可以(array)$array<br /><br />类似影响流程的不仅仅是foreach<br />如rawurlencode<br />echo rawurlencode('sss');<br />谁都知没问题<br />但!!回到刚才我曾说过的,认清变量类型<br />echo rawurlencode(array('11'));<br /><br />肯定出错,大家可以测试下<br />因此 rawurlencode($s) 的时候<br />一定要确保$s是字串还是数组<br /><br />用php的每一个函数的时候都要看清接受参数的类型<br />如果是字串,切记刚才说的<br />php很傻,有时候字串、数组不分<br />这是最后一点,爆路径,爆路径后能干啥,黑客最清楚<br />变量<em>, </em>字串<em>, </em>addslashes<em>, </em>一个<em>, </em>类型 收藏了~~ 下次开发插件前注重以下 感谢分享, <br /><br />机会是留给<br /> 还有很多历史原因遗留的严重BUG,<br />php的正则匹配中小数点.即使你后面没有加/s也是包括\r这个换行符的(网络上所有的教程说法都是错误误导的),<br /><br />还有PHP_EOL这个常量千万不能用,因为它在windows下等同于\r\n,linux下等同于\n, 比如现在是windows写入和读取后分割都是用\r\n确实没有问题, 但如果你以后服务器换到了linux的话分割用PHP_EOL \n那么以前在windows时写入的数据分割后后面全部会平白无故多一个\r出来,长度完全就不对了
頁:
[1]