10.3. 确保FreeBSD的安全

命令与协议: 在这份文档中,我们使用 粗体 来表示命令或应用程序。这一约定被用于 类似 ssh 这样的概念,因为它既可以表示命令,又可以表示协议。

下面几节中的内容将包括 前一节 中提到的那些加强 FreeBSD 安 全性的方法。

10.3.1. 确保 root 和维护人员帐户的安全

首先,如果你没有确保 root 帐户的安全, 就没必要先劳神确保用户帐户的安全了。绝大多数系统都会指派一个口令给 root 帐户。 我们的第一个假定是,口令 总是 不安全的。这并不意味着你要把口令删掉。口令通常对 访问机器的控制台来说是必须的。也就是说,你不应当让它用到控制台以外的口令,即使是使用 su(1) 命令。 例如,确信你的pty终端在 /etc/ttys 文件中被指定为 insecure (不安全),这将使直接通 过 telnetrlogin 登陆 root 会不被接受。 如果使用如 sshd 这样的其他登陆服务,确认直接登陆root也被关闭了。你可以通过 编辑 /etc/ssh/sshd_config 文件来做到这一点,确信 PermitRootLogin 被设置成 NO 。考虑到每一种访问方法 - 如FTP这样的服务,以免因为它们而导致安全性的损失。直接登陆 root 只有通过系统控制台 才被允许。

当然,作为一个系统管理员,你应当获得 root 身份,因此,我们开了一些后门来 允许自己进入。但这些后门只有在经过了额外的口令确认之后才能够进入。 一种让 root 可访问的方法是增加适当的用户帐户到 wheel 组 (在 /etc/group 中)。wheel 组中的用户成员可以使用 su 命令来成为 root 。绝对不应该通过在口令项中进行设置来赋予维护人员天然的 wheel 组成员身份。维护人员应被放置在 staff 组中,然后通过 wheel 文件加入到 /etc/group 组。事实上,那些需要以 root 身份进行操作的用户应被放到 wheel 组中。当然,也可以通过 某种其它的验证手段,例如 Kerberos,可以通过 root 帐户中的 .k5login 文件来允许执行 ksu(1) 成为 root ,而不必把它们放进 wheel 组。 这可能是一种更好的解决方案,因为 wheel 机制仍然可能导致入侵者获得 root ,如果他拿到了口令文件,并能够进入职员的帐户。 尽管有 wheel 比什么都没有要强一些,但它并不是一种绝对安全的办法。

一种间接地提高员工帐号,以及 root 访问的方法,使采用其他的登录访问方式,并使用 ``星号'' 替代员工加密的口令。使用 vipw(8) 命令,可以把每一个加密的口令 替换成一个 ``*'' 符。 这将更新 /etc/master.passwd 文件,以及 用户名/口令数据库,以禁用口令登录。

如下面的员工帐号

foobar:R9DT/Fa1/LV9U:1000:1000::0:0:Foo Bar:/home/foobar:/usr/local/bin/tcsh

应被改为:

foobar:*:1000:1000::0:0:Foo Bar:/home/foobar:/usr/local/bin/tcsh

这一更改将阻止一般的登录,因为加密的口令永远不会与 ``*'' 匹配。一旦这么做之后, 任何员工都必须使用其他的方式来完成登录,例如,使用 kerberos(1) 或者通过 ssh(1) 利用 公钥/密钥对 的方式 来完成登录。当使用 Kerberos 这样的工具时,通常必须加强运行 Kerberos 的服务器,以及桌面工作站的安全性。当使用 公钥/密钥对 以 ssh 登录时,通常必须加固用户 开始 登录的那台机器的安全 (通常这是他们的工作站)。在这之上还可以增加 一层安全性,即在使用 ssh-keygen(1) 生成它的时候,使用口令 来保护它们。如果能够用 ``星号'' 替换掉所有员工的口令, 那么,这也就保证了他们只能通过你设置的安全的方法来登录。这将迫使 所有的员工使用安全的、经过加密的连接来完成他们的会话,而这将使得 入侵者通过监听网络通讯,从某些不相关的、不太安全的机器上窃取口令 成为不可能。

