OpenClinica 安全
安装、配置和保护 OpenClinica [OC] 社区版 [CE] 无疑是一段冒险,需要具备计算机科学各方面的知识。然而,只要有充足的搜索和奉献精神,任何对 Linux 有些了解的人都可以做到。我本身不是计算机科学家,但还是承担了在 Web 服务器上部署 OC 的任务,最终成功地运行了它。不过,在某些时候,我希望能有一份详细的指南来指导整个过程。这篇帖子我想填补这个空白,至少在 OC 的安全方面,为尝试相同任务的任何人提供帮助。
我采取的安全措施是我从多个来源找到的,我不能保证它们是保护 OC 所能做或应该做的所有事情的详尽清单。正如我之前提到的,我不是该领域的专家,本指南仅仅是我大量搜索和反复试验的结果。这里的所有内容都要持保留态度,如果你认为你了解得更多,请随时更改其中的一些方面。也请随时建议改进/添加此过程,以便我们都能从这些集体知识中获益。
我尽可能认真地对待安全问题,因为截至 2020 年,OC 的 CE 所需的软件已经过时很久,因此尽一切可能让它变得安全至关重要,尤其是在处理患者数据时。
我选择的设置如下:
- OpenClinica CE 3.15
- CentOS 8
- PostgreSQL 8.4.22
- Tomcat 7.0.52
- JVM 1.7.0
我遵循了 这个安装指南,因为我找不到 v3.15 的安装指南。获得上面列出的某些特定软件版本是一个相当大的挑战,如果我不得不重新开始,我会考虑使用 CentOS 6,因为这个版本天生支持一些这些旧的依赖项。CentOS 6 也从今年开始不再提供支持,因此在选择它时要仔细考虑。
根据上述指南的说明,运行 OC 所需的所有组件的根目录位于:/usr/local
闲话少说,我们开始吧
首先,访问 Web 服务器(如果是远程服务器)应该被保护。每天都有数百次尝试使用常见的用户名登录 Web 服务器,因此这确实应该是你的第一道防线。
这个文件可以在 `/etc/ssh/sshd_config` 下找到,它控制着对你的服务器的 SSH 访问。在对它进行任何更改之前,请确保你已经备份了它。在文件中,修改以下参数:
只允许需要访问服务器的用户名
- AllowUsers <username1> <username2>
禁用 root 登录
- PermitRootLogin no
- ChallengeResponseAuthentication no
将你的 SSH 密钥复制到服务器后(使用 ssh-copy-id 或手动,通过复制你的公钥并将其粘贴到服务器上的 `~/.ssh/authorized_keys` 文件中),你应该禁用密码身份验证。这样,只有通过 SSH 密钥身份验证才能登录。
- PasswordAuthentication no
为了增加安全性,UsePAM 也可以设置为 `no`,但是,这会影响我的 SSH 身份验证过程,我不得不将其设置回 `yes`。
一切配置完成后,重新启动 sshd 服务
sudo systemctl restart sshd
这个软件包通过禁止在 x 次 SSH 身份验证失败后尝试连接的 IP 地址来提高 SSH 安全性。这里有一个 很棒的指南 来配置 fail2ban。
sudo yum install epel-release
sudo yum install fail2ban
在 `/etc/fail2ban` 中创建一个名为 `jail.local` 的文件,并将以下内容粘贴进去(使用你自己的电子邮件地址)
[DEFAULT] # Ban hosts for one hour: bantime = 3600 maxretry = 5 [sshd] enabled = true destemail = <[email protected]> sender = <[email protected]> sendername = Fail2ban mta = sendmail action = %(action_mwl)s
创建这个文件而不是直接在 `jail.conf` 中配置是首选,因为 fail2ban 更新会覆盖你的配置。现在,当一个 IP 地址在 5 次失败的登录尝试后被禁止或 fail2ban 被停止/启动时,你将收到电子邮件通知。
重新启动 fail2ban 服务:sudo systemctl restart fail2ban
如果你想接收更多关于被禁止 IP 的信息,可以安装 `whoami.x86_64` 软件包。
你可以通过以下方式查看 sshd 监狱状态:sudo fail2ban status sshd
虽然这听起来很明显,但维护服务器上的强壮且唯一的密码仍然至关重要。我建议使用 KeePass 来跟踪它们,因为它还可以让你自动生成非常强大的密码。
保护 Tomcat 与保护 Web 服务器一样重要,因为它是你的外部世界接触点。我从以下网站收集了下面列出的安全措施: [1] [2] [3] [4]
这是为了确保即使攻击者可以控制 Web 服务器,她所能造成的破坏也是最小的。首先,永远不要以 root 用户的身份运行 Web 服务器,创建一个名为 `tomcat` 的新用户,该用户拥有运行服务器所需的最小权限。
将文件夹 `tomcat` 及其所有内容的所有者设置为用户 tomcat 和组 tomcat
chown -R tomcat:tomcat /usr/local/tomcat
(不带 -R 标志,你只会更改文件夹的所有权,而不会更改内容)
给文件夹的所有者读写和执行权限
chmod -R 700 /usr/local/tomcat
(使用 chmod 的指南:https://www.lifewire.com/uses-of-command-chmod-2201064)警告:要能够打开文件夹,你需要对其拥有执行权限。我也是通过艰难的方式学到的。
从配置文件夹中删除写入和执行权限。这可以防止攻击者修改 Tomcat 的配置文件
chmod -R u-wx /usr/local/tomcat/conf
但是,Tomcat 需要能够打开该文件夹,因此为文件夹(= 不带 -R 标志)添加执行权限
chmod u+x /usr/local/tomcat/conf
从 openclinica.config 和 openclinica-ws.config(如果已安装)文件夹中删除读取权限
chmod -R u-w openclinica*.config
从 logs 文件夹中删除读取权限
chmod -R u-r /usr/local/tomcat/logs
从 oldwebapps 文件夹中删除所有权限,因为在部署期间,Tomcat 不必对这些文件夹进行任何操作
chmod -R u-rwx /usr/local/tomcat/oldwebapps
将 `datainfo.properties` 文件设置为只读(如果已安装,对 OpenClinica-ws 进行相同的操作)
chmod 400 /usr/local/tomcat/webapps/OpenClinica/WEB-INF/classes/datainfo.properties
这样,你就完成了 Tomcat 的访问控制方面。
您可以做的另一件事是将您的用户添加到 tomcat 组,并授予该组对 usr/local/tomcat 文件夹的读写执行权限。这样,您仍然可以方便地编辑所有内容,而无需使用 root/sudo。我对它的安全性不太确定,请自行承担风险使用。并且不要忘记在所有配置完成后从组中删除自己并删除权限。
HTTPS
[edit | edit source]如上所述,Tomcat 的 SSL 设置指南应该是您执行的第一步 (https://tomcat.net.cn/tomcat-7.0-doc/ssl-howto.html)。您需要确保本指南中列出的所有步骤都是使用用户 `tomcat` 完成的,因为如果以其他方式完成,.keystore 文件将在该用户的 home 目录下创建,tomcat 将无法找到并读取它。理论上,您也可以将 HTTP 连接器注释掉,因为对 OC 强制使用 HTTPS 是最佳实践。端口转发将在防火墙级别启用,因此理论上 Tomcat 不应该接收端口 8080 的请求,但我还没有实际测试过。
配置完成后,您应该能够使用 HTTPS 与您的 Web 服务器通信(在 https://127.0.0.1:8443/OpenClinica 上)。如果您使用的是自签名证书,您的浏览器会报错,但您仍然可以访问网站。您可以通过在 `tomcat/conf` 下的 web.xml 文件末尾添加以下内容(在 </web-app> 标签之前)来强制在 Tomcat 中使用 HTTPS
<security-constraint> <web-resource-collection> <web-resource-name>Protected Context</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint>
Cookies、自定义错误页面、禁用列表 ..
[edit | edit source]这些都是良好的实践,但并非必需的。这些包括从错误页面隐藏服务器版本或禁用文件系统列表以更好地防止 DDoS 攻击。
以下所有内容都需要添加到 `web.xml` 文件中。
安全且仅限 HTTP 的 cookie 以防止 XSS 攻击
[edit | edit source](粘贴到 </web-app> 标签之前)
<cookie-config> <http-only>true</http-only> <secure>true</secure> </cookie-config>
自定义错误页面
[edit | edit source](粘贴到 </web-app> 标签之前) (Tomcat 无法找到错误页面文件,因此不会显示任何错误页面,但这可以通过隐藏版本号来完成。如果您遇到问题,请跳过此步骤。)
<error-page> <error-code>404</error-code> <location>/error.jsp</location> </error-page> <error-page> <error-code>403</error-code> <location>/error.jsp</location> </error-page> <error-page> <error-code>500</error-code> <location>/error.jsp</location> </error-page> <error-page> <exception-type>java.lang.Exception</exception-type> <location>/error.jsp</location> </error-page> <error-page> <exception-type>java.lang.Throwable</exception-type> <location>/error.jsp</location> </error-page>
只读资源和无列表
[edit | edit source](粘贴到文件中的 **DefaultServlet** 部分)
<init-param> <param-name>readonly</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param>
server.xml
[edit | edit source]删除服务器横幅
[edit | edit source]如果您在连接器的参数列表中添加参数 `Server=" "`,这将隐藏 HTTP 标头中的版本号。它应该看起来像这样
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" Server =" " address="<webserver IP address or localhost>" clientAuth="false" sslProtocol="TLS" keystoreFile="${user.home}/.keystore" keystorePass="<your keystore pass>" />
更改关闭命令
[edit | edit source]为了防止攻击者能够关闭您的服务器,您应该考虑更改关闭端口以及关闭命令。您可以在 `server.xml` 文件开头 <Server> 标签内完成此操作
<Server port="任何未使用的端口" shutdown="<长且安全的字符串>">
AJP 连接器
[edit | edit source]如果 OC 是唯一将在 Tomcat 中部署的 Web 应用程序,您可以安全地注释掉 AJP 连接器行,因为 OC 没有使用此连接器。
可能良好的实践,但对我造成了问题
[edit | edit source]禁用自动部署
[edit | edit source]为了防止有人在您的 Tomcat 实例中自动部署他们自己的恶意 Web 应用程序,您可以在 `server.xml` 文件中通过在 <Host> 标签中将以下参数设置为 false 来关闭自动部署。
autoDeploy="false" deployOnStartup="false" deployXML="false"
这样,如果您重新启动服务器,您必须手动部署 OC,您将需要弄清楚如何操作。
安全容器
[edit | edit source]使用 Tomcat,您可以选择在容器中启动您的实例,这确保即使在最坏的情况下有人破坏了服务器,他们也只能访问此容器内的资源和文件。但是,您将需要花费时间才能使 OC 正常工作,因为它已知会(并且对我来说)破坏安装。如果您有时间和知识使其正常工作,这可能是值得的。
其他用法非常简单,在启动 Web 服务器时添加 -secure 标志
/usr/local/tomcat/bin/startup.sh -secure
这就是 Tomcat 的全部内容。
PostgreSQL
[edit | edit source]在安全性方面,您无法为 PostgreSQL 配置很多内容,但您也可以在这里进行一些访问控制。
更改 pgsql 文件夹的所有权
sudo chown -R postgres:postgres /usr/local/pgsql
更改权限
sudo chmod -R 700 /usr/local/pgsql
您还应该仔细配置 `/usr/local/pgsql/data/` 中的 pg_hba.conf 文件。此文件控制对数据库的不同访问权限。您需要授予 clinica 用户本地连接 openclinica 数据库的权限,但阻止所有其他连接尝试。身份验证方法应设置为 `md5`,永远不要使用 `password`,因为它以明文形式发送这些方法。如果您将直接从远程机器访问数据库,则必须在此处添加相应的行。
# TYPE DATABASE USER CIDR-ADDRESS METHOD # allow user clinica to connect to openclinica locally, using encrypted password based authentication (needed to automate backups) local openclinica clinica md5 # IPv4 local connections: host openclinica clinica 127.0.0.1/32 md5 # "local" is for Unix domain socket connections only local all all reject # IPv6 local connections: host all all ::1/128 reject
此配置对于将在下面描述的自动备份也很重要。
防火墙
[edit | edit source]为任何 Web 服务器设置防火墙都是必须的。这使您可以限制和监控发生在您的服务器和外部世界之间的所有流量。这里的主要目标是只留下 Web 应用程序运行所必需的那些端口打开。在这种情况下,这意味着
- 端口 80/tcp,预期传入 HTTP 流量,
- 端口 443/tcp,预期传入 HTTPS 流量,
- 端口 22/tcp,SSH 流量正在进行。
您可能需要进行一些试错,以查看关闭某个端口是否会破坏任何东西,但对我来说,只允许这 3 个端口打开就足够了。
FirewallD
[edit | edit source]如果您已经习惯使用 FirewallD,您可以跳到下一节(**设置 FirewallD**)。
CentOS 预装了 FirewallD,它是一个强大的工具,可以管理这些连接。网上有很多资源可以帮助您了解更多信息,比如这个。
(更新:本文中描述的一种现象已过时。无法由其分配区域处理的数据包不会“向上踢”到下一个区域(“区域漂移”的概念)。它被认为是不安全的,将在未来版本的 FirewallD 中删除。如果您确实想拥有这种行为,您可以在 FirewallD 的配置文件中启用区域漂移)
首先,确保 firewalld 已安装并在您的系统上启用。启用的默认区域是 `Public`,您可以为了我们的目的保持这种方式。您可以检查在此区域中默认启用了哪些服务
sudo firewall-cmd --zone=public --list-all
服务并不是一些特殊的实体,它们仅仅代表了给定服务正常运行所需打开的端口。这些端口定义在`/usr/lib/firewalld/services`目录下,以自解释的xml文件形式存在。如果你想定义自己的服务,可以将这些xml文件中的一个复制到`/etc/firewalld/services`目录,并根据自己的需求进行自定义。你可以通过以下命令将任何服务添加到防火墙区域:
sudo firewall-cmd --zone=public --add-service=<你的服务> --permanent
你也可以通过以下命令移除任何不需要的服务:
sudo firewall-cmd --zone=public --remove-service=<不需要的服务> --permanent
你也可以直接添加或移除需要的端口:
firewall-cmd --zone=public --add-port=<你的自定义端口号>/tcp --permanent
如果没有使用`--permanent`标志,则一旦FirewallD被重新加载或重启,更改将会被重置。你可以在没有使用标志的情况下尝试使用规则,一旦你找到了适合你的规则,就可以通过添加`--permanent`标志来最终确定这些规则。
以下配置假设你使用ssh访问你的远程服务器,但是,如果不是这种情况,你可以跳过设置内部区域。
防火墙将设置三个区域:public、internal和trusted。public区域应该处理来自任何请求IP地址的http/s请求。internal区域应该添加一个源,该源包含你的IP或MAC地址,因此(理论上)只有当你的机器与服务器通信时,这个区域才会处理数据传输。这也是启用ssh服务(=打开端口22)的区域。
在开始配置FirewallD之前,你应该确保iptables被禁用,并通过屏蔽它来阻止它启动。否则它可能会干扰FirewallD,导致一些奇怪的行为。
systemctl mask iptables
systemctl disable iptables
首先,从public和internal区域移除任何不需要的服务。使用`--list-all`标志查看当前允许的内容。你应该只保留(或添加,如果需要)http/https服务。在internal区域中,还要添加ssh服务。
你应该将你的外向网络接口分配到public区域(这样所有来自外部的请求都会由这个区域处理)。你可以通过以下命令列出所有可用的接口:
ip link show
你可以使用`--add-interface=<接口>`标志添加接口。
不幸的是,这些接口的命名并不简单(至少对我来说是这样,我的接口叫做`ens192`),所以你可能需要做一些研究才能弄清楚它们是什么。
你还应该将自己的IP/MAC地址添加到internal区域的源中,这样只有来自这个IP/MAC地址的请求才会由这个区域处理,例如:
sudo firewall-cmd --zone=internal --permanent --add-source=154.112.12.18
如果你想让ssh可以从某个特定的VPN网络访问,你也可以将这个IP地址设置得更通用,例如:
sudo firewall-cmd --zone=internal --permanent --add-source=154.112.0.0/16
这将允许来自154.112.0.0-154.112.255.255的任何人使用ssh。(请注意,只有当至少有一个接口或源分配给区域时,区域才处于活动状态!)
public区域的目标应该设置为DROP,这会在请求到达无效端口时返回空消息,而不是传输拒绝消息。这被认为更加安全。
sudo firewall-cmd --permanent --zone=public --set-target=DROP
接下来,创建端口转发规则,以限制通信到https,并适应Tomcat的端口约定(它使用8080而不是80,8443而不是443)。
sudo firewall-cmd --zone=public --permanent --add-forward-port=port=443:proto=tcp:toport=8443 sudo firewall-cmd --zone=public --permanent --add-forward-port=port=80:proto=tcp:toport=8443 sudo firewall-cmd --zone=internal --permanent --add-forward-port=port=443:proto=tcp:toport=8443 sudo firewall-cmd --zone=internal --permanent --add-forward-port=port=80:proto=tcp:toport=8443
为了使端口转发工作,你还需要启用伪装 (https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/sec-port_forwarding)
sudo firewall-cmd --zone=public --add-masquerade --permanent
sudo firewall-cmd --zone=internal --add-masquerade --permanent
现在这一步我不太确定,因为我没有找到关于它的太多信息。我将lo(回环)接口分配给了trusted区域。这是从同一台机器(本地主机,127.0.0.1)与你的Web服务器通信的接口。理想情况下,这是从外部世界无法访问的,因此我假设trusted区域适合它,但同样,我可能错了。
如果你需要ping功能,请运行以下命令(将区域替换为你需要ping功能的区域):
sudo firewall-cmd --permanent --zone=<你的ping区域> --add-rich-rule='rule protocol value="icmp" accept'
你必须重新加载firewalld才能使更改生效。
sudo firewall-cmd --reload
现在你已经配置了FirewallD,它只允许与外部世界的http/s通信,但仍然允许从你的个人机器/你的VPN网络进行ssh连接。
你可以使用nmap包测试你的防火墙,查看哪些端口是打开的。
你已经做了大部分能做的事情来使OC安全,但是,你仍然可以在OC本身做一些事情。如果你以root用户身份登录到Web界面,导航到`任务/管理/用户/配置密码要求`,那里会有几个选项,你可以打开/关闭并调整它们以满足你的需求。你也应该绝对启用尝试次数过多后的用户锁定(`任务/管理/用户/锁定`)。作为root用户,如果你锁定了这些帐户,你可以在Web界面上解锁它们。我自己采取的另一个措施(但我不知道它是否必要):要求我的OpenClinica实例的用户将他们的密码挑战问题设置为一个长字符串(不可猜测的)。我这样做是因为我不确定OC是如何发送密码重置邮件的,以及这个邮件是否会被潜在的攻击者劫持。一个更安全(但承认更不方便,甚至如果你有太多用户的话也不切实际)的解决方案是,以root用户身份,从Web界面重置他们的密码并自己发送给他们。
这实际上也应该是一个必须。我在这里分享的解决方案只是一种方法,如果你知道更好的方法,请不要犹豫,用你自己的方法去做。
这里有一份指南是关于如何在OC中实施备份的指南。基本上,你需要定期保存你的OC配置文件、你的研究目录($TOMCAT_ROOT/openclinica.data)和你的PostgreSQL数据库。
我已经附加了一个脚本,如果执行它,它应该备份所有这些文件,将它们打包成一个tarball文件并保存到`/usr/local/OpenClinica_backups`。你可以(并且应该)通过编辑脚本中的BCKP_path变量将备份位置更改到服务器外部。此外,你还需要为它创建一个.pgpass文件,更多详细信息见下文。
我对上面链接的指南的补充是,使这些备份自动发生,例如每天一次。为此,我使用了`cron`服务,该服务预装在CentOS 8上。
要让你的脚本每天执行,请将其放在`/etc/cron.daily/`目录下。通常,要能够获取pg_dump,你需要你的openclinica数据库的密码。要允许自动更新,你需要在`/var/lib/pgsql/`目录下创建一个.pgpass文件。它的内容应如下所示:
localhost:5432:openclinica:clinica:<你的数据库密码>
为了让Postgres能够使用这个文件,它必须由postgres拥有
sudo chown postgres:postgres .pgpass
并且它的权限必须设置为0600
sudo chmod 0600 .pgpass
如果你在身份验证方面遇到任何问题,请参阅pgpass的文档[5]。
为了能够创建pg_dump,postgres需要能够写入你的备份文件夹。将文件夹的组更改为postgres,并赋予组rwx权限。
sudo chown root:postgres <你的备份文件夹>
sudo chmod g+rwx <你的备份文件夹>
你应该练习恢复你的数据库,看看它是如何实际完成的。上面提到的指南可以帮助你完成这些工作。
有一个很棒的GitHub仓库,它可以让你每天运行扫描来检测各种恶意软件,包括特洛伊木马、病毒或rootkit。如果你将克隆的仓库中的脚本放在` /etc/cron.daily`目录下,它将每天与自动备份脚本一起执行。
为了做个明确的工作,最好禁用tomcat、clinica和postgres用户的登录
usermode -L tomcat usermode -L clinica usermode -L postgres
恭喜您,您刚刚使您的 OpenClinica 安装更加安全!不幸的是,您必须牢记我们一直在加固的软件是过时的软件(非常过时),因此您只能做很多事情,并寄希望于一切顺利。我所能给出的最好的建议是,如果可能,不要包含可识别您的研究对象的任何信息。这样,即使一切都失败了,至少您的研究对象不必担心个人身份被盗。
#! BCKP_path='/usr/local/OpenClinica_backups' DATE=`date +"%Y-%m-%d"` cd $BCKP_path # create a database dump with pg_dump sudo -u postgres /usr/local/pgsql/bin/pg_dump -U clinica openclinica -w > pg_dump # backup the data directory of OC with CRF, XML, etc.. data sudo tar -cf oc_data.tar /usr/local/tomcat/openclinica.data # backup OC configuration sudo cp /usr/local/tomcat/openclinica.config/datainfo.properties datainfo.properties # tar all the above created files and assign a date sudo tar -czf ${DATE}_openclinica_backup.tar.gz datainfo.properties oc_data.tar pg_dump # remove created temporary files sudo rm datainfo.properties sudo rm oc_data.tar sudo rm pg_dump # add a bit of security sudo chmod 400 *.tar.gz