将正确的 HTTP 头转发给后端服务器的一些问题

Apache Software Foundation 的 HTTP 服务器项目(通常称为 Apache)是当今互联网上占据优势的 Web 服务器,它占据了 60% 以上的市场份额。Apache 服务器是日渐流行的 LAMP 软件配置的一部分。LAMP 是一套免费软件程序,是在 Linux?、Apache、MySQL 和 PHP 等开放源码技术之上构建的 Web 平台。在本文中,您将学习一种使用 mod_proxy 模块和多个后端服务器来改进 LAMP 安全性的方法。我将讨论这种方法的优点和缺点,并提供一个配置示例。

  PHP 和 Apache:安全性难题

  LAMP 管理员面对的一个挑战是,提供完整 PHP 系统的所有特性,同时确保为系统的所有用户提供一个安全的环境。使用 PHP 的安全模式是实现这一目标的一种技术,但是它也过度地限制了用户,而且启用了这个设施之后,一些 PHP 应用程序就不能发挥作用。

  PHP 安全问题的根源在于大多数 Apache 服务器的配置方式。因为大多数 Apache 配置运行在特殊的 www-data 用户 ID 下,对 Web 站点进行主机托管的所有用户在默认情况下必须确保这个用户可以读取他们的文件。因此,系统上的所有其他用户都可能访问一个用户的所有 Web 可访问文件;所以系统上原本与您无关的安全漏洞会成为攻击您的 Web 站点的突破口。如果文件或目录必须设置为 www-data 用户可写的,那么这种情况会更加严重。

  通过使用 CGI 程序,比如用 Perl 和 Python 等流行语言编写的程序,可以在使用 suEXEC 机制时消除这个问题的部分影响。简单地说,suEXEC 使用一个特殊的中间程序以程序所有者的用户 ID 执行 CGI 程序。这是一种非常有效的机制,已经使用了许多年了。

  但是,在使用 mod_php 模块主机托管时,PHP 页面作为 Apache 主进程的一部分执行。因此,它们继承 Apache 进程的所有凭证,而且它们在文件系统上执行的任何工作必须作为 www-data 用户执行。

  在多个用户 ID 下运行 Apache

  对于上面描述的问题,明显的解决方案是要求对一个用户域的所有请求都来自一个只拥有此用户的凭证的 Apache 实例。可以将 Apache 配置为在启动时获取任何用户的凭证。对于给每个用户分配一个单独的互联网可见 IP 地址/端口组合的简单设置,这种方法可以解决问题。

  对于更复杂的设置(在其中 IP 地址很宝贵),这种方法是无效的。当单一 Apache 实例可以控制一个特定的 IP 地址/端口组合时,只能使用虚拟主机,这是 Apache 系统中广泛使用的一种技术。这就排除了让属于多个用户的多个域使用同一个 IP 地址/端口组合的可能性。

  Apache 2.0 引入了多处理模块(multiprocessing module,MPM) 的概念。在基本 Apache 2.0 包提供的 MPM 中有一个实验性的模块 perchild,它可以将一个分布器线程分配给 IP 地址/端口组合,并将请求传递给在单独用户的凭证下运行的子线程,从而实现多个用户 ID 下的虚拟主机。遗憾的是,perchild 仍然是实验性的,它不一定能够发挥作用,而且在 Apache 2.2 发布时从正式 Apache 发行包中删除了。在此之前,由于认识到仍然需要一个稳定的能够发挥作用的与 perchild 相似的 MPM,Apache 社区开始研发许多 MPM 来弥补这一欠缺。MetuxMPM 以及面向过程的 peruser 正在朝着这个方向努力。

  一个解决方案:mod_proxy

  尽管还没有正式的 Apache MPM 能够直接提供多个用户 ID 下的虚拟主机,但是仍然可以通过某些配置和管理在 Apache 系统中实现这种行为。这种方法的核心概念是使用 mod_proxy 模块,这个模块(加上其他功能)使 Apache 能够将页面请求转发给其他服务器,并将响应传递回原来发出请求的客户机。

清单 1. 基本请求转发的反向代理配置示例

ProxyRequests Off 
 
