[TOC]



修订控制页

版本 |修订日期 |修订人 |修订摘要
—|—|—|—
1.0| 2019年9月10日 09点00分 |WeiyiGeek |初稿


1.引言

1.1 目的

为了更好的指导部署Tomcat应用容器,保证服务的安全稳定高性能的运行,需要对其进行加固和优化;
本次进行Tomcat容器调优加固主要从以下几个部分:

  • 内核参数优化
  • 性能参数优化
  • 安全加固配置
1.2 目标范围

本文档仅供内部使用,禁止外传,帮助研发人员,运维人员对系统长期稳定的运行提供技术文档参考。

1.3 读者对象

1) 项目经理
2) 开发人员
3) 测试人员
4) 运维人员
5) 相关领导


2.参考说明

2.1 帮助参考

Apache Tomcat是美国Apache软件基金会的一款轻量级Web应用服务器,该程序实现了对Servlet和JSP的支持。
目前是Apache开源软件组织的一个软件项目它的官网 :http://tomcat.apache.org , 在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试 JSP 程序的首选,默认端口8080。

Tomcat8配置帮助文档: http://tomcat.apache.org/tomcat-8.0-doc/config/
Security Manage: http://tomcat.apache.org/tomcat-8.0-doc/security-manager-howto.html


Tomcat初始目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
/usr/local/tomcat #主目录即安装的目录
├── bin #存放启动和关闭Tomcat的脚本文件
├── conf #存放Tomcat服务器的各种配置文件(后面单介绍)
├── lib #存放tomcat服务器支撑的jar包
├── logs #存放Tomcat的日志文件
├── temp #存放Tomcat运行时产生的临时文件
├── webapps #web应用虽在目录,即供外界访问的web资源的存放目录,就是Web发布目录
│   ├── docs #帮助文档目录
│   ├── examples #Servlet and JSP Examples案例目录
│   ├── host-manager #主机管理目录
│   ├── manager #应用程序管理目录
│   └── ROOT #应用程序放置地址
└── work #Tomcat的工作目录,存放jsp编译后产生的class文件


Tomcat全局配置目录结构:

1
2
3
4
5
6
7
8
9
10
11
[[email protected] conf]# tree
├── catalina.policy #权限控制配置文件
├── catalina.properties #Tomcat属性配置文件
├── context.xml #上下文配置文件(selinux)
├── jaspic-providers.xml
├── jaspic-providers.xsd
├── logging.properties #日志log相关配置文件
├── server.xml #主配置文件(如修改监听端口,开启关闭额外的服务,设置网站根目录,虚拟主机,开启https等功能)
├── tomcat-users.xml #manager-gui管理用户配置文件(Tomcat安装后生成的管理界面,该文件可开启访问)
├── tomcat-users.xsd
└── web.xml #Tomcat的servlet,servlet-mapping,filter,MIME等相关配置


Tomcat的JavaWeb应用的组成结构:

1
2
3
4
5
6
7
8
mail--- #-Web应用所在目录
|---- #html、jsp、css、js等文件,根目录下的文件外界可以直接访问
|---- #WEB-INF目录 (应用配置文件存放)
|---------classes目录 #(java类)
|---------lib目录 #(java类运行所需的jar包)
|---------conf目录 #(数据库以及其他配置文件存放目录)
|---------web.xml #(web应用的配置文件)
#注意:WEB-INF 这个目录下的文件外界无法直接访问,由web服务器负责调用


Tomcat默认开发的端口介绍:

  • 8005:用于SHUTDOWN指令来关闭Tomcat时使用;
  • 8009:用于Apache连接Tomcat时候专用端口采用AJP协议;
  • 8080:用于HTTP协议远程访问端口即Web页面访问端口;

3.内核优化

需要进行调整的内核参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
cat >> /etc/sysctl.conf << EOF
# cat /proc/sys/net/core/netdev_max_backlog
# 默认值:1000
# 作用:网卡设备将请求放入队列的长度
net.core.netdev_max_backlog = 32768


# cat /proc/sys/net/core/somaxconn
# 默认值:128
# 作用:已经成功建立连接的套接字将要进入队列的长度
net.core.somaxconn = 32768


# cat /proc/sys/net/core/rmem_default
# 默认值:212992
# 作用:默认的TCP数据接收窗口大小(字节)
net.core.rmem_default = 8388608


# cat /proc/sys/net/core/wmem_default
# 默认值:212992
# 作用:默认的TCP数据发送窗口大小(字节)
net.core.wmem_default = 8388608




# cat /proc/sys/net/core/rmem_max
# 默认值:212992
# 作用:最大的TCP数据接收窗口大小(字节)
net.core.rmem_max = 16777216


# cat /proc/sys/net/core/wmem_max
# 默认值:212992
# 作用:最大的TCP数据发送窗口大小(字节)
net.core.wmem_max = 16777216


# cat /proc/sys/net/ipv4/ip_local_port_range
# 默认值:32768 61000
# 作用:可用端口的范围
net.ipv4.ip_local_port_range = 1024 65535


# cat /proc/sys/net/ipv4/route/gc_timeout
# 默认值 300
# 作用:路由缓存刷新频率,当一个路由失败后多长时间跳到另一个路由
net.ipv4.route.gc_timeout = 100


# cat /proc/sys/net/ipv4/tcp_fin_timeout
# 默认 60
# 作用:表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间
net.ipv4.tcp_fin_timeout = 30


# cat /proc/sys/net/ipv4/tcp_keepalive_time
# 默认值:7200
# 作用:间隔多久发送1次keepalive探测包
net.ipv4.tcp_keepalive_time = 1200


