跳转到内容

Arch 打包准则/安全

来自 Arch Linux 中文维基
Arch 打包准则

32 位安全CLRCMakeDKMSEclipseElectronFree PascalGNOMEGoHaskellJava交叉编译工具KDELispMesonMinGW内核模块Node.jsNonfreeOCamlPerlPHPPythonRRubyRustShellVCSWebWine字体

本文描述了适用于 Arch Linux 软件包的打包安全指引。对于 C/C++ 项目,可以为编译器和链接器应用安全加固选项。Arch Linux 默认会启用 PIEFORTIFY_SOURCE、栈保护、nxrelro

用法

可以使用 checksec 检查强化加固保护选项:

$ checksec --file=/usr/bin/cat
提示:Namcap 也可以报告本文描述的一些安全问题。

RELRO

RELRO 是用于加固 ELF 二进制文件和进程的一种通用缓解技术。在程序加载时,链接器需要写入几个 ELF 内存区段,但在将控制权交给程序前,可以将这些区段设为只读,由此防止攻击者覆盖掉部分 ELF 区段。有两种 RELRO 模式:

  • 部分 RELRO(-Wl,-z,relro):程序加载后部分区段被设为只读,但 GOT(.got.plt)仍为可写。
  • 完全 RELRO(-Wl,-z,now):程序加载时解析所有动态符号,使得整个 GOT 都可被设为只读。

如果应用汇报使用了部分 RELRO,需检查构建工具是否使用了我们传递的 LDFLAGS,以及是否允许覆写 LDFLAGS。对于 Go 软件包,需检查构建方法是否将 build.go 作为了纯 golang Makefile 替代使用,该方法不允许传递 LDFLAGS。

Haskell

暂不清楚如何为 Haskell 实现完全 RELRO。

Go

参见 Go 语言软件打包准则#Flags and build options

Stack canary

Stack canary(又称栈警惕标志)由编译器添加在栈的缓冲区和控制数据之间。如果该已知值被破坏,就代表出现了缓冲区溢出,运行中的程序就会报出段错误(segfaults),以防止执行潜在的错误代码。

gcc 包默认通过 --enable-default-ssp 编译选项启用了栈保护。

NX

C/C++

可执行空间保护会将内存区域标记为非可执行,使得在该区域执行机器码时会出现报错。该功能使用了类似 NX 位(No-eXecute bit,禁止执行位)的硬件功能,有时也会使用软件模拟这些特性。

PIE

C/C++

gcc 包默认通过 --enable-default-pie 选项为 C/C++ 启用了该特性。

Golang

将以下标志传递给 go build

export GOFLAGS='-buildmode=pie'
export CGO_CPPFLAGS="-D_FORTIFY_SOURCE=3"
export CGO_LDFLAGS="-Wl,-z,relro,-z,now"

Haskell

将以下标志传递给 runhaskell Setup.hs configure

--ghc-option='-pie'

RPATH/RUNPATH

RUNPATH/RPATH 为包含该参数的对象提供了更多的搜索路径(适用于可执行文件和共享对象):

$ objdump -x /usr/bin/perl | grep -E 'RPATH|RUNPATH'

如果 RPATH 的值包含了攻击者可控制的路径,那就有可能通过对应目录下的恶意库执行代码(例如 CVE-2006-1566CVE-2005-4280)。更多信息请参考 Debian:RpathIssue

RPATH 是通过向 LDFLAGS 传递类似 -Wl,-rpath -Wl,/usr/local/lib 的参数,然后由链接器负责设置的。如果要设置 RUNPATH,请将 --enable-new-dtags 添加到链接器标志中。

FORTIFY

Fortify source 是一个宏,可以为执行内存和字符串操作的函数添加缓冲区溢出保护。它会检测是否有攻击者尝试通过负责更多的字节来溢出缓冲区,并停止程序的运行。该保护项已在默认 CPPFLAGS 中启用:

makepkg.conf
CPPFLAGS="-D_FORTIFY_SOURCE=3"

更多信息请参考 makepkg#配置

systemd 服务

此页面或章节适合移动到 systemd/Sandboxing

附注: 内容主题与 systemd#应用程序环境沙盒 相同,两者可被汇总到单独页面,草稿可参考 User:NetSysFire/systemd sandboxing。(在 Talk:Security#systemd unit hardening and system.conf tweaks 讨论)


如果上游不提供 systemd 服务文件,使得需随软件包附带,请检查是否需要使用以下 systemd 服务加固功能。Systemd 提供并为服务启用了分析安全特性的方法:

$ systemd-analyze security reflector.service

文件访问

可以通过限制文件系统访问来加固服务。

为执行的进程配置一个新的文件系统命名空间,并在内部挂载私有 /tmpvar/tmp 目录,使得其不与命名空间外部的进程共享。该方法适用于会向 /tmp 目录写入数据的程序:

PrivateTmp=true

ProtectSystem 参数对于执行中进程有三种只读挂载方式,其中 “full” 选项会将 /usr/boot/etc 目录挂载为只读。ProtectHome 会使进程无法访问 /home/root/run/user 目录:

ProtectSystem=strict
ProtectHome=true

PrivateDevices 会为执行中的进程配置新的 /dev 命名空间,其中只包含类似 /dev/null/dev/zero/dev/random 的 API 伪设备,不包含任何物理设备、系统内存、系统端口和其它设备。该选项可以防止进程直接向物理设备进行写入,systemd 还会为 @raw-io 集添加系统调用过滤器:

PrivateDevices=true

以下选项会使执行中进程无法修改可通过 /proc/sys/sys 等路径访问的内核变量。ProtectControlGroups 会使 /sys/fs/cgroup 层次结构变为只读:

ProtectKernelTunables=true
ProtectControlGroups=true

以下方法可禁止访问部分文件路径:

InaccessiblePaths=/etc

更多信息请参考 systemd.exec(5)

用户

确保执行中的进程和其子进程无法通过 execve(2) 获得新权限:

NoNewPrivileges=true

内存

禁止创建同时为可写和可执行的内存映射、将映射修改为可执行和创建可执行共享内存。该方法会将进程沙箱化,防止攻击者向可执行的内存部分写入数据。注意,该方法与使用 JIT 的应用不兼容:

MemoryDenyWriteExecute=true

系统调用

锁定 personality(2) 系统调用,使内核执行域无法被修改:

LockPersonality=true

还可以限制服务的系统调用,以下命令将显示 systemd 可以过滤的系统调用:

$ systemd-analyze syscall-filter

可以使用预定义组,以下示例为使用推荐的系统服务调用白名单配置起点:

SystemCallFilter=@system-service

还可以按架构来限制系统调用,例如限制在 64 位设备上执行 32 位二进制文件(即非原生架构二进制文件):

SystemCallArchitectures=native

网络

如果进程不需要访问网络,可以为进程配置一个新的网络命名空间,并只配置回环接口,以此完全禁用网络访问:

PrivateNetwork=true

如果需要网络,还可以限制 socket(2) 系统调用可使用的地址族类型,以只允许 UNIX socket 为例:

RestrictAddressFamilies=AF_UNIX

也可以对只需连接到 localhost(本机)或特定 IP 段的情况进行限制,以只允许 localhost 为例:

IPAddressAllow=localhost
IPAddressDeny=any

更多网络过滤相关的信息请参考 systemd.resource-control(5)

其它

为执行的进程配置新的 UTS 命名空间,并禁止修改主机名或域名:

ProtectHostname=true