ProxyPass /foo http://foo.example.com/bar 
ProxyPassReverse /foo http://foo.example.com/bar 

  清单 1 中的代码是一个简单的示例,它将对一个主机的 /foo 层次结构下任何页面的请求转发到 http://foo.example.com/bar 的对应页面。例如,对 /foo/index.htm 页面的请求会转发到 http://foo.example.com/bar/index.htm。可以使用这一原理解决问题。

  示例场景

  我们来考虑一个场景:Apache 管理员必须为两个单独的客户建立两个域。一个客户是在线创业企业,很关注在线安全性。另一个是个人客户,他在站点安全性方面比较宽松,可能将不安全的代码上载到这个站点。因此,Apache 管理员必须采取措施将这两个站点隔离开。

  因此,管理员有两个域:www.startup.tld,它属于在线创业企业(用户 ID startup);以及 www.reckless.tld,它属于个人(用户 ID nimrod)。为了解决这个问题,管理员决定使用 mod_proxy 解决方案。管理员给每个用户一个单独的 Apache 实例,这个实例运行在用户自己的用户 ID 下,使用私有的 IP 地址/端口组合,并使用 mod_proxy 解决方案通过一个 facade 服务器提供对这两个用户的域的访问,这个服务器作为 www-data 运行,使用一个公共的 IP 地址/端口组合。图 1 说明了整个场景。

图 1. 场景示例

  推荐的 Apache 版本

  对于示例应用程序配置中的每个元素,Apache 管理员应该使用 表 1 中列出的 Apache 版本。

表 1. 示例应用程序中使用的 Apache 版本

元素 Apache 版本 原因 
facade 服务器 Apache 2,运行 worker 或 event MPM Apache 2 对 mod_proxy 模块做了重要的改进。worker 和 event MPM 是线程化的,有助于减少 facade 服务器的内存开销。 
后端服务器 Apache 1.3,或运行 prefork MPM 的 Apache 2 Apache 管理员必须意识到 PHP 模块不应该运行在线程化环境中。这两个解决方案为 PHP 模块提供了基于进程的环境。

  后端 Apache 实例的配置

  清单 2 和 清单 3 中的代码片段说明了与标准 Apache 配置的基本差异。应该根据需要将它们添加到适当的配置中,比如这里忽略的 PHP 功能配置。

清单 2. 在线创业企业的 Apache 配置

# Stuff every Apache configuration needs 
ServerType standalone 
LockFile /var/lock/apache/accept.startup.lock 
PidFile /var/run/apache.startup.pid 
 
ServerName necessaryevil.startup.tld 
DocumentRoot "/home/startup/web" 
 
# Essential modules 
LoadModule access_module /usr/lib/apache/1.3/mod_access.so 
 
# Which user to run this Apache configuration as 
User startup 
Group startup 
 
# This must be off else the host isn‘t passed correctly 
UseCanonicalName Off 
 
# The IP/port combination to listen on 
Listen 127.0.0.2:10000 
 
# Using name-based virtual hosting allows you to host multiple sites per IP/port combo 
NameVirtualHost 127.0.0.2:10000 
 
<VirtualHost 127.0.0.2:10000> 
    ServerName www.startup.tld 
 
    # You can add aliases so long as the facade server is aware of them! 
    ServerAlias startup.tld 
 
    DocumentRoot "/home/startup/web/www.startup.tld" 
 
    <Directory /home/startup/web/www.startup.tld/> 
      Options Indexes FollowSymLinks MultiViews ExecCGI Includes 
      AllowOverride All 
      Order allow,deny 
      Allow from all 
    </Directory> 
 
</VirtualHost> 

清单 3. 个人客户的 Apache 配置

# Stuff every Apache configuration needs 
ServerType standalone 
LockFile /var/lock/apache/accept.nimrod.lock 
PidFile /var/run/apache.nimrod.pid 
 
ServerName necessaryevil.nimrod.tld 
DocumentRoot "/home/nimrod/web" 
 
# Essential modules 
LoadModule access_module /usr/lib/apache/1.3/mod_access.so 
 
# Which user to run this Apache configuration as 
User nimrod 
Group nimrod 
 
# This must be off else the host isn‘t passed correctly 
UseCanonicalName Off 
 
# The IP/port combination to listen on 
Listen 127.0.0.2:10001 
 
# Using name-based virtual hosting allows you to host multiple sites per IP/port combo 
NameVirtualHost 127.0.0.2:10001 
 