# cat /proc/sys/net/ipv4/tcp_timestamps
# 默认值:1
# 作用:TCP时间戳,时间戳必须要开启,否则下面的 TIME_WAIT 重用和快速回收无效
net.ipv4.tcp_timestamps = 1


# cat /proc/sys/net/ipv4/tcp_tw_recycle
# 默认值:0
# 作用:表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_tw_recycle = 1


######################## cat /proc/sys/net/ipv4/tcp_tw_reuse
# 默认值:0
# 作用:针对 TIME-WAIT,做为客户端可以启用(例如,作为nginx-proxy前端代理,要访问后端的服务)
net.ipv4.tcp_tw_reuse = 1


# cat /proc/sys/net/ipv4/tcp_syn_retries
# 默认值 2
# 作用:在内核放弃建立连接之前发送SYN包的数量。
net.ipv4.tcp_syn_retries = 1


# cat /proc/sys/net/ipv4/tcp_synack_retries
# 默认值 2
# 作用:为了打开对端的连接,内核需要发送一个SYN并附带一个回应前面一个SYN的ACK。也就是所谓三次握手中的第二次握手。这个设置决定了内核放弃连接之前发送SYN+ACK包的数量。
net.ipv4.tcp_synack_retries = 1


# cat /proc/sys/net/ipv4/tcp_mem
#确定 TCP 栈应该如何反映内存使用;每个值的单位都是内存页(通常是 4KB)。
#第一个值是内存使用的下限。
#第二个值是内存压力模式开始对缓冲区使用应用压力的上限。
#第三个值是内存上限。在这个层次上可以将报文丢弃,从而减少对内存的使用。对于较大的 BDP 可以增大这些值(但是要记住,其单位是内存页,而不是字节)。
net.ipv4.tcp_mem = 94500000 915000000 927000000


# cat /proc/sys/net/ipv4/tcp_max_orphans
# 默认值 16384
# 作用:系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。如果超过这个数字,孤儿连接将即刻被复位并打印出警告信息。这个限制仅仅是为了防止简单 的DoS攻击,你绝对不能过分依靠它或者人为地减小这个值,更应该增加这个值(如果增加了内存之后)。
net.ipv4.tcp_max_orphans = 3276800


# cat /proc/sys/net/ipv4/tcp_max_syn_backlog
# 默认值:128
# 作用:增大SYN队列的长度,容纳更多连接
net.ipv4.tcp_max_syn_backlog = 65535
EOF
#执行使内核参数生效
sysctl -p


4.性能优化

描述: 由于Tomcat的运行依赖于JVM,从虚拟机的角度我们把Tomcat的调整分为外部环境调优自身调优两类来描述。

4.1 外部调优

主要调整Tomcat运行环境的操作系统参数和运行Tomcat的java虚拟机参数。

(1)JAVA虚拟机性能优化
Tomcat需要依赖Java虚拟机运行,根据客户选用的主机的操作系统选择对应的JDK的版本建议使用最新的版本。

Tomcat的使用内存配置实质上是JVM的内存配置编辑 catalina.[sh / bat]配置文件,注意这里区分Windows和Linux;

VM调优内存生产配置参数列表:

参数 描述 备注
-server 模式启动应用慢,但是极大程度提高运行性能
-Xms 堆内存初始大小,单位m、g
-Xmx 堆内存最大允许大小,一般不要大于物理内存的80%
-Xmn 新生代最大可用内存,一般设置为整个堆的 1/3到 1/4 左右
-Dfile.encoding=UTF-8 设置字符集避免日志中出现乱码
-Dsun.jnu.encoding=UTF-8 设置字符集避免日志中出现乱码
-Duser.timezone=GMT+08 时区设置
-XX:PermSize 非堆内存初始大小,一般应用设置初始化200m,最大1024m support was removed in 8.0
-XX:PrintGC 每次触发GC的时候打印相关日志
-XX:+PrintGCDetails 更详细的GC日志
-XX:SurvivorRatio=2 用来设置新生代中 Eden 空间和 from/to 空间比例,假如设置为 2,那么 Eden 区的大小就是 From 和 To 加起来的和
-XX:NewRatio=2 设置新生代和老年代的比例,假如设置为 2,新生代内存设置为 2G,那么老年代就是 4G
-XX:+HeapDumpOnOutOfMemoryError 堆异常报错输出
-XX:HeapDumpPath=path 设置在dump heap时将文件dump到哪里。默认是当前目录下 java_pidpid.hprof这样形式的文件。
-XX:+UseParallelGC 选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集
-XX:+UseParallelOldGC 配置年老代垃圾收集方式为并行收集。
-XX:ParallelGCThreads=8 设置支持并发GC的线程数。
-XX:MaxPermSize 非堆内存最大允许大小 support was removed in 8.0
-XX:+UseParallelGCThreads=8 并行收集器线程数,同时有多少个线程进行垃圾回收,一般与CPU数量相等 support was removed in 8.0
-XX:+UseParallelOldGC 指定老年代为并行收集 tomcat 8.0 报警告
-XX:+UseConcMarkSweepGC CMS收集器(并发收集器)
-XX:+UseCMSCompactAtFullCollection 开启内存空间压缩和整理,防止过多内存碎片 tomcat 8.0 报警告
Headless=true windows系统可不用设置,适用于Linux系统与图形操作有关,如生成验证码含义是当前的是无显示器的服务器,应用中如果获取系统显示有关的参数会抛出异常