另一钟间接的安全机制则是,从严格受限的机器向限制更宽松的机器 上登录。例如,如果你的服务器运行了所有的服务,那么,工作站应该什么都 不运行。为了让工作站尽可能地安全,应该避免运行任何没有必要的服务,甚 至不运行任何服务。另外,也应该考虑使用带口令保护功能的屏幕保护程序。 毋庸置疑,如果攻击者能够物理地接触你的工作站,那么他就有能力破坏任何 安全设施,这确实是我们需要考虑的一个问题,但同样地,真正能够物理接触 你的工作站或服务器并实施攻击的人在现实生活中并不常见,绝大多数攻击来 自于网络,而攻击者往往无法物理地接触服务器或工作站。

使用类似 Kerberos 这样的工具,也为我们提供了使用一个工具来禁 用某个用户,或修改他们口令并在所有机器上立即生效的方法。如果员工的帐 号被窃取,能够在所有的其他机器上生效的口令变更将很有意义。如果口令分 散地保存在多个机器上,一次修改 N 台机器上的口令很可能是一件痛苦的事 情。此外, Kerberos 还能够提供更多的限制,除了 Kerberos 令牌有很好的 过期机制之外,它还能够强制用户在某个特定的期限内修改口令(比如说,每 月一次).

10.3.2. 确保以root用户权限运行的服务器和suid/sgid可执行程序的安全

谨慎的管理员只运行它们需要的服务,不多,不少。要当心第三方 的服务程序很可能有更多的问题。例如,运行旧版的 imapd 或者 popper 无异于将 root 令牌拱手送给整个世界上的攻击者。 永远不要运行那些你没有仔细检查过的服务程序,许多服务程序并不需要以 root 身份运行。例如,ntalkcomsat,以及 finger 服务程序都能够以一种称作 沙盒 的特殊用户身份运行。除非你解决了大量的 麻烦,否则沙盒远不是一个完美的策略,但洋葱规则仍然成立,如果某个人 设法攻破了在沙盒中运行的程序,那么他们还必须冲出沙盒才能够做的更多。 攻击者需要冲破的层次越多,他成功的机会就越小。root漏洞曾经在几乎所有 的以root身份运行的程序中存在,包括基本的系统服 务。另外,如果你只通过 sshd 登录,而不打算使用 telnetdrshdrlogind,那么,毫不犹豫地关闭这些服务!

FreeBSD 现在默认在沙盒中运行 ntalkd, comsat, 以及 finger。此外, named(8) 也可以这样运行。 /etc/defaults/rc.conf 中包括了如何如此运行 named 的方法,只是这些内容被注释掉了。 如何升级或安装系统将决定这些沙盒所使用的特殊用户是否被自动安装。 谨慎的系统管理员将根据需要研究并实现沙盒。

此外,还有一些服务通常并不在沙盒中运行: sendmail, popper, imapd, ftpd, 以及一些其他的服务。当然,它们有一些替代品,但安装那些服务可能需要 做更多额外的工作。可能必须以 root 身份运行这些 程序,并通过其他机制来检测入侵。

系统中另一个比较大的 root 漏洞 是安装在其中的 suid-root 和 sgid 的可执行文件。绝大多数这类程序, 例如 rlogin, 被存放于 /bin, /sbin, /usr/bin, 或 /usr/sbin 中。 尽管并没有 100% 的安全保证,但系统默认的 suid 和 sgid 可执行文件 通常是相对安全的。当然,偶尔也会发现一些存在于这些可执行文件中的 root 漏洞。1998年,Xlib 中发现了一处 root 漏洞,这使得 xterm (通常是做了suid的) 变得可以入侵。 安全通常比时候沮丧更好,因此,谨慎的管理员通常会限制 suid 可执行 文件,并保证只有员工帐号能够执行它们,或只开放给特定的用户组,甚 至彻底干掉 (chmod 000) 任何 suid 可执行文件, 以至于没有人能够执行它们。没有显示设备的服务器通常不会需要 xterm 可执行文件。sgid 可执行文件通常 同样地危险。一旦入侵者攻克了sgid-kmem,那么他就能够读取 /dev/kmem 并进而读取经过加密的口令文件,从而 窃取任何包含口令的帐号。另外,攻破了 kmem 的入 侵者能够监视通过 pty 传送的按键序列,即使用户使用的是安全的登录方 式。攻破了 tty 组的用户则能够向几乎所有用 户的 tty 写入数据。如果用户正在运行一个终端程序,或包含了键盘模拟 功能的终端仿真程序,那么,入侵者能够以那个用户的身份执行任何命令。

10.3.3. 确保用户帐户的安全

