一台多人共用的生产服务器,某天被人执行了rm -rf误删了数据,追查时敲下history命令,看到的只有一串光秃秃的命令文本——没有时间、没有用户、没有来源IP,甚至因为shell退出时才写入文件,最后几条操作已经丢失。这就是CentOS默认history机制的真实短板。默认配置下,历史命令不带时间戳,多个终端会互相覆盖记录,用户可以随手敲一行history -c清空一切,审计价值几乎为零。本文从时间戳、实时落盘、权限加固三个层面,给出一套完整的历史命令安全审计配置方案。

一、给历史命令打上时间戳:HISTTIMEFORMAT配置
history默认不记录执行时间,根源在于bash并没有把时间信息存进历史文件。要让每条命令都能溯源,第一步是设置HISTTIMEFORMAT环境变量。这个变量的值遵循strftime格式,设置后history输出会在每条命令前追加格式化的时间列。
推荐写入/etc/profile.d/history.sh,这样对所有登录用户全局生效,而不是只改单个用户的~/.bashrc,避免有人漏配:
# /etc/profile.d/history.sh # 历史命令记录格式:日期 时分秒 用户 export HISTTIMEFORMAT="%F %T `whoami` " # 记录条数适当放大,生产建议至少5000 export HISTSIZE=5000 export HISTFILESIZE=5000 # 追加而非覆盖,多个终端共存时不会互相冲掉记录 shopt -s histappend
其中%F等价于年-月-日,%T是时分秒。写入后执行source /etc/profile即可生效,效果如下:
$ history 1001 2024-05-20 14:32:11 root vim /etc/ssh/sshd_config 1002 2024-05-20 14:35:40 root systemctl restart sshd
有两点值得注意。第一,设置HISTTIMEFORMAT之前的历史条目没有时间信息,bash会统一用文件最后一次写入时间填充,所以这些旧记录的时间是不可信的。第二,shopt -s histappend很关键,默认行为是覆盖写入,先开的终端退出时会把后开终端的记录冲掉,审计场景下必须改为追加模式。
二、实时同步到日志文件:防止history被清空
只靠HISTTIMEFORMAT还不够,因为历史记录默认在用户退出shell时才写入~/.bash_history,中途断电、被kill进程都会丢记录,而且用户执行history -c或直接删文件就能销毁痕迹。真正的审计需要命令一执行就落盘,写入到用户改不了的地方。
bash提供了一个冷门但好用的内置变量PROMPT_COMMAND,它会在每次显示提示符之前执行。利用它把命令实时转储到一个所有用户可写但不可删的目录(借助粘滞位),配合chattr加锁,就形成了低成本方案:
# /etc/profile.d/history.sh 追加以下内容
export HISTFILE=/var/log/command_audit/.audit_$(who -u | awk '{print $1"_"$NF}') 2>/dev/null
readonly HISTFILE
readonly HISTSIZE
readonly HISTFILESIZE
readonly HISTTIMEFORMAT
export PROMPT_COMMAND='history -a; history -c; history -r'
history -a把当前会话的新命令立即追加到HISTFILE,history -c清掉内存缓存,history -r再重新读入,这样多终端之间还能共享实时历史。用readonly锁定环境变量,普通用户无法在会话里改掉这些配置。
更稳妥的做法是把命令转发到rsyslog,由日志服务器统一收集,用户在本机根本摸不到原始记录:
# /etc/bashrc 末尾追加 export PROMPT_COMMAND='RETRN_VAL=$?; logger -p local6.info "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" ) [$RETRN_VAL]"' # /etc/rsyslog.conf 中添加 local6.* /var/log/commands.log # 重启服务 systemctl restart rsyslog
logger通过local6 facility把命令发往rsyslog,写入commands.log后建议再对日志做轮转并加chattr +a只允许追加。这种方案的成本是多一次管道调用,对交互体验几乎无感知,但审计链条完整得多:即使用户清空自己的history,日志文件里的记录依然存在。
三、权限加固与全局生效的细节
审计目录本身也要防篡改。创建专门的目录,设置正确权限,并用文件属性锁死:
mkdir -p /var/log/command_audit chmod 1733 /var/log/command_audit # 1733:粘滞位+组和其他可写不可读,用户能写自己的文件,不能看别人的,也不能删别人的 touch /var/log/commands.log chmod 600 /var/log/commands.log chattr +a /var/log/commands.log
粘滞位(1xxx)保证了用户只能删除自己创建的文件,组和其他权限设为只写(-wx)意味着普通用户可以追加自己的记录却无法偷看他人的操作,这在多租户环境下既保护隐私又保证可追溯。chattr +a则让root之外的所有删除、改写尝试都失败。
配置全局生效时还要检查几个容易遗漏的坑。一是用户家目录下的~/.bashrc可能覆盖全局设置,建议在/etc/bashrc里再兜底一份;二是rsyslog方案中logger里的引号嵌套容易写错,history 1配合sed去掉行号这一段建议原样复制;三是如果系统启用了SELinux,logger写入可能被策略拦截,可先用getenforce确认状态,必要时调整策略而不是简单粗暴地关闭SELinux。
最后,定期把/var/log/commands.log通过rsync或日志采集器同步到独立的日志服务器,本机记录只作为兜底,异地留存才是对抗深度入侵的最后防线。三个层面叠加:时间戳保证可读、实时落盘保证不丢、权限加固与异地备份保证不可篡改,一套基础但够用的命令审计体系就搭建完成了。
CentOS安全审计HISTTIMEFORMAThistory命令修改时间:2026-09-04 22:42:45