Tomcat默认可以使用的内存为128MB,在较大型的应用项目中内存是不够的所以需要调大,一般设置初始化内存为256MB,可以使用的最大内存为512MB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#Linux#
$vim /usr/local/tomcat8/bin/catalina.sh
#在OS specific support. $var _must_ be set to either true or false.110行下面添加
JAVA_OPTS="-server
-Xms512m
-Xmx2048m
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=80
-XX:-PrintGC
-XX:-PrintGCDetails
-XX:-PrintGCTimeStamps"


#Windows#
$vim /usr/local/tomcat8/bin/catalina.bat
#在/bin目录下的catalina.bat可以直接通过Tomcat设置JVM内存参数
set "JAVA_OPTS=%JAVA_OPT% -server -Xms2048m -Xmx2048m -XX:PermSize=256m -XX:MaxPermSize=512m -Djava.awt.headless=true"

WeiyiGeek.

WeiyiGeek.

注意事项:

  • java8开始,PermSize被MetaspaceSize代替,MetaspaceSize共享heap不会再有java.lang.OutOfMemoryError:PermGen space可以不设置
  • java8开始,MaxPermSize/UseParallelGCThreads=8已经移除
  • Java提供的垃圾回收机制虚拟机的堆大小决定了虚拟机花费在收集垃圾上的时间和频度并且收集垃圾可以接受的速度与应用有关
    • 如果堆(heap)的空间很大,那么完全垃圾收集(FULL GC)就会很慢,但是频度会降低。
    • 如果在客户系统中把堆的大小和内存的需要一致,完全收集就很快,但是会更加频繁。
    • 推荐把-Xms设置为应用所需的最小值,这样会产生高效的垃圾回收


(2)Tomcat与web服务器整合
描述: 虽然tomcat也可以作web服务器但其处理静态html的速度比不上Nginx服务,并且为了更好的进行设置负载均衡,还是得和Nginx进行联用;

因此我们可以把 Nginx 和 Tomcat 集成起来, 将html与jsp的功能部分进行明确分工, 让tomcat只处理jsp部分,或者也可以由其它的由 apache, IIS 等这些 web服务器处理,由此大大节省了tomcat有限的工作线程。


4.2 自身调优

(1)Connector元素属性优化
禁用DNS查询

  • 描述: 消除DNS查询对性能的影响我们可以关闭DNS查询
  • 优化: enableLookups="false"
  • 结果: 节省了网络带宽、查询时间和内存,而且更小的流量会使日志数据也会变得更少,显而易见也节省了硬盘空间

调整线程数(需要根据压力测试进行调整)

  • 描述: 可通过应用程序的连接器(Connector)进行性能控制的参数是创建的处理请求的线程数。在Java中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。但是应防止流量不可控制(或者恶意的服务攻击),从而导致超出了虚拟机使用内存的大小;web server允许的最大连接数还受制于操作系统的内核参数设置,通常Windows是2000个左右,Linux是1000个左右。
  • 优化: 修改 minProcessorsmaxProcessors 的值来控制线程数
  • 结果: 它们共享相同的地址空间。多线程帮助程序员写出CPU最大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。

NIO 配置

  • 描述:NIO (No-blocking I/O)从JDK 1.4起,NIO API作为一个基于缓冲区,并能提供非阻塞I/O操作的API被引入[LD6];TOMCAT可以支持高并发的企业级应用,配置良好的tomcat都会使用APR(Apache Portable Runtime),APR是Apache HTTP Server2.x的核心,它是高度可移植的本地库,它使用高性能的UXIN I/O操作,低性能的java io操作。
  • 优化: protocol="org.apache.coyote.http11.Http11NioProtocol"
  • 结果: NIO使用单线程(单个CPU)或者只使用少量的多线程(多CPU)来接受Socket,而由线程池来处理堵塞在pipe或者队列里的请求。只要OS可以接受TCP的连接,web服务器就可以处理该请求。大大提高了web服务器的可伸缩性。

ARP配置(推荐方式-但是需要安装额外的包):

  • 描述: Tomcat APR 模式也是 Tomcat 在高并发下的首选运行模式
  • 优化: protocol="org.apache.coyote.http11.Http11AprProtocol"
  • 结果: 调用 httpd 核心链接库来读取或文件传输,从而提高 tomcat 对静态文件的处理性能


Connector 属性 说明
port Tomcat启动监听端口 Default 8080
protocol Tomcat的3种运行状态协议 Default bio HTTP/1.1
acceptCount 监听端口队列最大数,满了之后客户请求会被拒绝(不能小于maxSpareThreads) Default 100,建议设置1000
maxThreads 当前可以同时处理的最大用户访问数最大连接数配置(并发能力) Default 200 ,建议设置1000
minSpareThreads Tomcat初始化时创建的socket线程数,线程的最小运行数目 Default 10 ,建议设置1000
maxSpareThreads Tomcat连接器的最大空闲socket线程数,一旦创建的线程超过这个值将会关闭socket 建议设置1000
minProcessors 服务器创建时的最小处理线程数 建议设置 100
maxProcessors 服务器同时最大处理线程数 建议设置 1000
enableLookups 支持域名解析可把ip地址解析为主机名 建议设置false 关闭DNS反向查询
compression 是否打开压缩功能 on
compressionMinSize 压缩的最小字节 2048
compressableMimeType 压缩的MImeType类型 text/html,text/xml,text/javascript,text/css,text/plain
connectionTimeout 代表连接超时时间,单位为毫秒 默认值为60000。通常情况下设置为30000
disableUploadTimeout 标志允许servlet在一个servlet执行的时候,使用一个不同的更长的连接超时。 建议设为false
keepAliveTimeout 长连接超时时间 常常设置-1永不过期
URIEncoding URL统一编码 utf-8
redirectPort 在需要基于安全通道的场合,把客户请求转发到基于SSL的redirectPort端口 8443