用户帐号的安全通常是最难保证的。虽然你可以为你的员工设置 严苛的登录限制,并用 ``星号'' 剃掉他们的口令,但你可 能无法对普通的用户这么做。如果有足够的决策权,那么在保证用户帐号 安全的斗争中或许会处于优势,但如果不是这样,你能做的只是警惕地监 控这些帐号的异动。让用户使用 ssh 或 Kerberos 可能会有更多的问题, 因为需要更多的管理和技术支持,尽管如此,与使用加密的口令文件相比, 这仍不失为一个好办法。

10.3.4. 确保口令文件的安全

尽可能使用 * 替换掉口令是保证口令文件安 全唯一的解决方法,如果能够用 ssh 或 Kerberos 的话。即使只有 root 用户能够读取加密过的口令文件 (/etc/spwd.db) 入侵者仍然可能设法读到它,即使他暂时还无法写入这个文件。

你的安全脚本应该经常检查并报告口令文件的异动 (参见后面的 检查文件完整性 一节)。

10.3.5. 确保内核中内核设备、直接访问设备和文件系统的安全

如果攻击者已经拿到了 root 那么他就有能力作任何 事情,当然,有一些事情是他们比较喜欢干的。例如,绝大多数现代的内核都包括 一个内建的听包设备。在 FreeBSD 中,这个设备被称作 bpf 。攻击者通常会尝试在攻克的系统上运行它。 如果你不需要 bpf 设备提供的功能,那么,就不要把它编 入内核

但即使已经关掉了 bpf 设备,你仍然需要担心 /dev/mem/dev/kmem 。 就事论事地说,攻击者仍然能够通过直接访问的方式写入磁盘设备。同样地, 还有一个被称作模块加载器, kldload(8) 的机制,也会包含潜在的危险。 尝试入侵企业网络的入侵者会尝试在正在运行的内核上安装他自己的 bpf 设备,或其他听包设备。为了防止这些问题, 需要抬高内核安全级,至少调整到1。可以通过对 kern.securelevel 执行 sysctl 来完成这个任务。一旦把安全级调整到1, 对于直接访问设备的写入操作将被拒绝,而特殊的 chflags 标记,如 schg ,将被强制执行。一定要在重要的启动执 行文件、目录和脚本文件上设置 schg 标记 - 在 安全级生效之前的所有文件。这可能做得有些过火,并将导致在较高安全级上 运行时升级系统变得困难。此外,运行于较高安全级上,而没有正确设置 schg 标记的系统仍然是存在弱点的。另一种方法是把 //usr 以只读方式挂接。 此外,请注意过于严苛的安全设置将使得入侵检测同样无法进行。

10.3.6. 检查文件完整性: 可执行文件,配置文件和其他文件

当实施严格的限制时,往往会在使用的方便性上付出代价。例如,使用 chflags 来把 schg 标记 应用到 //usr 中的绝大多数文件上可能会起到反作用,因为 尽管它能够保护那些文件,但同样关掉了一个很好的监测机制。层次化安全 的最后一层可能是最重要的 - 检测。安全的其他部分可能相对来讲 意义并不那么大 (或者,更糟糕的事情是,那些措施会给你安全的假象), 加入你无法检测潜在的入侵事件。层次化安全最重要的功能是减缓入侵者, 而不是彻底不让他们入侵,这能够让检测起到作用,并更好地捕捉入侵行为。

检测入侵的一种好办法是查找那些被修改、删除或添加的文件。检测 文件修改的最佳方法是与某个 (通常是中央的) 受限访问的系统上的文件进行 比对。在一台严格限制访问的系统上撰写你的安全脚本通常不能够被入侵者察 觉,因此,这非常重要。为了最大限度地发挥这一策略的优势,通常会使用只 读的NFS,或者设置 ssh 钥匙对以便为其他机器提供访问。除了网络交互之外, NFS可能是一种很难被察觉的方法 - 它允许你监控每一台客户机上的文件系统, 而这种监控几乎是无法察觉的。如果一台严格受限的服务器和客户机是通过交 换机连接的,那么 NFS 将是一种非常好的方式。不过,如果那台监控服务器 和客户机之间通过集线器(Hub),或经过许多层的路由来连接,则这种方式就 很不安全了,此时,应考虑使用 ssh ,即使这可以在审计记录中查到。

