3台服务器跑JSP挂马惊魂,对比评测后我找到了安全方案
上周凌晨三点,江苏南京一位做医疗器械官网的老张给我打电话,声音都在抖。他的网站突然弹窗下载木马,后台全是乱码,客户投诉电话被打爆。他慌了,问我是不是服务器被黑挂马了?别慌,这种事儿在JSP项目里太常见了,尤其是那些用老旧Tomcat版本、没做权限隔离的服务器。很多新手以为买个云服务器就能高枕无忧,结果上线三个月就被植入后门。今天我就拿最近帮三个客户做的JSP站做对比评测,从环境搭建到安全加固,手把手教你在服务器上把JSP网站跑得稳、跑得快、跑得安全。
需求分析与技术选型避坑
很多站长一上来就问“JSP用什么服务器好”,这问题本身就问歪了。JSP是Java Server Pages,核心是运行在JVM上,所以选型关键不是看CPU多强,而是看JDK版本兼容性、内存分配效率和安全补丁响应速度。
老张之前的服务器是CentOS 7 + JDK 1.8 + Tomcat 8.5旧版。这配置在2023年之前还行,但现在漏洞库全公开了,攻击者用扫描器一抓一个准。我给他做了个简单对比:
| 配置项 | 原服务器 | 新方案 | 风险点 |
|---|---|---|---|
| 操作系统 | CentOS 7 | Rocky Linux 9 | CentOS 7已停更,无安全补丁 |
| JDK版本 | 1.8.0_291 | 11.0.20 | 8u版本高危CVE-2023-21916 |
| 容器 | Tomcat 8.5.70 | Tomcat 10.1.18 | 8.5对Java 11支持不全 |
| 防火墙 | 仅安全组 | iptables+安全组双层 | 单靠云安全组无法防端口扫描 |
江苏这边不少中小型企业还在用CentOS 7,因为运维习惯和文档多。但说实话,从百度搜索资源平台发布的安全通告看,2023年下半年针对旧版Linux内核的利用代码已经在黑产市场流通。别等网站被黑挂马了再换系统,那代价是域名权重清零、数据恢复费用、客户流失。
选型建议:
- 操作系统:优先选有长期支持(LTS)的发行版,如Ubuntu 22.04 LTS或Rocky Linux 9。避免使用已停止维护的版本。
- JDK版本:JSP项目强烈建议JDK 11或17。JDK 8虽兼容老代码,但安全更新周期短,且新框架(如Spring Boot 3)不再支持。
- Tomcat版本:至少用10.x系列,它原生支持Servlet 5.0和Java 11+,性能优化明显,且社区活跃度高。
环境准备与基础加固
环境准备不是简单装个JDK就完事。老张的服务器之所以被黑,很大原因是初始配置太“裸奔”。SSH默认端口22、root远程登录开启、Tomcat管理后台暴露在公网,这些都是送人头行为。
我在江苏部署JSP站时,有一套标准初始化流程,必须严格执行:
- 修改SSH端口:默认22端口是扫描器首选目标。改成高位端口如2222,并在
/etc/ssh/sshd_config中设置PermitRootLogin no,强制使用密钥登录。 - 关闭不必要服务:用
systemctl disable关掉telnet、rshd等服务。JSP网站只需要HTTP/HTTPS和SSH。 - 文件权限收紧:Tomcat运行用户不要用root,创建专用用户
tomcat。/opt/tomcat目录权限设为750,日志目录750,确保Web应用无法写入系统关键路径。
这里有个细节很多人忽略:JSP编译临时文件目录。Tomcat默认把编译后的class文件放在work目录,如果权限过宽,攻击者上传恶意JSP后可直接执行。务必确保work目录只有tomcat用户可写,其他用户只读。
核心步骤:从代码到部署
下面以Spring Boot打包的JSP应用为例,演示在Rocky Linux 9上的完整部署流程。假设项目已构建为app.jar。
第一步:上传并配置JDK
# 创建安装目录
mkdir -p /opt/java && cd /opt/java# 下载OpenJDK 11 (示例URL,实际需根据架构选择)
wget https://download.java.net/java/GA/jdk11.0.20/891b685f4c2f4a3191d6176f08c35f38/17/openjdk-11.0.20_linux-x64_bin.tar.gz# 解压并配置环境变量
tar -xzf openjdk-11.0.20_linux-x64_bin.tar.gz
mv jdk-11.0.20 jdk11# 添加到环境变量 (写入/etc/profile.d/java.sh)
echo 'export JAVA_HOME=/opt/java/jdk11' > /etc/profile.d/java.sh
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> /etc/profile.d/java.sh
source /etc/profile.d/java.sh# 验证安装
java -version
# 预期输出: openjdk version "11.0.20" 2023-07-18
第二步:部署Tomcat与应用
# 创建tomcat用户
useradd -r -m -d /opt/tomcat tomcat# 下载Tomcat 10.1.18
cd /opt
wget https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.18/bin/apache-tomcat-10.1.18.tar.gz
tar -xzf apache-tomcat-10.1.18.tar.gz
mv apache-tomcat-10.1.18 tomcat
chown -R tomcat:tomcat /opt/tomcat# 复制应用jar到webapps
cp /home/app.jar /opt/tomcat/webapps/# 关键配置: server.xml 中配置Connector,启用HTTP/2和SSL
# 编辑 /opt/tomcat/conf/server.xml
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"maxThreads="150" connectionTimeout="20000"scheme="http" secure="false"useBodyEncodingForURI="true"redirectPort="8443" />
第三步:启动与监控
# 启动Tomcat
sudo -u tomcat /opt/tomcat/bin/startup.sh# 查看日志确认启动成功
tail -f /opt/tomcat/logs/catalina.out# 设置开机自启 (systemd服务文件)
cat > /etc/systemd/system/tomcat.service <<EOF
[Unit]
Description=Apache Tomcat Server
After=network.target[Service]
User=tomcat
Group=tomcat
Environment="JAVA_HOME=/opt/java/jdk11"
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh
Restart=on-failure[Install]
WantedBy=multi-user.target
EOFsystemctl daemon-reload
systemctl enable tomcat
systemctl start tomcat
注意:ExecStart用startup.sh而非catalina.sh run,因为后者会阻塞前台,不适合systemd管理。如需实时日志,建议在logging.properties中配置日志滚动策略,避免日志文件过大撑爆磁盘。
代码示例:安全过滤器与异常处理
很多JSP网站被黑挂马,根源是代码层面缺乏输入验证和异常泄露。下面两个代码片段,必须加到项目中。
示例1:全局异常处理器,防止堆栈信息泄露
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;@ControllerAdvice
public class GlobalExceptionHandler {private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);@ExceptionHandler(Exception.class)public ModelAndView handleException(Exception e) {// 关键: 只记录到服务端日志,不向前端返回任何敏感信息logger.error("Server Error", e);ModelAndView mv = new ModelAndView();mv.setViewName("error/500"); // 自定义错误页面mv.addObject("message", "系统繁忙,请稍后再试");return mv;}
}
示例2:JSP输入验证,防止XSS攻击
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%// 关键: 对参数进行HTML转义,防止脚本注入String name = request.getParameter("name");if (name != null) {name = name.replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);}
%>
<html>
<body><h1>欢迎, <%= name %></h1><!-- 错误写法: <h1>欢迎, <%= request.getParameter("name") %></h1> -->
</body>
</html>
别小看这两段代码。老张的网站被挂马,就是因为某个后台管理页面的JSP直接拼接了未过滤的SQL参数,攻击者通过union select拖库,再植入Webshell。加上全局异常处理,至少不会把数据库连接串、服务器路径泄露给攻击者。
常见报错与故障排查
部署过程中,90%的问题集中在JDK版本不匹配、端口冲突和权限不足。
- 报错:
UnsupportedClassVersionError: class file version 61.0- 原因:代码用JDK 17编译,但服务器运行JDK 11。
- 解决:统一JDK版本,或在Maven中指定
<maven.compiler.source>11</maven.compiler.source>。
- 报错:
Access denied或Permission denied- 原因:Tomcat用户无权限写入
work、temp目录。 - 解决:检查
chown -R tomcat:tomcat /opt/tomcat是否执行,确认server.xml中unpackWARs="true"时目标目录可写。
- 原因:Tomcat用户无权限写入
- 报错:
Address already in use- 原因:8080端口被其他进程占用。
- 解决:
netstat -tlnp | grep 8080找出进程,杀掉或修改Tomcat端口。
如果网站被黑挂马,不要急着重装系统。先用find / -name "*.jsp" -mtime -1查找最近修改的JSP文件,检查是否有异常脚本。用netstat -antp查看异常外联连接。保留日志证据,必要时联系云服务商安全团队。
小结:安全是底线,不是选项
服务器做JSP网站教程的核心,不是教你怎么把代码跑起来,而是教你怎么让它跑在安全的环境里。老张的新站上线三个月,零事故。这不是运气,是环境加固+代码规范+持续监控的结果。
江苏这边很多建站公司还在用“模板+快速上线”的思路,把安全当售后问题。但现实是,一旦网站被黑挂马,搜索引擎会直接降权甚至除名,百度搜索资源平台也会标记为“不安全网站”。修复权重的成本,远高于前期多花两天做安全加固。
做JSP站,记住三件事:JDK别用太老的,Tomcat别用停更的,代码别裸奔。这三条做到位,至少能避开80%的安全风险。
还有什么建站疑问?评论区留言挨个回。特别是JSP迁移到Spring Boot、或者老系统安全加固的问题,我最近正好在整理案例,可以针对性分享。


