Linux服务器JDK加固核心是切断被滥用路径:严格限制安装目录权限(755且root属主)、为Java服务配置专用低权限systemd用户、全局管控JAVA_HOME与PATH、禁用危险JVM参数,并持续验证执行来源。

Linux服务器上JDK本身不是服务进程,不直接暴露网络或监听端口,但它的安装路径、运行权限、环境变量和调用方式会显著影响系统整体安全。加固核心不是“给JDK加密码”,而是切断它被滥用的路径——尤其是防止低权限用户提权、劫持Java命令、篡改JRE或利用JAVA_HOME污染执行环境。
为什么不能让普通用户写入JDK安装目录
JDK安装目录(如/usr/lib/jvm/java-17-openjdk-amd64)若对other或group开放写权限,攻击者可替换java二进制、注入恶意libjli.so、或篡改rt.jar等核心类库,实现任意代码执行。即使JDK以非root用户运行,一旦JVM被劫持,后续加载的Java应用(如Tomcat、Spring Boot)也会继承该风险。
实操建议:
- 检查安装目录权限:
ls -ld /usr/lib/jvm/java-17-openjdk-amd64,应为drwxr-xr-x root:root(即755且属主为root) - 禁止非root用户写入:
sudo chmod 755 /usr/lib/jvm/java-17-openjdk-amd64,再确认子目录无world-writable文件:find /usr/lib/jvm/java-17-openjdk-amd64 -type f -perm -002 - 若使用手动解压安装(如
/opt/jdk-17.0.9),同样需chown -R root:root并chmod -R go-w
systemd服务中运行Java应用时的User/Group设置
很多Java应用(如Elasticsearch、ZooKeeper、自研Spring Boot服务)通过systemd托管,但默认常以root或daemon用户启动。这违反最小权限原则:JVM不需要读/etc/shadow、不需要加载内核模块、更不该能修改/var/log以外的日志路径。
实操建议:
- 为每个Java服务创建专用低权限用户:
sudo useradd -r -s /sbin/nologin -d /opt/myapp myapp - 在
/etc/systemd/system/myapp.service中强制指定:User=myapp、Group=myapp - 若需绑定80/443端口,**不要**改用
root,而应添加能力:CapabilityBoundingSet=CAP_NET_BIND_SERVICE+AmbientCapabilities=CAP_NET_BIND_SERVICE - 禁用危险能力:
CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SYS_MODULE CAP_AUDIT_WRITE
JAVA_HOME和PATH环境变量的风险控制
JAVA_HOME若被普通用户写入(如设在/home/deploy/.bashrc中),或PATH中包含用户可控路径(如~/bin),攻击者可通过伪造java脚本劫持所有Java调用。尤其当sudoers配置了NOPASSWD: ALL时,风险极高。
实操建议:
- 全局
JAVA_HOME应设在/etc/profile.d/java.sh,由root维护,权限644;内容仅含export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 - 避免在
~/.bashrc或~/.profile中覆盖JAVA_HOME,尤其禁止指向/tmp或/home下用户可写路径 - 检查
PATH顺序:echo $PATH中,JDK的bin目录(如/usr/lib/jvm/java-17-openjdk-amd64/bin)必须排在用户家目录路径之前 - 对需要sudo执行Java命令的场景,用
env_reset限制环境变量传递:Defaults env_reset+Defaults env_keep += "JAVA_HOME"(需显式白名单)
Java应用自身启动参数的安全补充
JDK提供多个JVM参数可用于限制运行时行为,这对防御内存马、反射调用、动态类加载等攻击有实际价值。这些参数需在应用启动脚本(如start.sh)或systemd的ExecStart中显式声明,不能依赖默认值。
实操建议:
- 禁用JNI本地库加载(除非明确需要):
-Djna.nosys=true -Djna.loaded=false - 限制类路径扫描范围:
-Djava.ext.dirs=(清空)+-Djava.endorsed.dirs=(清空) - 关闭JVM Attach机制(防arthas、greys等):
-XX:+DisableAttachMechanism - 启用SecurityManager已废弃,但可配合
--add-opens最小化模块开放,例如:--add-opens java.base/java.lang=ALL-UNNAMED(按需放开,不盲目ALL-UNNAMED)
最容易被忽略的是:JDK加固不是一次性的“装完就完”。当运维人员用sudo -u appuser bash切到应用用户后,再执行java -version,这个java到底来自哪?是系统PATH里的,还是用户自己编译的?有没有被alias重定义?这些细节在应急响应时往往就是突破口。


















