如何实现Unix套接字对共享库的独占写入权限?
我开发了一个日志应用和一个共享库,该共享库通过Unix套接字向日志应用写入内容。我希望实现仅允许该动态库独占写入日志应用的权限。
我曾考虑通过限制套接字的用户/组写入权限来实现,但调用该动态库的应用(如mate-screensaver)运行时无特殊权限,它由被锁定的用户启动,未设置euid和egid。
我想到可以在日志应用中添加挑战请求机制:日志应用向客户端发送随机数(nonce),客户端需返回正确的HMAC值。但我认为这是一种不太优雅的临时方案,因为密钥生成算法嵌入在二进制文件中,存在被逆向分析的风险。
请问是否有通用的解决方案来解决这类问题?
更新:
我查看了标准syslog机制,发现无特权用户也能写入/var/log/secure,示例代码如下:
#include <syslog.h> int main() { openlog("sudo", 0, LOG_AUTHPRIV); syslog(LOG_ERR, "pam_unix(sudo:auth) authentication failure; logname=..."); }
因此我开始怀疑自己试图解决的问题其实是一个伪问题。
1. 先明确:你的担忧是否真的是问题?
syslog的例子已经说明,无特权用户写入敏感日志文件本身是被允许的,但日志应用会对内容做过滤/校验。比如syslogd会根据LOG_AUTHPRIV等设施级别,结合配置决定是否写入/var/log/secure,同时会记录发送者的进程信息(PID、UID等)。你不需要阻止普通用户连接套接字,而是要在日志应用层面校验写入内容的合法性——只有来自你的共享库的合法日志才被接受,其他非法写入直接丢弃。
2. 针对“仅允许共享库写入”的通用方案
如果确实需要严格限制只有你的共享库能写入,以下几种方案比硬编码密钥更可靠:
- 基于进程属性的校验:
日志应用在接受连接后,通过SO_PEERCRED选项获取客户端进程的UID、GID、PID,然后进一步读取/proc/[PID]/maps文件,检查该进程是否加载了你的共享库(通过匹配库的路径或唯一标识符)。这种方式的核心是:只有加载了你的库的进程才能通过校验,直接伪造连接的进程无法通过。 - 使用Linux的抽象命名空间套接字+SELinux/AppArmor:
把Unix套接字放在抽象命名空间(比如@my-log-socket),然后通过SELinux或AppArmor规则,仅允许加载了你的共享库的进程访问该套接字。这种方式依赖系统的强制访问控制,安全性更高,也不需要硬编码密钥。 - 共享库注入标记:
在共享库初始化时,给进程设置一个唯一的、不易伪造的标记(比如通过线程局部存储或进程的某个特殊内存区域),日志应用在接收连接后,通过进程内存读取这个标记。不过这种方式需要处理进程内存读取的权限问题,实现起来稍复杂。
3. 为什么硬编码HMAC不是好选择?
你担心的逆向风险是真实存在的——只要二进制文件能被拿到,攻击者总能提取出密钥或算法。这种方案的维护成本也高,一旦密钥泄露,所有客户端都要更新,完全不如依赖系统原生的权限机制可靠。
内容的提问来源于stack exchange,提问作者dodik