Once you give a limited-access box, at least read access to the client systems it is supposed to monitor, you must write scripts to do the actual monitoring. Given an NFS mount, you can write scripts out of simple system utilities such as find(1) and md5(1). It is best to physically md5 the client-box files at least once a day, and to test control files such as those found in /etc and /usr/local/etc even more often. When mismatches are found, relative to the base md5 information the limited-access machine knows is valid, it should scream at a sysadmin to go check it out. A good security script will also check for inappropriate suid binaries and for new or deleted files on system partitions such as / and /usr.

When using ssh rather than NFS, writing the security script is much more difficult. You essentially have to scp the scripts to the client box in order to run them, making them visible, and for safety you also need to scp the binaries (such as find) that those scripts use. The ssh client on the client box may already be compromised. All in all, using ssh may be necessary when running over insecure links, but it is also a lot harder to deal with.

A good security script will also check for changes to user and staff members access configuration files: .rhosts, .shosts, .ssh/authorized_keys and so forth... files that might fall outside the purview of the MD5 check.

If you have a huge amount of user disk space, it may take too long to run through every file on those partitions. In this case, setting mount flags to disallow suid binaries and devices on those partitions is a good idea. The nodev and nosuid options (see mount(8)) are what you want to look into. You should probably scan them anyway, at least once a week, since the object of this layer is to detect a break-in whether or not the break-in is effective.

Process accounting (see accton(8)) is a relatively low-overhead feature of the operating system which might help as a post-break-in evaluation mechanism. It is especially useful in tracking down how an intruder has actually broken into a system, assuming the file is still intact after the break-in occurs.

Finally, security scripts should process the log files, and the logs themselves should be generated in as secure a manner as possible - remote syslog can be very useful. An intruder tries to cover his tracks, and log files are critical to the sysadmin trying to track down the time and method of the initial break-in. One way to keep a permanent record of the log files is to run the system console to a serial port and collect the information on a continuing basis through a secure machine monitoring the consoles.

10.3.7. Paranoia

A little paranoia never hurts. As a rule, a sysadmin can add any number of security features, as long as they do not effect convenience, and can add security features that do effect convenience with some added thought. Even more importantly, a security administrator should mix it up a bit - if you use recommendations such as those given by this document verbatim, you give away your methodologies to the prospective attacker who also has access to this document.

10.3.8. Denial of Service Attacks

This section covers Denial of Service attacks. A DoS attack is typically a packet attack. While there is not much you can do about modern spoofed packet attacks that saturate your network, you can generally limit the damage by ensuring that the attacks cannot take down your servers.

  1. Limiting server forks.

  2. Limiting springboard attacks (ICMP response attacks, ping broadcast, etc.).

  3. Kernel Route Cache.

A common DoS attack is against a forking server that attempts to cause the server to eat processes, file descriptors, and memory, until the machine dies. inetd (see inetd(8)) has several options to limit this sort of attack. It should be noted that while it is possible to prevent a machine from going down, it is not generally possible to prevent a service from being disrupted by the attack. Read the inetd manual page carefully and pay specific attention to the -c, -C, and -R options. Note that spoofed-IP attacks will circumvent the -C option to inetd, so typically a combination of options must be used. Some standalone servers have self-fork-limitation parameters.

Sendmail has its -OMaxDaemonChildren option, which tends to work much better than trying to use sendmail's load limiting options due to the load lag. You should specify a MaxDaemonChildren parameter, when you start sendmail, high enough to handle your expected load, but not so high that the computer cannot handle that number of sendmails without falling on its face. It is also prudent to run sendmail in queued mode (-ODeliveryMode=queued) and to run the daemon (sendmail -bd) separate from the queue-runs (sendmail -q15m). If you still want real-time delivery you can run the queue at a much lower interval, such as -q1m, but be sure to specify a reasonable MaxDaemonChildren option for that sendmail to prevent cascade failures.

Syslogd can be attacked directly and it is strongly recommended that you use the -s option whenever possible, and the -a option otherwise.

You should also be fairly careful with connect-back services such as tcpwrapper's reverse-identd, which can be attacked directly. You generally do not want to use the reverse-ident feature of tcpwrappers for this reason.

