Apache HTTP服务器 2.0版本
The solution of this problem is trivial and is left as an exercise for the reader.
-- Standard textbook cookie
由于SSL、HTTP和Apache三者共同对请求进行处理, 使得在支持SSL的网站服务器上实现特殊的安全制约变得不那么简单。 本节介绍了普通情况下的解决方案,作为找出最终方案的第一步。 采用这些方案以前,先要尽量地去理解,不了解其限制和相关性就贸然使用是最糟糕的了。
可以这样建立一个仅使用SSLv2协议及其密码算法的服务器:
SSLProtocol -all +SSLv2
SSLCipherSuite SSLv2:+HIGH:+MEDIUM:+LOW:+EXP
如下设置为仅使用最强的七种密码算法:
SSLProtocol all
SSLCipherSuite HIGH:MEDIUM
这个功能被称为以服务器为网关的加密(Server Gated Cryptography [SGC]),
在README.GlobalID
文档中有详细说明。
简单地说就是:服务器拥有一个由来自Verisign的一个特殊的CA证书签发的服务器身份证,
从而在对外浏览器上实现强加密。
其过程如下:浏览器使用对外密码进行连接,服务器返回其全局ID身份证,
浏览器校验后在后继HTTP通讯产生之前提升其密码组。
现在的问题是:如何允许这样的提升,而又强制性地使用强加密。
换句话说就是:浏览器必须在开始连接时就使用强加密,或者提升倒强加密,
但是维持对外密码是不允许的。以下巧妙地解决了这个问题:
# 允许在初始握手阶段使用所有的密码,
# 以允许对外服务器通过SGC功能提升密码组
SSLCipherSuite ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
<Directory /usr/local/apache2/htdocs>
# 但是最终会拒绝所有没有提升密码组的浏览器
SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128
</Directory>
显然,不能使用服务器全局设置SSLCipherSuite
,它会限制密码为强类型。
但是,mod_ssl允许重配置针对目录的密码组,并自动进行一个带有服从新配置的SSL参数的重协商。
因此,其解决方案成了:
# 在一般情况下的处理是宽松的
SSLCipherSuite ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
<Location /strong/area>
# 但对于https://hostname/strong/area/ 及其以下的内容
# 要求强密码
SSLCipherSuite HIGH:MEDIUM
</Location>
如果你了解你的用户群体(比如:一个封闭的用户组),正如在一个Intranet中,
则可以使用一般的证书认证。
所有要做的事情只是,建立由你自己的CA证书签发的客户证书ca.crt
,
并依此证书校验客户。
# 索取ca.crt中直接由CA证书签发的客户证书
SSLVerifyClient require
SSLVerifyDepth 1
SSLCACertificateFile conf/ssl.crt/ca.crt
这又要用到mod_ssl
提供的针对目录的重配置功能:
SSLVerifyClient none
SSLCACertificateFile conf/ssl.crt/ca.crt
<Location /secure/area>
SSLVerifyClient require
SSLVerifyDepth 1
</Location>
其关键在于对客户证书的各个组成部分进行验证,
一般就是指验证Distinguished Name (DN)的全部或部分。
有基于mod_auth
和基于SSLRequire
类型的两种方法以验证。
第一种方法适合用于客户完全属于不同类型,
并为所有客户建立了密码数据库的情形;
第二种方法适用于客户都属于一个被编码写入DN的公共分级的一部分的情形,
因为匹配客户会更容易。
第一种方法:
SSLVerifyClient none <Directory /usr/local/apache2/htdocs/secure/area> SSLVerifyClient require SSLVerifyDepth 5 SSLCACertificateFile conf/ssl.crt/ca.crt SSLCACertificatePath conf/ssl.crt SSLOptions +FakeBasicAuth SSLRequireSSL AuthName "Snake Oil Authentication" AuthType Basic AuthUserFile /usr/local/apache2/conf/httpd.passwd require valid-user </Directory>
/C=DE/L=Munich/O=Snake Oil, Ltd./OU=Staff/CN=Foo:xxj31ZMTZzkVA /C=US/L=S.F./O=Snake Oil, Ltd./OU=CA/CN=Bar:xxj31ZMTZzkVA /C=US/L=L.A./O=Snake Oil, Ltd./OU=Dev/CN=Quux:xxj31ZMTZzkVA
第二种方法:
SSLVerifyClient none <Directory /usr/local/apache2/htdocs/secure/area> SSLVerifyClient require SSLVerifyDepth 5 SSLCACertificateFile conf/ssl.crt/ca.crt SSLCACertificatePath conf/ssl.crt SSLOptions +FakeBasicAuth SSLRequireSSL SSLRequire %{SSL_CLIENT_S_DN_O} eq "Snake Oil, Ltd." \ and %{SSL_CLIENT_S_DN_OU} in {"Staff", "CA", "Dev"} </Directory>
假设Intranet客户的IP地址是192.160.1.0/24,Intranet站点子区域的URL是/subarea
,
则可以在HTTPS虚拟主机以外这样配置:(以同时作用于HTTPS和HTTP)
SSLCACertificateFile conf/ssl.crt/company-ca.crt <Directory /usr/local/apache2/htdocs> # subarea以外的区域只允许来自Intranet的访问 Order deny,allow Deny from all Allow from 192.168.1.0/24 </Directory> <Directory /usr/local/apache2/htdocs/subarea> # 在subarea以内,允许所有来自Intranet的访问, # 但对来自Internet的访问,仅允许HTTPS + Strong-Cipher + Password # 或者 HTTPS + Strong-Cipher + Client-Certificate # 如果使用了HTTPS,则确保使用强加密 # 同时允许客户以基本认证的形式认证 SSLVerifyClient optional SSLVerifyDepth 1 SSLOptions +FakeBasicAuth +StrictRequire SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128 # 强制来自Internet的客户使用HTTPS RewriteEngine on RewriteCond %{REMOTE_ADDR} !^192\.168\.1\.[0-9]+$ RewriteCond %{HTTPS} !=on RewriteRule .* - [F] # 允许网络访问和基本认证 Satisfy any # 控制网络访问 Order deny,allow Deny from all Allow 192.168.1.0/24 # HTTP基本认证 AuthType basic AuthName "Protected Intranet Area" AuthUserFile conf/protected.passwd Require valid-user </Directory>