描述:修改server.xml 文件中的Connector元素得各个属性值;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
$vim ./conf/server.xml
<Connector
port="8080"
#默认BIO/建议采用NIO或者arp提高并发处理能力
protocol="HTTP/1.1"

#网络连接超时,单位:毫秒。设置为0表示永不超时(不推荐))
connectionTimeout="30000"
maxHttpHeaderSize="32768"
redirectPort="8443"

#当前可以同时处理的最大用户访问数最大连接数配置(并发能力)
maxThreads="1000"

#Tomcat初始化时创建的线程数。最小空闲线程连接数用于优化线程池
minSpareThreads="100"

#一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。
maxSpareThreads="1000"

acceptorThreadCount="2"

#当所有的线程以分配,仍然允许连接进来,但是出于等待状态的用户数。
#等待线程数+工作线程数=总的可最大连接数,超过这个数的请求将不予处理。
acceptCount="2000"
minProcessors="100"
maxProcessors="2000"

#为了消除DNS查询对性能的影响我们可以关闭DNS查询,关闭该功能在一定程度上提高了Tomcat服务器的性能;
enableLookups="false"
maxKeepAliveRequests="-1"
keepAliveTimeout="-1"
disableUploadTimeout="false"
connectionUploadTimeout="150000"
useSendfile="false"

#Tomcat 压缩配置,建议在前端 nginx 上开启压缩。Tomcat 作为应用服务器本身就很繁忙了。
compression="on"
compressionMinSize="2048"
compressableMimeType="text/html,text/xml,application/javascript,application/json,text/javascript,text/css,text/plain,image/gif,image/png"
URIEncoding="UTF-8" />


(2)采用用arp模式进行高并发选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#1.下载编译apr依赖
cd /usr/local/src/
wget http://mirrors.tuna.tsinghua.edu.cn/apache/apr/apr-1.7.0.tar.gz
wget http://mirrors.tuna.tsinghua.edu.cn/apache/apr/apr-util-1.6.1.tar.gz
tar xf apr-1.7.0.tar.gz && tar xf apr-util-1.6.1.tar.gz

cd apr-1.7.0
./configure --prefix=/usr/local/apr
make -j 2 && make install

cd apr-util-1.6.1
./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr/
make -j2 && make install

cp "/opt/tomcat/apache-tomcat-8.5.45/bin/tomcat-native.tar.gz" .
tar xf tomcat-native.tar.gz && cd tomcat-native-1.2.23-src/native/
./configure --prefix=/usr/local/apr --with-java-home=/opt/tomcat/jdk8/
make -j 2 && make install

#添加apr path
echo "export LD_LIBRARY_PATH=/usr/local/apr/lib" > /etc/profile
source /etc/profile

配置arp模式:

1
2
3
4
5
6
7
$ cd /usr/local/tomcat/conf/
$ vim server.xml
<Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol"
connectionTimeout="20000"
redirectPort="8443" />
#注意如果SSLEngine设置off会导致报错
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />

WeiyiGeek.

WeiyiGeek.

成功后查看启动日志:

1
2
3
4
$cat /opt/tomcat/apache-tomcat-8.5.45/logs/catalina.out
12-Sep-2019 12:37:47.381 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-apr-10.10.107.222-8080"]
12-Sep-2019 12:37:47.417 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["https-openssl-apr-443"]
12-Sep-2019 12:37:47.419 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 891 ms


(3)启用外部连接池
描述:执行器(线程池)在tomcat中每一个用户请求都是一个线程,所以可以使用线程池提高性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#去掉下面两个元素注释并修改maxThreads:
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="300" minSpareThreads="4"/>

<Connector executor="tomcatThreadPool"
port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="1000"
minSpareThreads="100"
maxSpareThreads="200"
acceptCount="1000"
disableUploadTimeout="true"
connectionTimeout="30000"
URIEncoding="UTF-8"
enableLookups="false"
compression="on"
compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain,image/gif,image/jpg,image/png"
redirectPort="8443" />

WeiyiGeek.

WeiyiGeek.


(4)禁用自动检测更新加载
Context元素reloadable属性会监视在 web-inf/classes 和 web-inf/lib 目录下class文件的改动,如果监测到有class文件被更新的,服务器会自动重新加载web应用。

  • 在开发和调试阶段,将其改为true但是一般像Eclipse等开发环境都会默认改为true
  • 在正式发布阶段,应将其该为false可以降低Tomcat的运行负荷,提高Tomcat的运行性能
    1
    2
    3
    $vim conf/server.xml
    #在</Host>之前加入类似以下内容:(重启生效)
    <context path="/bbs" docbase="bbs" reloadable="false"/>


(5)增大随机数熵池
描述:当你执行了 ./startup.sh 或者 ./catalina.sh start 后等待时间过长能要几分钟才能正常提供服务。
原因:在apache-tomcat 官方文档:如何让 tomcat 启动更快里面提到了一些启动时的优化项,其中一项是关于随机数生成时,采用的"熵源"(entropy source)的策略

使用伪随机函数生成器:

  • 通过修改 Tomcat 启动文件 -Djava.security.egd=file:/dev/urandom
  • 通过修改 JRE 中的 java.security 文件 securerandom.source=file:/dev/urand