It is a very good idea to protect internal services from external access by firewalling them off at your border routers. The idea here is to prevent saturation attacks from outside your LAN, not so much to protect internal services from network-based root compromise. Always configure an exclusive firewall, i.e., ``firewall everything except ports A, B, C, D, and M-Z''. This way you can firewall off all of your low ports except for certain specific services such as named (if you are primary for a zone), ntalkd, sendmail, and other Internet-accessible services. If you try to configure the firewall the other way - as an inclusive or permissive firewall, there is a good chance that you will forget to ``close'' a couple of services, or that you will add a new internal service and forget to update the firewall. You can still open up the high-numbered port range on the firewall, to allow permissive-like operation, without compromising your low ports. Also take note that FreeBSD allows you to control the range of port numbers used for dynamic binding, via the various net.inet.ip.portrange sysctl's (sysctl -a | fgrep portrange), which can also ease the complexity of your firewall's configuration. For example, you might use a normal first/last range of 4000 to 5000, and a hiport range of 49152 to 65535, then block off everything under 4000 in your firewall (except for certain specific Internet-accessible ports, of course).

Another common DoS attack is called a springboard attack - to attack a server in a manner that causes the server to generate responses which overloads the server, the local network, or some other machine. The most common attack of this nature is the ICMP ping broadcast attack. The attacker spoofs ping packets sent to your LAN's broadcast address with the source IP address set to the actual machine they wish to attack. If your border routers are not configured to stomp on ping's to broadcast addresses, your LAN winds up generating sufficient responses to the spoofed source address to saturate the victim, especially when the attacker uses the same trick on several dozen broadcast addresses over several dozen different networks at once. Broadcast attacks of over a hundred and twenty megabits have been measured. A second common springboard attack is against the ICMP error reporting system. By constructing packets that generate ICMP error responses, an attacker can saturate a server's incoming network and cause the server to saturate its outgoing network with ICMP responses. This type of attack can also crash the server by running it out of mbuf's, especially if the server cannot drain the ICMP responses it generates fast enough. The FreeBSD kernel has a new kernel compile option called ICMP_BANDLIM which limits the effectiveness of these sorts of attacks. The last major class of springboard attacks is related to certain internal inetd services such as the udp echo service. An attacker simply spoofs a UDP packet with the source address being server A's echo port, and the destination address being server B's echo port, where server A and B are both on your LAN. The two servers then bounce this one packet back and forth between each other. The attacker can overload both servers and their LANs simply by injecting a few packets in this manner. Similar problems exist with the internal chargen port. A competent sysadmin will turn off all of these inetd-internal test services.

Spoofed packet attacks may also be used to overload the kernel route cache. Refer to the net.inet.ip.rtexpire, rtminexpire, and rtmaxcache sysctl parameters. A spoofed packet attack that uses a random source IP will cause the kernel to generate a temporary cached route in the route table, viewable with netstat -rna | fgrep W3. These routes typically timeout in 1600 seconds or so. If the kernel detects that the cached route table has gotten too big it will dynamically reduce the rtexpire but will never decrease it to less than rtminexpire. There are two problems:

  1. The kernel does not react quickly enough when a lightly loaded server is suddenly attacked.

  2. The rtminexpire is not low enough for the kernel to survive a sustained attack.

If your servers are connected to the Internet via a T3 or better, it may be prudent to manually override both rtexpire and rtminexpire via sysctl(8). Never set either parameter to zero (unless you want to crash the machine). Setting both parameters to 2 seconds should be sufficient to protect the route table from attack.

10.3.9. Access Issues with Kerberos and SSH

There are a few issues with both Kerberos and ssh that need to be addressed if you intend to use them. Kerberos V is an excellent authentication protocol, but there are bugs in the kerberized telnet and rlogin applications that make them unsuitable for dealing with binary streams. Also, by default Kerberos does not encrypt a session unless you use the -x option. ssh encrypts everything by default.

ssh works quite well in every respect except that it forwards encryption keys by default. What this means is that if you have a secure workstation holding keys that give you access to the rest of the system, and you ssh to an insecure machine, your keys are usable. The actual keys themselves are not exposed, but ssh installs a forwarding port for the duration of your login, and if an attacker has broken root on the insecure machine he can utilize that port to use your keys to gain access to any other machine that your keys unlock.

We recommend that you use ssh in combination with Kerberos whenever possible for staff logins. ssh can be compiled with Kerberos support. This reduces your reliance on potentially exposable ssh keys while at the same time protecting passwords via Kerberos. ssh keys should only be used for automated tasks from secure machines (something that Kerberos is unsuited to do). We also recommend that you either turn off key-forwarding in the ssh configuration, or that you make use of the from=IP/DOMAIN option that ssh allows in its authorized_keys file to make the key only usable to entities logging in from specific machines.