---
title: 第 11 章 Ports 的安全
prev: books/porters-handbook/port-upgrading
next: books/porters-handbook/porting-dads
showBookMenu: true
weight: 11
params:
  path: "/books/porters-handbook/security/"
---

[[security]]
= Ports 的安全
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumlevels: 6
:sectnumoffset: 11
:partnums:
:source-highlighter: rouge
:experimental:
:images-path: books/porters-handbook/

ifdef::env-beastie[]
ifdef::backend-html5[]
:imagesdir: ../../../../images/{images-path}
endif::[]
ifndef::book[]
include::shared/authors.adoc[]
include::shared/mirrors.adoc[]
include::shared/releases.adoc[]
include::shared/attributes/attributes-{{% lang %}}.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/urls.adoc[]
toc::[]
endif::[]
ifdef::backend-pdf,backend-epub3[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
endif::[]

ifndef::env-beastie[]
toc::[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]

[[security-intro]]
== 安全为何如此重要

软件中偶尔会引入 bug。 毋庸置疑， 安全漏洞是最为危险的。 从技术角度看， 这些漏洞可以通过消除导致它们的 bug 来修复。 然而， 处理一般的 bug 和安全漏洞的策略是截然不同的。

典型的小 bug 通常只影响那些启用了某些能够触发它的选项组合的用户。 开发人员最终会在发布没有那个问题的新版之后给出一个补丁来修正它， 而用户中的主体并不会立即升级， 因为他们并没有因存在问题而感到苦恼。 严重的 bug 可能会导致数据丢失和其它问题， 无论如何， 谨慎的用户知道， 除了软件 bug 之外还有很多其它事故可能会导致数据丢失， 因此他们会备份重要数据； 此外， 严重的 bug 通常会被很快发现。

安全漏洞则完全不同。 第一， 它们可能存在数年而不被发现， 因为它们可能并不导致软件无法正常工作。 第二， 通过利用漏洞， 恶意的一方可能会得到未获授权的访问权限， 并利用这些权限毁掉或修改敏感数据； 而更糟糕的情况则是用户可能根本注意不到损害已经发生。 第三， 暴露出安全漏洞的系统， 往往能够帮助攻击者闯入其它之前不可能进入的系统。 因此， 只是修正安全漏洞是不够的： 必须以清晰和全面的方式通知公众， 这样他们就能够评估风险， 并采取适当的措施。

[[security-fix]]
== 修复安全漏洞

当说起 port 或 package 时， 安全漏洞往往是出现在原作者的发行包， 或移植过程中加入的文件里。 对于前一种情况， 软件的原作者通常会立刻发布一个补丁甚至新版， 您只需要按照原作者的修正去更新 port 就可以了。 如果由于某种原因修正被延误， 则要么 <<dads-noinstall,将 port 标记为 `FORBIDDEN`>>， 要么在 port 中加入一个自己的补丁。 如果有存在漏洞的 port， 尽可能尽快修复其漏洞就是。 无论是哪种情况， 您还是需要按照 <<port-upgrading,标准的提交流程>> 提交， 除非您有直接在 ports tree 上 commit 的权限。

[IMPORTANT]
====
作为 ports committer 并不能够随便 commit 所有 port。 请注意通常 port 都有维护者， 而他们应得到您的尊重。
====

在漏洞被修正之后， 一定要同时增加 port 的修订版本号。 这样， 规律性地升级安装的 package 的用户就能够看到他们需要进行升级。 另外， 还应联编预编译的安装包， 并通过 FTP 和 WWW 镜像发布， 以取代有漏洞的版本。 注意要增加 `PORTREVISION` 数字， 除非在修正问题时 `PORTVERSION` 发生了变化。 一般来说， 如果在 port 中增加了补丁文件， 就应该增加 `PORTREVISION`， 但例外的例子是您已经将软件升级到了最新版， 因为这时已经改掉 `PORTVERSION` 了。 请参见 <<makefile-naming-revepoch,相关小节>> 以了解进一步的信息。

[[security-notify]]
== 通知整个用户群体

[[security-notify-vuxml-db]]
=== VuXML 数据库

当发现了安全漏洞时的一项重要而紧迫的步骤， 就是让使用 port 的用户群了解其危险。 这类通知有两重目的。 首先， 如果危害真的很严重， 可能理性的办法就是立即应用一项缓解措施， 例如， 停止受到影响的服务， 甚至完全删除 port， 直到问题被修正为止。 其次， 许多用户只是偶尔升级所安装的软件包， 通过通知， 他们能够知道已经到了 _必须_ 更新软件的时候， 因为已经有了修正这些问题的版本了。

由于现有的 port 数量极其庞大， 为每一个问题都发布安全公告， 毫无疑问地会发表和狼来了一样多的安全公告， 并增大受众在真的发生严重的问题时忽略问题的可能。 因此， 在 port 中发现的安全漏洞， 会在 http://vuxml.freebsd.org/[FreeBSD VuXML 数据库] 中记录。 安全官团队成员会持续地追踪这个数据库的修改， 以了解需要他们注意的内容。

如果您是 committer， 则可以自行更新 VuXML 数据库。 这样， 您就能够同时帮助安全官团队， 并尽早将至关重要的信息传达给用户群体。 然而， 如果您不是 committer， 或者您相信自己发现了一个异常严重的漏洞， 请不要犹豫， 按照 http://www.freebsd.org/zh-cn/security/#how[FreeBSD 安全信息] 页面上的方法联系安全官团队。

正如其名称所暗示的那样， VuXML 数据库是一个 XML 文档。 其源文件 [.filename]#vuln.xml# 被保存在 package:security/vuxml[] port 的目录中。 所以， 它的全名是 [.filename]#PORTSDIR/security/vuxml/vuln.xml#。 每当您发现 port 中的安全漏洞时， 请把新的纪录加入到那个文件中。 在熟悉 VuXML 之前， 您最好先看看是否有类似的您发现的问题的其它记录， 并复制它作为模板。

[[security-notify-vuxml-intro]]
=== VuXML 简介

XML 是一个复杂的语言， 它远远超越了这本书的范围。 不过， 只需了解标记的命名规则， 就能 VuXML 记录的结构有一个大体的了解了。 XML 标记的名字应出现在一对尖括号之间。 每一个 <tag> 必须有一个对应的 </tag>。 标记可以嵌套， 如果嵌套的话， 内层的标记必须在外层标记之前结束。 这就形成了一个标记的层次结构， 也就是关于它们之间如何嵌套的规则。 听起来很像 HTML， 是不是？ 最主要的区别在于， XML 是可扩展的 (e__X__tensible)， 例如通过定义新的标记等等。 由于其结构的内在性质， XML 能够赋予无组织的数据新的形态。 VuXML 是专门为描述安全漏洞设计的语言。

现在让我们来观察一个实际的 VuXML 记录：

[.programlisting]
....
<vuln vid="f4bc80f4-da62-11d8-90ea-0004ac98a7b9"> <.>
  <topic>Several vulnerabilities found in Foo</topic> <.>
  <affects>
    <package>
      <name>foo</name> <.>
      <name>foo-devel</name>
      <name>ja-foo</name>
      <range><ge>1.6</ge><lt>1.9</lt></range> <.>
      <range><ge>2.*</ge><lt>2.4_1</lt></range>
      <range><eq>3.0b1</eq></range>
    </package>
    <package>
      <name>openfoo</name> <.>
      <range><lt>1.10_7</lt></range> <.>
      <range><ge>1.2,1</ge><lt>1.3_1,1</lt></range>
    </package>
  </affects>
  <description>
    <body xmlns="http://www.w3.org/1999/xhtml">
      <p>J. Random Hacker reports:</p> <.>
      <blockquote
        cite="http://j.r.hacker.com/advisories/1">
        <p>Several issues in the Foo software may be exploited
          via carefully crafted QUUX requests.  These requests will
          permit the injection of Bar code, mumble theft, and the
          readability of the Foo administrator account.</p>
      </blockquote>
    </body>
  </description>
  <references> <.>
    <freebsdsa>SA-10:75.foo</freebsdsa> <.>
    <freebsdpr>ports/987654</freebsdpr> <.>
    <cvename>CAN-2010-0201</cvename> <.>
    <cvename>CAN-2010-0466</cvename>
    <bid>96298</bid> <.>
    <certsa>CA-2010-99</certsa> <.>
    <certvu>740169</certvu> <.>
    <uscertsa>SA10-99A</uscertsa> <.>
    <uscertta>SA10-99A</uscertta> <.>
    <mlist msgid="201075606@hacker.com">http://marc.theaimsgroup.com/?l=bugtraq&amp;m=203886607825605</mlist> <.>
    <url>http://j.r.hacker.com/advisories/1</url> <.>
  </references>
  <dates>
    <discovery>2010-05-25</discovery> <.>
    <entry>2010-07-13</entry> <.>
    <modified>2010-09-17</modified> <.>
  </dates>
</vuln>
....

标记的名字都是简单明了的， 下面我们来介绍一下需要由您填写的字段：

<.> 这是 VuXML 记录的顶级 tag。 它有一个强制性的字段， `vid`， 用于为此记录 (它包含的部分) 指定一个全局唯一标识符 (UUID)。 您应为每一个新的 vuXML 生成新的 UUID (而且别忘了要把模板中的 UUID 换成新的， 如果您不是从头开始的话)。 您可以使用 man:uuidgen[1] 来生成 VuXML UUID。

<.> 关于问题的一句话描述。

<.> 此处给出受到影响的 package 的名字。 可以给出多个名字， 因为可能有多个软件包基于同一个 master port 或软件产品。 这可能包括稳定和开发分支、 本地化版本， 以及提供了不同的编译时选项的 slave port。

<.> 受影响的 package 版本， 可以使用 `<lt>`, `<le>`, `<eq>`, `<ge>`, 和 `<gt>` 表达成一个或多个版本及其范围。 注意给出的版本范围不应存在重叠。在描述范围的时候， `\*` (星号) 表达最小的版本。 更具体地说， `2.*` 小于 `2.a`。 因此， 星号可以用来匹配所有可能的 `alpha`、 `beta`， 以及 `RC` 版本。 例如， `<ge>2.*</ge><lt>3.*</lt>` 可以选择性地匹配每一个 `2.x` 的版本， 而 `<ge>2.0</ge><lt>3.0</lt>` 显然不能， 因为它会漏掉 `2.r3` 而匹配 `3.b`。上面的例子指定了受影响的版本， 是包括 `1.6` 到 `1.9` 上下界的所有版本， 以及 `2.x` 在 `2.4_1` 之前的版本， 和 `3.0b1` 版。

<.> 受到影响的一组 package (本质上是 ports) 可以列在 `<affected>` 小节中。 如果多个软件产品都采用了同样的基础代码， (比如说 FooBar、 FreeBar 和 OpenBar) 而且包含同样的 bug 或漏洞。 请注意列出多个名字时， 应该在一个 <package> 小节中完成。

<.> 如果可能， 版本的范围应包括 `PORTEPOCH` 和 `PORTREVISION`。 务必注意， 根据加权规则， 带有非零 `PORTEPOCH` 的版本， 系统会认为比没有 `PORTEPOCH` 的版本高， 例如 `3.0,1` 高于 `3.1` 甚至 `8.9`。

<.> 关于问题的摘要性信息。 此处使用 XHTML。 务必要成对使用 `<p>` 和 `</p>`。 可以使用较为复杂的标记， 但仅限于有助于让信息更准确和明了的修饰： 请不要过分地美化。

<.> 这部分包含了相关的可供参考的文档。 请尽可能多提供参考文献。

<.> 指定 http://www.freebsd.org/security/#adv[FreeBSD 安全公告]。

<.> 指定 http://www.freebsd.org/support/#gnats[FreeBSD 问题报告]。

<.> 指定 http://www.cve.mitre.org/[Mitre CVE] ID。

<.> 指定 http://www.securityfocus.com/bid[SecurityFocus Bug ID]。

<.> 指定 http://www.cert.org/[US-CERT] 安全公告。

<.> 指定 http://www.cert.org/[US-CERT] 漏洞说明。

<.> 指定 http://www.cert.org/[US-CERT] 计算机安全警报。

<.> 指定 http://www.cert.org/[US-CERT] 技术性计算机安全警报。

<.> 指向邮件列表存档的 URL。 属性 `msgid` 是可选项， 用以指定某一封信的 message ID。

<.> 一般的 URL。 只有在没有其它更适合的参考文献时， 才应使用它。

<.> 问题被全面披露的日期 (_YYYY-MM-DD_)。

<.> 记录加入到数据库中的日期 (_YYYY-MM-DD_)。

<.> 记录最后一次被修改的日期 (_YYYY-MM-DD_)。 新记录不应包括这个字段。 只有在修改记录时才应加入它。

[[security-notify-vuxml-testing]]
=== 测试您对 VuXML 数据库所作的修改

假定您打算撰写， 或已经写好了一个关于 package `clamav` 的问题描述， 并且， 已经知道 `0.65_7` 版本修正了这个问题。

您需要做的准备工作， 是 _安装_ 一个新版本的 ports package:ports-mgmt/portaudit[] 程序、 package:ports-mgmt/portaudit-db[]， 以及 package:security/vuxml[]。

[NOTE]
====
要运行 `packaudit`， 您必须拥有其 [.filename]#DATABASEDIR#， 通常是 [.filename]#/var/db/portaudit# 的写入权限。

您可以通过 [.filename]#DATABASEDIR# 环境变量来指定一个不同的位置。

如果您的工作目录是 [.filename]#${PORTSDIR}/security/vuxml# 以外的其它地方， 则应使用环境变量 [.filename]#VUXMLDIR# 来指明 [.filename]#vuln.xml# 的位置。
====

首先， 检查一下是否已经有了关于这个漏洞的描述。 如果已经有过这样的记录， 那么它将匹配较早版本的 package， `0.65_6`：

[source,shell]
....
% packaudit
% portaudit clamav-0.65_6
....

如果什么都没有发现， 您就可以考虑写一个新的记录来描述这个漏洞了。 现在可以生成一个新的 UUID (假设它是 `74a9541d-5d6c-11d8-80e3-0020ed76ef5a`)， 然后将您的新记录加入到 VuXML 数据库中。 接下来， 用下面的命令来检查它是否符合语法：

[source,shell]
....
% cd ${PORTSDIR}/security/vuxml && make validate
....

[NOTE]
====
您需要安装下列 package 中的至少一个： package:textproc/libxml2[]、 package:textproc/jade[]。
====

接下来从 VuXML 文件重构 `portaudit` 数据库：

[source,shell]
....
% packaudit
....

要验证您新加入的项的 `<affected>` 小节能够正确地匹配希望的 package， 可以使用下面的命令：

[source,shell]
....
% portaudit -f /usr/ports/INDEX -r 74a9541d-5d6c-11d8-80e3-0020ed76ef5a
....

[NOTE]
====
请参见 man:portaudit[1] 以了解关于这个命令语法的更多细节。
====

请确信新添加的记录不会在输出中匹配不应匹配的项。

现在检查您添加的记录所匹配的版本是否正确：

[source,shell]
....
% portaudit clamav-0.65_6 clamav-0.65_7
Affected package: clamav-0.65_6 (matched by clamav<0.65_7)
Type of problem: clamav remote denial-of-service.
Reference: <http://www.freebsd.org/ports/portaudit/74a9541d-5d6c-11d8-80e3-0020ed76ef5a.html>

1 problem(s) found.
....

显然， 前一个版本会匹配， 而后一个不会。

最后， 验证您从 VuXML 数据库中能够正确地得到预期的网页效果：

[source,shell]
....
% mkdir -p ~/public_html/portaudit
% packaudit
% lynx ~/public_html/portaudit/74a9541d-5d6c-11d8-80e3-0020ed76ef5a.html
....