1
JAVA_OPTS="-server -Xms1024m -Xmx2048m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=80 -XX:-PrintGC -XX:-PrintGCDetails -XX:-PrintGCTimeStamps  -Djava.security.egd=file:/dev/urandom"
WeiyiGeek.

WeiyiGeek.

增大/dev/random的熵池(推荐)

1
2
3
4
#安装熵池服务 rngd
$yum -y install rng-tools
$systemctl start rngd
$systemctl enable rngd

启动服务后观察 cat/proc/sys/kernel/random/entropy_avail 基本在三千左右。
1
2
$cat /proc/sys/kernel/random/entropy_avail
3311


5.安全加固

描述:加固依然分为身份鉴别、访问控制、安全审计、资源控制和入侵防范5个方面;
大部分加固基于xml配置文件进行修改,也应根据实际需求制定方案。

1
2
3
4
#寻找配置文件目录
find /-name *tomcat*
#配置文件目录存放目录
${tomcat_home}/conf/...


5.1 身份鉴别

0.口令复杂度,不同用户不同账号
描述:口令要求:长度至少8位,并包括数字、小写字母、大写字母和特殊符号4类中至少3类。
修改tomcat配置文件/conf/tomcat-users.xml配置文件,要求usr1密码必须满足复杂度要求。

1
2
<user username="Tomcat1" password="12345qwe" roles="manager-gui">
<user username="Tomcat2" password="12345qwe" roles="admin-gui">


1.安全配置manager登录页面并且删除多余账号
描述:Tomcat的主要管理界面被称为Manager应用程序

  • 如果不使用manager来管理部署应用,建议修改tomcat配置文件/conf/tomcat-users.xml配置文件来禁用或者删除manager文件夹
  • 如果使用该功能我们需要进行配置口令以及强密码包括访问控制,删除与工作无关的帐号以及应用。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #1.配置强密码登录manager后面
    $vim tomcat_user.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <tomcat-users xmlns="http://tomcat.apache.org/xml"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
    version="1.0">

    #角色如下并且要记得只赋予最小权限:
    # manager-gui:可以访问web界面
    # manager-status:只可以访问“Server Status”页面
    # manager-script:可以脚本文本界面和“Server Status”页面
    # manager-jmx:可以访问JMX代理界面和“Server Status”页面
    <role rolename="manager-gui"/>
    #host-manager页面
    <role rolename="admin-gui"/>
    <!--密码复杂度需要满足等保要求-->
    <user username="admin" password="@[email protected]" roles="manager-gui,admin-gui"/>
    </tomcat-users>

    #2.访问控制设定(其实默认只能本机访问)
    $/opt/tomcat/apache-tomcat-8.5.45/webapps/manager/META-INF/
    #采用正则添加匹配
    <Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
WeiyiGeek

WeiyiGeek


2.Tomcat虚拟主机管理器安全配置并且删除多余账号
描述:与manager管理一样如果使用的话进需要进行安全配置

  • 不使用的话建议删除webapps应用目录下的host-manager
  • 使用需要进行安全配置
    1
    2
    3
    4
    5
    6
    7
    #登录密码配置
    $vim conf/tomcat-users.xml
    <role rolename="admin-gui"/>
    <user username="tomcat" password="s3cret" roles="admin-gui"/>

    #访问配置(其实默认只能本机访问)
    <Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1|10\.\d+\.\d+\.\d+" />
WeiyiGeek.

WeiyiGeek.


3.非root启动服务
描述:建议使用专用用户 tomcat 或者 nobody 用户来启动 Tomcat,为了防止 Tomcat 被植入 web shell 程序后,可以修改项目文件。
因此我们要将 Tomcat 和项目的属主做分离(常常使upload目录可以有上传权限,但是不能有执行的权限))))),他也无法创建和编辑项目文件。

1
2
$ps -ef | grep tomcat|grep -v "grep"
tomcat 13243 1 0 10:27 pts/0 00:00:02 /opt/tomcat/jdk8/jre/bin/java -Djava.util.logging.config.file=/opt/tomcat//apache-tomcat-8.5.45/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -Xms1024m -Xmx2048m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=80 -XX:-PrintGC -XX:-PrintGCDetails -XX:-PrintGCTimeStamps -Xloggc:../logs/gc.log -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /opt/tomcat//apache-tomcat-8.5.45/bin/bootstrap.jar:/opt/tomcat//apache-tomcat-8.5.45/bin/tomcat-juli.jar -Dcatalina.base=/opt/tomcat//apache-tomcat-8.5.45 -Dcatalina.home=/opt/tomcat//apache-tomcat-8.5.45 -Djava.io.tmpdir=/opt/tomcat//apache-tomcat-8.5.45/temp org.apache.catalina.startup.Bootstrap start


5.2 安全审计

0.增加记录日志功能
描述:Tomcat的日志文件存放于logs文件夹,里面包含了多种类型的日志,主要分为两类:

  • 一是运行中的日志,它主要记录运行的一些信息,尤其是一些异常错误日志信息。
  • 二是访问日志信息,它记录的访问的时间,IP,访问的资料等相关信息。

各个日志文件的作用:

  • localhost.2019-09-10.log:程序异常没有被捕获的时候抛出的地方
  • catalina.2019-09-10.log:程序的输出,tomcat的运行日志
  • manager.2019-09-10.log:manager项目专有的
  • host-manager.2019-09-10.log:manager项目专有的
  • localhost_access_log.2019-09-10.txt:访问日志记录

