Discuz!基础的代码安全和代码规范
阅读原文时间:2023年07月11日阅读:1

变量
所有漏洞都来源于变量,因此变量首先要做的就是定义初始化。用任何一个变量前一定要先定义,初始化它
虽然现在Discuz!X来说,GPC不会被全局覆盖了,但是大家写插件的过程中也不要忽视了
因为在服务器php.ini的配置中 global on 时
所有的GET POST 都会变成变量
$_GET['xxx']  如果存在
就会变成 $xxx 而产生在程序里
因此,你自己要用的变量,一定要初始化

第一点,变量的初始化
无论你要怎么利用变量,一定要初始化
不管是Discuz!还是Discuz!X
由于PHP的历史原因
你不能相信任何一个服务器
只因为php有个global on 的参数
他会让GPC变量直接变成全局变量
因此,你自己的变量一定要初始化

第二点,认清变量的类型
整刚才说到,变量一定要初始化,那么初始化成什么类型
如果你要用于数组

初始化$a = array();
字串 $a ='';
数字 $a = 0

这样子,这里说说数组,这里又存在一个php历史问题,这历史延续至今。

$s = '123456';
echo $s[2];
等于什么?

[]大家都知,这是数组的用法,但php太自由了 $s[3] 的数组用法,竟然可以用在字串里,这个在大家认为很自然。
但是,很多漏洞会从此诞生,这就是认清自己的变量类型的道理所在。
$s[1] 是数组的用法,可以用在字串中,但是我不推荐大家用,黑客会这么用在GET中更改你的变量类型。

举例
echo $s[2];
简单这一句,你认为这是输出数组的一个项,还是字串?
大多数人会认为这明显是输出数组的一个项目,但是刚才也看到了,它输出字串的一个字节也可以。
那,我们反过来思考,如果程序的某逻辑需要输出字串的某字节,但是,如果你没明确告知变量类型。那么有可能会让这一个字节变成一个字串 xxx.php?s[2]=hello

初始化+认清自己的变量类型
特别是数组和字串,如果你要取字串的某一位,安全的方法,可能还是 substr($s, 2, 1)
切忌[]的用法,一定要$a = array() 不要让他和字串类型的变量产生互用理中

第三点、不要相信任何一个即将入库的变量
进很多SQL注入都是从变量开始,要仔细看每一个SQL语句中可能出现的变量,如,整数一定要intval;字串一定要addslashes处理,说到addslashes,说说Discuz!的特点,国外某些论坛是,所有变量都无需addslashes,addslashes只在SQL数据库类中统一处理,但Discuz!不是,Discuz!会统一给GPC变量自动addslashes。

注意,是只给GPC变量加addslashes,其他的都没加。所以,你要注意两件事:
1. GPC变量你想用于显示?
那么别忘记stripslashes 后在显示,否则万一遇到有带有 ' 的,那就会多一个\,I'm 变成 I\'m
htmlspecial只处理<>这些,和单引号无关

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进去。

那么,我再继续深一步,刚才说到了数据库。
我们保证了入库前的所有变量必须是addslashes,但是,如果你不做下面的一件事。那么你再addslashes也是白搭
哪件事呢?
select * from table where id=$id
select * from table where id=I\'m
看,依然sql错误,而且还被注入

我举例子 
没错,单引号封闭,无论你是什么类型的字段
在Discuz!的规范里,必须都加单引号
select * from table where id='$id'
WHERE后面的所有条件
变量必须加单引号,这是规范。也许你少加一个,并不会产生漏洞,也只是也许。

但,有些隐形的漏洞就是在七拐八拐中产生的。你少写一个,那些黑客就会用七拐八拐的方式去分析,看看可否利用行中。
intval是必须的,规则也是必须遵守的,然后是html的问题,不要漏掉任何一个字串类型。
所有字串类型,如果你不希望他们显示html,入库前一定要htmlspecialchars后再入库,或者strip_tags下

数字类型
大家都知道intval,字串是htmlspecialchars,当然,后台无所谓!后台比较安全,但是,最好也有,一个习惯,仅作参考。

$a = '12345';
$a_en = htmlspecialchars($a)
$a_ad = addslashes($a);

这样,入库的时候sql查询语句里应该都是_ad结尾的
html模版中的应该都是_en结尾的
我说不出很多黑客的那些漏洞属于
但是刚才说的那些如果都做到了,代码安全不成问题

然后说说Discuz!内置的一些变量

Discuz!X中,理论上你要用 $_G ($_G['gp_*'] 除外) 里的变量,都要考虑htmlspecialchars和addslashes
$_G['gp_*'] 是DX里的GPC
GP
GET POST 都经过了 addslashes后存放到了$_G['gp_*]中
但_G中的其他,均没addslashes
GPC都addslashes过
dhtmlspecialchars 支持数组
其他同理
如果你有一个良好的代码规范,也会让代码安全性提高
很多规范大家可以给自己定,像刚才的$a_en $a_ad

第四点,下面简单说说代码规范
大家阅读代码的时候也许都看到了
$a = 1;
赋值语句前后空格
if(………
if后面没空格,这些细节涉及不到安全,但有一点,这些也有安全影响?

SQL
良好的代码规范可增加自己的可读性
DB::query("UPDATE ".DB::table('common_member_count')." set $giftunit = $remaining where uid = $_G[uid]");

Discuz!规定sql语句中的SQL关键词必须大写
DB::query("UPDATE ".DB::table('common_member_count')." SET $giftunit='$remaining' WHERE uid='$_G[uid]'");
修正过的

$giftunit = $remaining 
没引号,而且大小写不明,阅读起来很累,规范的不规范的写在一起
虽然站长,不会看代码。但是,一个漂亮代码文档,就像一篇好作文。

有一个SQL上的细节 notifications=notifications+1
这种SQL语句,理论上我们不推荐,虽然这么写本身是正确的
如果a字段是unsigned类型,那么会产生数暴增
产生溢出

a=a+'-1'
就不会

mysql好奇怪的
这点大家通过pma试试就知道了

插件如有减分操作的时候
当a=0的时候
1、a=a-1
2、a=a+'-1'
3、a=a+(-1)
三种方法,你看哪个ok

下面说最后一点
涉及安全的,代码的流程,你的程序,如果只在你的流程下运作
允许100、1000次也许都不会出错。但是,在别人那里,不一定

举例
foreach($array as $k => $v)
嗯,$array是数组,并且存在的时候。。。

没错,是,你自己调试的时候,这数组肯定有
但是在用户那里,有可能就没了
这时候,这语句有错

如果$array不是数组的情况下,这句就错了
你可以if(is_array($array))
也可以(array)$array

类似影响流程的不仅仅是foreach
如rawurlencode
echo rawurlencode('sss');
谁都知没问题
但!!回到刚才我曾说过的,认清变量类型
echo rawurlencode(array('11'));

肯定出错,大家可以测试下
因此 rawurlencode($s) 的时候
一定要确保$s是字串还是数组

用php的每一个函数的时候都要看清接受参数的类型
如果是字串,切记刚才说的
php很傻,有时候字串、数组不分
这是最后一点,爆路径,爆路径后能干啥,黑客最清楚