<VirtualHost 127.0.0.2:10001> 
    ServerName www.reckless.tld 
 
    # You can add aliases so long as the facade server is aware of them! 
    ServerAlias reckless.tld 
 
    DocumentRoot "/home/nimrod/web/www.reckless.tld" 
 
    <Directory /home/nimrod/web/www.reckless.tld/> 
      Options Indexes FollowSymLinks MultiViews ExecCGI Includes 
      AllowOverride All 
      Order allow,deny 
      Allow from all 
    </Directory> 
 
</VirtualHost> 

  清单 4 说明了门面 Apache 实例的配置。

清单 4. 门面 Apache 实例的 Apache 配置

# Stuff every Apache configuration needs 
LockFile /var/lock/apache/accept.www-data.lock 
PidFile /var/run/apache.www-data.pid 
 
ServerName necessaryevil.facade.server 
DocumentRoot "/home/www-data" 
 
# Essential modules 
LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so 
LoadModule proxy_http_module /usr/lib/apache2/modules/mod_proxy_http.so 
 
# Which user to run this Apache configuration as 
User www-data 
Group www-data 
 
# These must be set else the host isn‘t passed correctly 
UseCanonicalName Off 
ProxyVia On 
ProxyRequests Off 
# This must also be set, though it‘s only an option in Apache2 
ProxyPreserveHost On   
 
# The IP/port combination to listen on 
Listen 9.20.1.1:80 
 
# Using name-based virtual hosting allows you to host multiple sites per IP/port combo 
NameVirtualHost 9.20.1.1:80 
 
# Configuration to forward requests for startup.tld 
<VirtualHost 9.20.1.1:80> 2881064151
    ServerName www.startup.tld 
    ServerAlias startup.tld 
 
    ProxyPass / http://127.0.0.2:10000/ 
    ProxyPassReverse / http://127.0.0.2:10000/ 
    ProxyPassReverse / http://www.startup.tld:10000/ 
    ProxyPassReverse / http://startup.tld:10000/ 
</VirtualHost> 
 
# Configuration to forward requests for reckless.tld 
<VirtualHost 9.20.1.1:80> 
    ServerName www.reckless.tld 
    ServerAlias reckless.tld 
 
    ProxyPass / http://127.0.0.2:10001/ 
    ProxyPassReverse / http://127.0.0.2:10001/ 
    ProxyPassReverse / http://www.reckless.tld:10001/ 
    ProxyPassReverse / http://reckless.tld:10001/ 
</VirtualHost> 

  一定要注意这里的 ProxyPreserveHost 指令。这个指令是 Apache 2 提供的,它解决了将正确的 HTTP 头转发给后端服务器的一些问题。因此,强烈建议使用 Apache 2 实例作为 facade 服务器。

  运行示例配置

  根用户应该运行每个配置。Apache 将取得配置文件中指定的特权,并将其用于所有与主机相关的进程。清单 5 说明运行示例的方法。

清单 5. 启动示例服务器

  /usr/sbin/apache -f /etc/apache/startup.tld.conf 
/usr/sbin/apache -f /etc/apache/nimrod.tld.conf 
/usr/sbin/apache2 -f /etc/apache2/facade.tld.conf 

  mod_proxy 方法的限制

  一定要注意,本文中描述的方法不适用于需要 SSL 连接的域。这是因为 SSL 协议不允许域的虚拟主机。由于这个限制,任何 SSL 主机必须以适当的方式执行,让每个 SSL 域使用它自己的 IP/端口组合。这个限制对所有 Apache 配置都存在,使用这个解决方案的 Apache 也不例外。仍然可以在它们的所有者的用户 ID 下运行 SSL 域。

时间: 2024-08-29 09:46:18

将正确的 HTTP 头转发给后端服务器的一些问题的相关文章

nginx转发及后端服务器获取真实client的IP

针对nginx的模块介绍可以查阅wiki:http://wiki.nginx.org/Modules 常用模块:HTTP Core Proxy Rewrite Upstream 原理:squid,varnish以及nginx等,在做反向代理的时候,因为要代替客户端去访问服务器,所以,当请求包经过反向代理后,在代理服务器这里这个IP数据包的IP包头做了修改,最终后端web服务器得到的数据包的头部的源IP地址是代理服务器的IP地址,这样一来,后端服务器的程序给予IP的统计功能就没有任何意义,所以在做

Webpack系列:在Webpack+Vue中如何将对后端的http请求转到https的后端服务器中?