编辑tomcat配置文件server.xml配置文件将以下内容的注释标记:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />

<!--
每个字段的含义为
192.168.228.1 - - [29/Sep/2017:11:26:10 +0800] "GET / HTTP/1.1" 200 11418
%h:远程主机名或IP地址:192.168.228.1
%l:远程用户名,始终为“-”
%u:已验证的远程用户,如果没有则为“-”
%t:访问日期和时间:[29/Sep/2017:11:26:10 +0800]
%r:http请求中的第一行:GET / HTTP/1.1
%s:http状态码:200
%b:发送的字节数:11418
-->

属性解释:

  • Directory:日志文件放置的目录,在tomcat下面有个logs文件夹,那里面是专门放置日志文件的,也可以修改为其他路径;
  • Prefix:这个是日志文件的名称前缀,日志名称为localhost_access_log.2008-10-22.txt,前面的前缀就是这个localhost_access_log。
  • Suffix: 文件后缀名。
  • Pattern:common方式时,将记录访问源IP、本地服务器IP、记录日志服务器IP、访问方式、发送字节数、本地接收端口、访问URL地址等相关信息在日志文件中。
  • resolveHosts:值为true时,tomcat会将这个服务器IP地址通过DNS转换为主机名,如果是false,就直接写服务器IP地址。

Tomcat的运行日志有以下7个级别:

SEVERE > WARNING > INFO > CONFIG > FINE > FINER > FINEST


5.3 资源控制

0.移除默认应用程序
描述:Tomcat可能自带一些默认的web应用程序。如果不是一定需要,必须将它们移除。
移除${tomcat_home}/webapps中所有的默认的web应用程序有:ROOT、Documentation、Examples、Host Manager和Manager

