幕间 • 安全、伦理与责任
一个不应存在的漏洞
写作本文的前段时间,MCBBS 纪念版通报了 一起严重的插件漏洞事件。该插件的代码能够从指定的远程服务器获取 SQL 语句,并且无条件执行。如果你不知道这意味着什么,这种行为等价于允许插件开发者任意控制插件所使用的数据库:如果该数据库存储着密码,他们可以随意更改,而如果数据库存储着其它重要的信息(比如背包数据),他们也可以随心所欲地将其摧毁 —— 只要他们想这么做。
在调查中,该插件的开发者试图解释这种行为是为了进行正版验证,但这种说法是苍白无力的 —— 不论是有意还是无心,他们授予了插件远远超出必要的权限,并且没有对此进行必要的安全限制 —— 这就是后门,没什么好辩解的。尽管论坛已经广泛通知服务器管理员停用该插件,但我们无法估计究竟有多少服务器仍然在使用该插件,并面临着潜在的威胁。
这一案例让我们不得不开始重新审视插件安全问题和插件开发相关的伦理道德,也最终促使笔者将这部分内容添加到本书。我们希望能在这篇『幕间』里,向各位读者介绍关于插件安全的一些内容,以改善插件社区的整体质量。
不要编写恶意代码
我们会尽可能做到公开、诚实和相信他人,我们希望您也是如此。—— Mojang
我想这不应该是在本书中还需要强调的内容,但我们还是在此重申:在任何时候都不要编写有害的代码 —— 诚然,能在服务器上为自己留个管理员权限的后门听上去确实很诱人,但这绝不是你应该做的事情。传播任何恶意代码都是被整个 Minecraft 社区所谴责的,不仅将被各平台和组织严厉制裁,并且还会面临法律上的指控。
在 Minecraft 服务器发展史上,作弊软件、后门插件和病毒已经让整个社区付出极为惨痛的代价,坏人为了做坏事(盗取数据、破坏游戏、干扰运营)无所不用其极。尽管『不应编写恶意代码』是常识性的东西,但就是有坏人不认同这一点,这些人以破坏为乐,它们的工作和乐趣就是让别人没办法好好玩游戏。这些怪物是 Minecraft 社区和整个软件界所共同憎恶的敌人,希望你不要成为它们中的一个。
使用最新版本的程序
每当开发者发现程序有漏洞的时候,他们就会编写代码来修复,并发布相应的更新。及时获取这些更新是非常重要的,典型的例子是 Mojang 对物品复制漏洞的修复:1.16.2 中基于幼年猪灵的复制漏洞只需要简单升级到 1.16.3 就可以修复。
在开发插件时,我们会使用到各种工具和依赖库。如果这些软件本身存在漏洞,它们也有可能影响到我们的插件,因此应当及时更新 IDE、Java(主要版本除外)、Kotlin 编译器和各种依赖库的版本。
在 Gradle 中,升级依赖库版本非常简单:你可以在 Maven 仓库索引 上查找某个依赖库的最新版本,并且修改 build.gradle.kts 中的相应版本号。例如,假设你在使用 MapDB 的 3.0.10 版本:
implementation("org.mapdb:mapdb:3.0.10")
但你通过 Maven 仓库索引查找,发现 MapDB 的最新版本是 3.1.0,那么就应该尽快将 3.0.10 修改为 3.1.0 进行升级:
implementation("org.mapdb:mapdb:3.1.0")
有时,依赖库的一些升级可能是破坏性(Breaking) 的,你可能必须要修改已有的代码才能升级,这时应当根据开发者发布的说明相应修改代码。
此外,如果某个 Minecraft 版本自身存在影响插件安全的漏洞,那么最好是不要支持该版本,并建议用户尽快升级。例如,对于上面提到的物品复制漏洞,如果你的插件拥有诸如『打造超级武器』这样的功能,那么最好是不要支持有漏洞的 1.16.2 版本。即使出于某些原因必须支持这些版本,那么也需要做额外的处理,例如通过事件阻止幼年猪灵捡起『超级武器』等。
持续维护你的插件
如果你编写了一个插件,并且计划将它向社区公开发布,那么就要做好长期维护它的准备。你的插件一旦发布,就可能会有人来使用,而他们会希望你能够及时处理插件中发现的漏洞。有时,一个插件可能不仅直接被用在服务器上,还可能会有其它插件基于它开发,如果源插件不及时更新,这些衍生项目的安全也会同样受到影响。
虽说如此,但要长期花费时间来维护插件项目的确是非常耗费精力的事情。插件社区有不少项目都是由于开发者的个人时间问题而被废弃的。有鉴于此,如果大家不在一个专门的插件开发团队中,那么最好是不要公开地发布个人项目,特别是那些还没准备好长期维护的项目。即使你信心满满,觉得自己是那种非常有毅力的人,那么最好也先在小范围内试用你的插件,试着维护它,并评估自己是否有能力继续这么做。
简而言之,发布任何插件项目之前,都要始终问自己一个问题:
『长期维护,及时更新』……你是抱着多大的觉悟说出这种话的?你有能力负起这样的责任吗?
避免使用废弃的 API
随着版本的更迭,一些 API(不论是 Bukkit 提供的还是依赖库或者 Kotlin 本身的)可能会被废弃(Deprecate),被废弃的 API 可能在几个版本内还能使用,但最好是尽快更新代码,不再使用这些被废弃的 API。如果你的代码本来就不包含废弃的 API,那就更不要把它引入进来了。
一般而言,当开发者决定废弃掉某个 API 时,肯定有出于功能、性能或者安全方面的考虑。『废弃』的意思是,有其它的 API 能做到相同的事情,并且做起来更快或者更安全。如果你不知道被废弃的 API 该使用什么替代,可以去咨询做出这一决定的开发者。
在 IDEA 中,被废弃的类、方法、属性等会以一条横线划掉,提醒你『不要再使用它啦!』,就像这样:

通常在废弃 API 的文档中能找到废弃原因和可供替代的选择:

在上面的例子中,Bukkit 希望我们改用地址对象作为 banIP 的参数,而不是简单的字符串。
保护数据的隐私
数据隐私是大家都关注的一个话题,因此虽然大家也许还对此一知半解,可能没有编写过相关的代码,但笔者还是想在这一节里单独把它拿出来讨论一番。
关于数据隐私必须强调的一点是,尽量避免收集用户使用数据,并且绝对不要未经允许就收集数据。我们知道有一部分插件在这么做,有些得到过用户的授权,有些则是非法获取的。当然,未经允许收集数据是触犯法律的,并可能会被提起相应指控 —— 这就是后门!但即便管理员同意你这么做,也最好避免不必要的数据收集,这是出于以下几个方面的考虑:
- 服务器上所存在的使用数据,不仅来自于你的用户(即管理员),还有这些服务器的玩家。你很难确认他们是否同意与你共享这些数据。
- 许多服务器的网络环境有所限制,要共享这些收集的数据本身很困难。
- 收集不必要的数据会对服务器的性能造成不必要的影响。
- 这部分用来收集数据的开发时间和资源本可以用来改进功能。
数据收集是有一些合理的用途的,例如收集错误信息来改进插件稳定性,如果你的插件属于这种用途,那么也请一定要遵循下面的几条规范:
- 必须经过用户同意才收集或发送数据。
- 只收集绝对必要的数据。
- 在发送前向用户展示将要发送的数据。
- 从数据中删除敏感信息。
- 绝不要将数据用于声明之外的用途。
遗憾的是,尽管大多数人都同意『数据隐私是个热点话题』,但『用户数据隐私值得保护』这一点仍有许多争议。我们无法心灵控制各位绝对不要编写数据收集的代码(笑),但我们还是在此倡议,如果你不希望在你的服务器上到处充斥着胡乱扒取各种信息的插件,那么首先自己就不要这样做。除此之外,我们也在推进各类论坛和资源发布站不断改进隐私审核的条例,促进用清洁、公开的插件替换掉那些所谓『验证正版』但实际上偷偷修改数据库的插件。
共同的责任
应该重申的是,Minecraft 服务器上的安全是管理员、启动器(开服器)、服务端软件、数据库系统、插件、模组乃至数据包等相关工作者的共同责任。当你在加入这个社区的时候,无数玩家安全和快乐的游玩体验也都同时寄托在你的身上。作为插件开发者,在服务器这样一个高度敏感的环境里,对程序安全的重视显得比以往任何时刻都要重要。尽管大多数与插件开发相关的文献都着重于技术、性能与可维护性,但笔者想在这里告诉大家,程序安全也是考察插件的重要指标,而且比性能和功能都更加关键。