在上一篇文章(Webpack系列:在Webpack+Vue开发中如何调用tomcat的后端服务器的接口?)我们介绍了如何将对于webpack-dev-server的数据请求转发到后端服务器上,这在大部分情况下就够用了. 然后现在问题又来了,在生产环境下接口一般采用https协议,如果我们要把数据请求转发到生产服务器上怎么办? 首先会想是不是把上一篇博文中提到的proxyTable改成https就可以了,如下:     proxyTable: {                '/appserve

LVS(Linux Viretual Server) 负载均衡器 + 后端服务器

定义: LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统. 结构: 一般来说,LVS集群采用三层结构,其主要组成部分为: A.负载调度器(load balancer),它是整个集群对外面的前端机,负责将客户的请求发送到一组服务器上执行,而客户认为服务是来自一个IP地址(我们可称之为虚拟IP地址)上的. B.服务器池(server pool),是一组真正执行客户请求的服务器,执行的服务有WEB.MAIL.FTP和DNS等. C.共享存储(

Angularjs学习笔记(四)----与后端服务器通信

一.使用$http进行XHR和JSONP请求 1.1 XHR请求 GET:$http.get(url,config) POST:$http.post(url,data,config) PUT:$http.put(url,data,config) DELETE:$http.delete(url,config) HEAD:$http.head 1.2 JSONP请求 $http.jsonp(url,config) 1.3 方法参数说明 url:调用目标URL data:请求体中送出的数据 confi

nginx_upstream_check_module监控后端服务器http

nginx_upstream_check_module 是专门提供负载均衡器内节点的健康检查的外部模块,由淘宝的姚伟斌大神开发,通过它可以用来检测后端 realserver 的健康状态.如果后端 realserver 不可用,则后面的请求就不会转发到该节点上,并持续检查几点的状态.在淘宝自己的 tengine 上是自带了该模块.项目地址:https://github.com/yaoweibin/nginx_upstream_check_module . 下面的是一个带后端监控检查的 nginx.

nginx反向代理-后端服务器组设置

nginx服务器的反向代理时其最常用的重要功能之一,在实际工作中应用广泛,涉及的配置指令也比较多.下面会尽量详细地介绍对应的指令,及其使用状态. 反向代理一般是互联网需要向内网拉取资源,比如访问一个web网站时,互联网应用通过一个代理服务器到后面真实的web服务器拉取应用所需的数据. nginx服务器反向代理用到的指令如果没有特别的说明,原则上可以出现在nginx配置文件的http块,server块和location块中,但是同正向代理一样,一般是搭建在nginx服务器中单独配置一个server

【转】nginx 主动式后端服务器健康检查

原文链接  http://tengine.taobao.org/document_cn/http_upstream_check_cn.html ngx_http_upstream_check_module 该模块可以为Tengine提供主动式后端服务器健康检查的功能. 该模块在Tengine-1.4.0版本以前没有默认开启,它可以在配置编译选项的时候开启:./configure --with-http_upstream_check_module Examples http { upstream

nginx后端服务器返回给nginx502、504、404、执行超时等错误状态的解决方法

今天公司的网站访问的时候全部变成404页面,查看网站的文件没有问题,来检查nginx的配置的时候,发现后端的一台服务器不可用,直接访问那台后台的服务器的时候,返回的是404页面,因为upstream 里面设置了ip_hash.所以导致我怎么刷新都是404页面.由此想到了nginx的一个功能,就是当后端的服务器返回给nginx502.504.404.执行超时等错误状态的时候,nginx会自动再把这个请求转发到upstream里面别的服务器上面,从而给网站用户提供更稳定的服务. 配置如下:locat

从app上传图片到php,再上传到java后端服务器的方法一览

在现在的网络开发中,上传图片类的需求实在是太普通不过了,但是对于怎么样做到上传图片,对于刚开始建立项目的时候,还是有点不知所措的.也许有幸,我们做的项目是之前已经有人写过类似的用例了,那么我们只需要依葫芦画瓢就行了. 好好了解下图片上传(文件上传)的方式,对于认知的提升还是有好处的.而且说不定哪天你就有个这样的需求呢,这里是一条龙上传. 本文就一个从app到php层,再到java层的流程,演译下整个上传图片的流程吧. 一.app端获取用户选择的图片,转化为输入流,上传至php前端接口: pack