插件审核规范
最后修改:2021-4-10 版本:V1.0.1
一 基本信息
1 LOGO
常出现的问题是:
- 禁止单色或者简单渐变背景加文字的LOGO
- 禁止一个LOGO用于多个应用(同应用不同分支、扩展的除外)
2 应用截图
开发者至少需要上传一幅应用截图。如果插件拥有后台设置,提交的截图中应该包含后台部分。
对于仅使用嵌入点实现功能的插件,应该截图展示启用插件后的效果。
对于搭配模板使用的插件,应该上传使用了该插件的模板截图。
3 应用介绍
应用介绍不能为空,原则上也不能过于简单,如只有一句话描述。
应用介绍应当主要介绍当前插件,不允许出现大部分内容都是其它插件广告的情况。
对于验证码、验证问答等某些插件而言,开发者应当说明安装插件后,还需要在后台的何处进行何种设置才能启用插件,否则某些站长可能会因为不会使用、安装插件后无效果而产生疑问。
如果当前插件依赖其它插件、模板、扩展才能使用,必须清楚的注明。
4 文件打包
文件打包前,开发者应清理插件目录中的无关文件,包括 IDE 项目文件、版本管理相关文件、Windows 缩略图缓存(Thumb.db 等),以及未被使用的 PHP、HTML、图片文件等。
插件目录及所有子目录中都应该包含一个空白的 index.htm 文件。
文件命名原则上只使用小写英文字母,不建议文件名包含中文,不允许文件名中包含任何特殊字符。
5 提交审核
插件分支的常见用途是用来区分“免费版”、“收费版”等。同一类型版本的不同版本号(如“1.0 免费版”、“1.1 免费版”)更新时,不应该创建新的分支,而应该在同一个分支下(“免费版”)进行更新。
不允许使用不同分支来区分同一版本、不同编码的情况(如“免费版 GBK”、“免费版 UTF-8”),这种情况下应该使用语言包来解决。
提交审核时应该正确选择编码(尤其是同时支持 GBK 和 UTF-8),切勿随意勾选。
应当慎重选择支持的 Discuz! 版本。从 X2.5 起,新引入了 C::t() 数据层,且不再支持 $_G[gp_xxx]。如果代码中使用了这些特性,插件不支持 X2,不能勾选。
如果插件主要功能是调用第三方 API,在提交审核时应该提供测试账号和密码。
二 代码规范
1 文件开头
PHP 标签应该使用 <?php
,慎用 <?
和 <?=
。
所有 *.inc.php 文件都必须检测 IN_DISCUZ 常量是否已经定义。如果该文件是后台模块,则应该检测 IN_ADMINCP 常量(后台模块可同时检测 IN_DISCUZ、IN_ADMINCP,或单独仅检测 IN_ADMINCP)。
请注意,所有的 *.inc.php 都可通过 plugin.php?id=xxx:yyy 方式进行访问。如果某文件是嵌入模块(如嵌入“个人设置”、“论坛管理”等),必须额外检测当前环境是否是嵌入环境(如检测 CURSCRIPT 是否为 home 保证当前确实是被 home.php 引入)。
所有 *.class.php 文件都必须检测 IN_DISCUZ 常量。
对于其它 PHP 文件,除 API 和第三方 SDK 外,必须检测 IN_DISCUZ 常量。不需检测 IN_DISCUZ 常量的情况有:没有使用 Discuz! 提供的任何类和函数;手动载入 class_core.php 并初始化 Discuz! 环境。
install.php、uninstall.php、upgrade.php 必须检测 IN_ADMINCP 常量。
2 文件结尾
对于纯 PHP 文件,推荐文件结尾不使用 ?>
标签。
如果文件结尾使用了 ?>
标签,在标签后不允许存在任何多余的字符,包括空格、回车在内的空白。
3 后台入口
有些站长为了安全会修改后台入口文件名,所以网站后台入口请使用 ADMINSCRIPT ,而不是 admin.php。
4 代码风格
所有的 PHP、JS、HTML 文件代码风格应该干净整洁、可读性好,易于审核人员阅读和检查。使用 jQuery 等第三方库的压缩版本可例外。
提交的源代码不允许自行进行加密和混淆。
三 插件规范
1 数据表规范
开发者尽量仅使用 MyISAM 和 Memory 两种数据表,不使用事务功能(X3.4及以前版本);开发者尽量仅使用Innodb 和 Memory 两种数据表,不使用事务功能(X3.5开始)。
设计数据表时,应正确设计字段类型,如:存储时间原则上使用 int 类型保存时间戳;定长字符串(如 md5 哈希值)使用 char 而非 varchar 等。
开发者应当合理设置数据表的索引,尤其是作为查询条件的字段。
2 安装和卸载
安装时创建数据表语句不应指定数据表编码(Discuz! 会自动将 TYPE=MyISAM
转换为 ENGINE=MyISAM DEFAULT CHARSET=xxx
)。
卸载时,应当删除插件安装时建立的所有数据表和字段,完全清理插件使用过程中产生的文件(如保存的用户上传文件)。
如果开发者认为插件数据价值较大,可在卸载时向用户询问是否保留数据。如果用户保留数据,下次安装插件时,不能出现无法安装的情况,如产生数据表已存在等错误。
3 多编码支持
开发者应当使用语言包机制实现多编码支持。对于手动使用 diconv() 手动对中文进行转码的方式,目前不禁止,但并不提倡。
如果插件文件(包括 PHP、JS、模板)中出现了中文字符且并未进行转码,在上传插件提交审核时禁止勾选多种编码支持。
4 效率优化
开发者在设计插件时,在保证功能完整和安全性的前提下,应多考虑代码的运行效率。
最常见的低效率代码是在循环中执行 SQL 语句,如下面的代码:
foreach($data as $id) {
C::t('#xxx#yyy')->delete_by_id($id);
}
在大多数场景下,循环内的 SELECT
和 DELETE
语句均可通过使用 WHERE IN
语句进行优化,使原本的执行多次查询优化为一次。
在使用全局嵌入点时必须考虑效率问题:决不允许在循环中执行 SQL 查询;查询条件对应的字段原则上必须建立索引;尽量使用缓存。
某些插件需要在嵌入点访问远程 URL,原则上不允许进行这种行为,因为如果访问 URL 耗时较长,会直接拖慢整个页面的加载速度。这种情况应该将远程访问的行为单独放在一个 PHP 脚本中进行,并在嵌入点中使用 script 标签访问,将原本的操作优化为异步进行。
开发者应该充分利用缓存功能,并尽可能降低数据库的查询次数,达到效率优化的目的。
5 内置函数
开发者应当使用以下 Discuz! 内置函数来取代 PHP 原生函数,提高代码的兼容性:
- 使用
dfsockopen()
访问远程 URL,而非使用file_get_contents()
或手动创建 CURL 对象 - 使用
updatemembercount()
更新用户的积分,而非手动执行 SQL 语句更新 pre_common_member_count 表 - 使用
dhtmlspecialchars()
过滤 HTML 字符,防止在 PHP 5.4.0 或更高版本的服务器中出现问题 - 使用
daddslashes()
过滤入库的字符串,正确处理数组变量 - 使用
diconv()
转换编码 - 使用
cutstr()
而非substr()
切割字符串,避免中文字符被错误切割
四 安全规范
1 include/require
如果使用 include/include_once/require/require_once 动态的引入文件,必须保证路径的合法性,例如下面的代码:
$mod = $_GET['mod'];
require_once DISCUZ_ROOT.'./source/plugin/xxx/module/'.$mod.'.php';
变量 $mod
未经过滤就被直接使用,存在重大的安全隐患。
开发者必须添加相应的过滤代码(如 in_array($mod, array ('index', 'del'))
),保证类似情况下,$mod
是合法的。
2 SQL 注入
避免 SQL 注入漏洞的原则是所有数据在入库前都必须进行过滤,如数字型 ID 执行 intval()
,字符串数据执行 daddslashes()
。
为避免代码逻辑复杂而导致部分地方遗漏过滤,建议使用数据层功能,将对某一数据表的操作都封装为类方法,统一进行过滤。
同时,尽量不要使用类似下面手动生成 SQL 执行的代码:
DB::fetch_all("SELECT * FROM ".DB::table('mytable')." WHERE id='$id'");
而是使用替换占位符:
DB::fetch_all("SELECT * FROM %t WHERE id=%d", array('mytable', $id));
同时还需要注意,如果查询条件是 LIKE 语句,还应该额外过滤 %
和 _
字符。
二次入库导致的 SQL 注入问题常被人忽视,如下列代码:
foreach(DB::fetch_all("SELECT name FROM ".DB::table('mytable1')) as $result) {
DB::query("UPDATE ".DB::table('mytable2')." SET xxx=xxx+1 WHERE name='".$result['name']."'");
}
此处由于 $result['name']
是从数据库中查询得到,未经过滤就再次代入到 SQL 语句中,依旧存在 SQL 注入漏洞,而且较为隐蔽,开发者应当正确处理这种情况。
3 CSRF
无论前台或后台功能,只要涉及数据变更操作(如插入、修改和删除),都必须检测请求来源,避免 CSRF 跨站请求伪造攻击。
如下列代码存在安全隐患:
if(empty($_GET['delsubmit'])) {
include template('xxx');
} else {
DB::delete('mytable', array('id' => $_GET['id']));
}
请求来源的检测可通过调用 submitcheck()
或手动检测 FORMHASH,上述代码 if(empty($_GET['delsubmit'])) {
可改造为:
if(!submitcheck('delsubmit')) {
或
if(FORMHASH != $_GET['formhash']) {
4 XSS
插件将数据库中的字符串类型数据显示在页面之前,如果没有经过 dhtmlspecialchars()
过滤,则存在 XSS 跨站脚本攻击漏洞。
开发者应当意识到 XSS 漏洞的危害,在数据直接输出前进行过滤。应注意,数据输出除了直接显示外,还包括 <input type="text" value="$data" />
和 <textarea>$data</textarea>
等情况。
五 其它说明
严禁发布包含色情、赌博及其它违反国家法律内容的插件,严禁在插件中包含病毒、木马、后门及其它恶意代码,如有发现,严肃处理。
不允许发布单机 Flash 游戏、网页游戏类、在线后台服务器文件管理类的插件;发布支付类插件必须通过企业认证,不允许包含除支付宝、微信等知名合法渠道之外的第四方支付功能;不允许发布功能过于简单、界面过于粗糙的插件;不允许发布应用中心已有多款类似且无明显创新的插件。
开发者应当明确为用户提供服务的时间(如 7x24、5x8 等)、时效(如 2 小时内)、方式(如 QQ、论坛等)、期限(商业用户)等。
更多要求请阅读:https://open.dismall.com/audit
审核人员不是插件测试人员,请开发者在提交上传插件前自行完成插件的测试。插件在审核时,会随机分配给不同的审核人员。不同的审核人员有不同的习惯(如有的发现一个问题就打回,有的打回时多寻找问题),请开发者理解。