1
2
#果不是一定需要,必须将它们移除。
rm -rf /opt/tomcat/apache-tomcat-8.5.45/webapps/*


1.错误页面重定向
描述:编辑tomcat配置文件/conf/web.xml文件,在最后一行之前加入以下内容,然后需要重新启动tomcat服务;

1
2
3
4
5
6
7
8
9
10
<error-page>
<!-- 配置实现了将404未找到jsp网页的错误导向noFile.htm页面,还可以添加其多的错误代码导向页面,如403,500 -->
<error-code>404</error-code>
<location>/noFile.htm</location>
</error-page>
<error-page>
<!--配置实现了当jsp网页出现java.lang.NullPointerException导常时,转向error.jsp错误页面 -->
<exception-type>java.lang.NullPointerException</exception-type>
<location>/error.jsp</location>
</error-page>

当出现NullPointerException异常时tomcat会把网页导入到error.jsp,且会打印出出错信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<%@ pageerrorPage="/error.jsp" %>
典型的error.jsp错误页面的程序写法如下:
<%@ pagecontentType="text/html;charset=GB2312"%>
<%@ pageisErrorPage="true"%>
<html>
<head>
<title>错误页面</title>
</head>

<body>出错了:</p>错误信息:<%= exception.getMessage() %>
<br>
Stack Trace is :
<pre>
<font color="red"><%
java.io.CharArrayWritercw = new java.io.CharArrayWriter();
java.io.PrintWriterpw = new java.io.PrintWriter(cw,true);
exception.printStackTrace(pw);
out.println(cw.toString());
%>
</font>
</pre>
</body>
</html>

补充事项:

  • 如果Manager应用程序没被移除,必须手动将位于 CATALINA_HOME/webapps/manager/WEB-INF/jsp/ 的错误页面里的Tomcat版本信息移除。


3.限制访问Tomcat文件夹

描述:Tomcat文件夹只能由tomcat用户本身访问,尤其是对于目录${tomcat_home}/conf /和${tomcat_home}/webapps
当不需要通过应用程序服务器自动部署时,标准配置就是将所有Tomcat文件的所有者设置为root,并且所属群组设置为Tomcat,然后用chmod 740仅允许root用户编辑文件并允许Tomcat用户读取文件。

1
2
3
4
#(注意点)例外是临时和工作目录的所有者应该是Tomcat用户而不是root用户。
whown root:tomcat /opt/tomcat
chmod 740 ${tomcat_home}/conf
chmod 740 ${tomcat_home}/webapps


4.会话超时
描述:所有的web应用程序的会话超时必须设置为20分钟可通过编辑 CATALINA_HOME/conf/web.xml 文件并做以下配置来实现:

1
2
3
4
<!-- Tomcat8默认的超时时间是半个小时 -->
<session-config>
<session-timeout>20</session-timeout>
</session-config>


4.4 访问控制

描述: Tomcat提供了防止恶意攻击或禁止某些机器访问的设置,限制手段来防止恶意的服务攻击;
可以让你过滤来自请求的主机或IP地址,并允许或拒绝哪些主机/IP。与之类似的在Apache的httpd文件里有对每个目录的允许/拒绝指定。

1.Context.xml访问控制设置
Tomcat提供了两个参数供你配置:RemoteHostValve 和RemoteAddrValve。
例如: 你可以把Admin Web application设置成只允许本地访问设置如下:

1
2
3
4
5
<Context path="/path/to/secret_files" >
#如果没有给出允许主机的指定,那么与拒绝主机匹配的主机就会被拒绝,除此之外的都是允许的
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="10.0.172.109" deny="" />
</Context>


5.5 入侵防范

0.防止缓慢的http拒绝服务攻击与限制监听网络接口
描述:不要让连接器(connector)监听服务器上所有可用的网络接口和IP地址,而要让连接器监听指定的网络接口和IP地址采用address属性,防止应用程序意外地运行在某个开放的网络接口上。

1
2
3
4
5
#将默认值20000改成10000即可单位ms注意需要根据实际需求更改
$vim conf/server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="10000"
redirectPort="8443" address="127.0.0.1" />


1.禁用tomcat的AJP协议
描述:AJP(Apache JServer Protocol)AJPv13协议是面向包的。
WEB服务器和Servlet容器通过TCP连接来交互,为了节省SOCKET创建的昂贵代价,WEB服务器会尝试维护一个永久TCP连接到servlet容器,并且在多个请求和响应周期过程会重用连接。

1
2
3
#  编辑server.xml进行注释配置
$vim conf/server.xml
<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->

WeiyiGeek.

WeiyiGeek.


2.禁用非法HTTP请求方法
描述:readonly参数默认是true即不允许delete和put操作
编辑web.xml文件查看org.apache.catalina.servlets.DefaultServlet是否存在如下配置

1
2
3
4
5
$vim conf/web.xml
<init-param>
<param-name>readonly</param-name>
<param-value>true</param-value>
</init-param>


3.禁止目录列出

描述:设置DefaultServlet的listings为false
这不仅仅是因为允许显示目录列表被认为是不安全的,而且还因为生成具有数千个文件的目录列表会消耗大量的CPU资源,相当于被DDoS攻击。
注意:Tomcat8默认是false并且更改后需要重新启动tomcat服务;

1
2
3
4
5
6
7
$vim conf/web.xml
<!-- 同样也是在下面进行添加-->
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>


4.防止恶意关闭服务
描述:编辑tomcat配置文件conf/server.xml配置文件,shutdown的值为复杂的字符串:

1
2
3
4
5
6
7
8
#随机生成密码
$cat /dev/urandom | tr -dc '_a-zA-Z0-9' | head -c 12
r02BPRnHyq89
#配置如下:将默认的SHUTDOWN变成r02BPRnHyq89
<Serverport="8005" shutdown="r02BPRnHyq89">

#如果不需要该功能,必须要将其停用,设置如下:
<Server port="-1" shutdown="SHUTDOWN"> #本地管理脚本可将服务器关闭,即使在关闭端口被禁用的情况下。


5.修改Banner掩盖真实信息
描述:修改以掩饰真实版本信息防止攻击者以版本的漏洞进行攻击,影响错误页面和响应头;

1
2
3
4
5
6
7
8
##(1)修改/lib/catalina.jar中org/apache/catalina/util/Serverinfo.properties问津中的以下参数 (推荐方式)
$unzip catalina.jar
$vim catalina/org/apache/catalina/util/Serverinfo.properties
server.info=Nginx
server.number=0.0.0.0
server.built=Apr 2 2017 07:25:00 UTC
#将修改后的信息压缩回jar包
jar uvf catalina.jar org/apache/catalina/util/ServerInfo.properties

WeiyiGeek.

WeiyiGeek.

1
2
3
4
5
6
7
#(2)修改web.xml中国Connector链接器
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"
useBodyEncodingForURI="true"
server="Microsoft-IIS/6.5"/> #关键点
WeiyiGeek.

WeiyiGeek.


6.关闭自动部署
描述:Tomcat允许在Tomcat运行时自动部署应用程序。为了防止被植入木马等恶意程序,因此我们要关闭自动部署。

1
2
3
$vim server.xml
#将unpackWARs与autoDeploy的true值改成false;
<Host name="localhost" appBase="webapps" unpackWARs="false" autoDeploy="false">

补充:在托管环境中Web应用程序可能不受信任,也可以设置deployXML属性为false来忽略context.xml以防给该web应用程序提高权限。


7.配置HTTPS加密协议
描述:采用HTTPS协议加密可以防止中间人攻击,以及数据的拦截和修改的验证导致攻击无效;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#(1)用JDK自带的keytool工具生成一个证书,默认三个月的有效期;
$JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA -keystore /tmp/keystore
Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]: Tomcat
What is the name of your organizational unit?
[Unknown]: CQ
What is the name of your organization?
[Unknown]: CQ
What is the name of your City or Locality?
[Unknown]: BEIJING
What is the name of your State or Province?
[Unknown]: XIZHIMEN
What is the two-letter country code for this unit?
[Unknown]: 408119
Is CN=Tomcat, OU=CQ, O=CQ, L=BEIJING, ST=XIZHIMEN, C=408119 correct?
[no]: Y

Enter key password for <tomcat>
(RETURN if same as keystore password):
Re-enter new password:

#(2)修改tomcat安装目录下/conf/server.xml配置文件,更改为使用HTTPS方式,增加如下行:
<Connector classname="org.apache.catalina.http.HttpConnector"
port="443" protocol="HTTP/1.1" minProcessors="5"
SSLEnabled="true"
maxprocessors="100"
enableLookups="true" acceptCount="10" debug="0"
scheme="https"
Factory_classname="org.apache.catalina.SSLServerSocketFactory"
secure="true"
clientAuth="false"
keystoreFile="/tmp/keystore"
keystorePass="weiyigeek"
sslProtocol="TLS" />
#SSL Connector来指定可用的SSL加密方式:
ciphers="SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"

也可以通过 https://10.10.107.222/manager/html/sslConnectorCerts 查看证书情况

WeiyiGeek.

WeiyiGeek.

补充:为了使托管在Tomcat上的所有web应用程序强制使用HTTPS,必须在每个 CATALINA_HOME/webapps/$WEBAPP/WEB-INF/web.xml 文件里每个security-constraint标签关闭(标签)之前包含以下内容:

1
2
3
<user-data-constraint> 
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
<user-data-constraint>


8.HttpOnly标记
描述:对会话cookie自动启用HttpOnly的cookie标记,查看配置以确保该选项为被禁用。

1
2
3
4
#要启用HttpOnly设置使之全局应用于所有应用程序:
<Context useHttpOnly='true'>
...
<Context>

补充:如果应用程序需要通过JavaScript访问HttpOnly cookie,可以在METAINF/context.xml中一个单独的Context中定义一个异常。


9.CSRF防护(根据实际情况添加)
描述:为保护应用程序必须启用Tomcat的跨站请求伪造防护。Tomcat8~9提供了基本的CSRF防护。可以在 CATALINA_BASE/conf/web.xml 中配置一个全局过滤器。

1
2
3
4
5
#该过滤器可以被每个使用 WEB-INF/web.xml 文件的应用程序覆盖。
<filter-mapping>
<filter-name>CSRFPreventionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

补充:使用CSRF防护可能会影响程序功能,必须要牢记这一点,尤其是在应用程序大量使用异步请求的情况下。


5.6 默认设置

描述:以下是常规的默认配置并且默认情况下这些设置被认为是安全的,如有在项目中进行更改的建议进行整改和调整;

1.server.xml中默认安全配置

1
2
3
4
5
$vim $CATALINA_HOME/conf/server.xml
<Connector
#1.设置为空或者false
allowTrace="false"
/>


2.context.xml中默认安全配置

1
2
3
4
5
6
7
8
9
10
11
12
13
$vim $CATALINA_HOME/conf/server.xml
<Context
#1.将privileged属性设置为false,除非像Manager应用程序那样需要权限:
privileged="false"

#2.确保crossContext值为空或被设为false,
#crossContext值为true可能会导致允许恶意应用程序向受限应用程序发送请求。
crossContext="false"

#3.确保allowLinking值为空或被设为false。
#allowLinking值为true可能会导致目录遍历和源代码泄露漏洞的产生。
allowLinking="false"
/>


3.Tomcat启动参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#1.当RECYCLE_FACADES选项设置为true时,Tomcat会回收请求间会话外观(session facade),将导致请求间的信息泄漏
#默认情况下,此参数未被设置(确保使用的启动脚本不包含以下内容)
Dorg.apache.catalina.connector.RECYCLE_FACADES = false

#2.允许在Tomcat上指定不同的路径分隔符,可能会允许攻击者访问应用程序,该行为本该被代理程序(比如mod_proxy)阻止
#默认情况下,此参数未被设置(确保使用的启动脚本不包含以下内容)
Dorg.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH = FALSE
Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH = FALSE

#3.允许自定义header状态消息,使攻击者也能够插header。
#可能会导致XSS漏洞的产生。默认情况下此参数未被设置。
Dorg.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER = false
Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH= FALSE
Dorg.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER = false


5.7 可选操作

1.Java SecurityManager
描述:可用Java SecurityManager限制单个应用程序的功能。

  • $CATALINA_HOME/conf/catalina.policy 文件包含了Java SecurityManager使用的安全策略的配置
  • 一旦配置了catalina.policy 文件,便可以使用SecurityManager和–security选项启Tomcat。

注意事项:

  • 因为基本上所有的权限类型(比如访问单个文件和目录或Java包)都应该根据每个应用程序进行单独配置,所以这会大大增加操作成本。另外,限制过于严格的策略文件会影响应用程序的功能。


2.访问Java包控制
描述:Tomcat可限制对某些Java包的访问。如果检测到受限制的包被访问,将抛出安全异常。
对Java包做访问限制,打开 $CATALINA_BASE/conf/catalina.properties 文件并添加不允许访问的包至package.access列表。

分析Java import可以列出哪些应用程序需要哪些包。在Unix系统上,可以使用以下例子来实现:

1
grep –R import ${tomcat_home}/webapps/WEBAPP


附录

(1)生产配置实例
server.xml配置文件下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[[email protected] ~]# vim /usr/local/tomcat8/conf/server.xml
<!-- 修改Connector元素 -->
<Connector executor="tomcatThreadPool"
port="8080"
protocol="org.apache.coyote.http11.Http11NioProtocol"
address="127.0.0.1"
maxThreads="1000"
minSpareThreads="100"
maxSpareThreads="200"
acceptCount="1000"
disableUploadTimeout="true"
connectionTimeout="20000"
URIEncoding="UTF-8"
enableLookups="false"
compression="on"
compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain,image/gif,image/jpg,image/png"
redirectPort="8443"
server="SecWAF/1.9" />

#参数说明:
org.apache.coyote.http11.Http11NioProtocol:调整工作模式为Nio
address:绑定监听地址
maxThreads:最大线程数默认150表示最多同时处理150个连接,增大值避免队列请求过多,导致响应缓慢。
minSpareThreads:最小空闲线程数。
maxSpareThreads:最大空闲线程数,如果超过这个值,会关闭无用的线程。
acceptCount:当处理请求超过此值时,将后来请求放到队列中等待,如果超过这个连接的则直接返回拒绝连接。
disableUploadTimeout:禁用上传超时时间
connectionTimeout:连接超时,单位毫秒,0代表不限制
URIEncoding:URI地址编码使用UTF-8
enableLookups:关闭dns解析,提高响应时间
compression:启用压缩功能
compressionMinSize:最小压缩大小,单位Byte
compressableMimeType:压缩的文件类型
server:设置server响应头;