共计 7322 个字符,预计需要花费 19 分钟才能阅读完成。
为了提高网站的安全性,一般会在比较敏感的部分页面采用 https 传输,比如注册、登录、控制台等。像 Gmail、网银等全部采用 https 传输。
https/ssl 主要起到两个作用:网站认证、内容加密传输和数据一致性。经 CA 签发的证书才起到认证可信的作用,所有有效证书均可以起到加密传输的作用。
浏览器与 SSL 证书
![SSL 证书与 HTTPS 应用部署小结 SSL 证书与 HTTPS 应用部署小结](https://s.xgss.net/sky/2022-01-21/ba152233f5558b8eebd8415dae12d2e3.jpg)
Chrome 网站安全性指示器说明:http://support.google.com/chrome/bin/answer.py?hl=zh&answer=95617
SSL 最主要应用是在浏览器和 Web 服务器之间,尽管不限于此。当然,安全本身是重要的内在属性。但 在表面上看,部署 SSL 就是为了让用户浏览器里看起来更安全一些,以增加用户的信任感。所以很多企业更把它当作门面,而签发机构也为此卖高价,尤其是国内的价格明显高于国外的。
实际上 SSL 证书也可以做客户端认证,用户拥有自己特有的证书,用它可以证明自己的身份,当然也就用不着用户名和密码了。但这种用的很少,一般 web 服务器也不支持。
内容加密传输更安全,如果只是为了加密,使用自签发的证书也可以,但浏览器无法验证证书,所以会给出一个非常吓人的警告,所以自签发证书不 适合给外人使用,只适合内部使用,把这个证书 加入到自己的信任列表或忽略证书验证即可,以后就不会继续拦截了。
证书需要被少数一级或二级 CA 认证才有效。计算机安全中的信任就是一个信任链的关系,信任链最顶端的被称为根证书。
自签发的证书在技术上是完全一样的,仅用于加密传输是没问题的。但是不能被外人信任,所以一般仅用于内部使用。除了自签发不被信任,如果证书过期、已被吊销或者非证书所代表的域名也都是不被信任的,导致证书验证出错。
用于网站的证书需要被大众信任,所以不能自签发的证书,那就申请(购买)一个吧。
申请证书
1. 证书类别
按证书包含域名数量分为:
- 单域名:只针对这个域名有效,不能用在其它域名下。
- 多域名:只针对列出的多个域名有效。
- 通配符域名(wildcard):对任意子域名有小,显示的是 *.example.com。
注意:SSL 所说的单个域名是一个完整的域名,一个子域名就算一个,而非一个顶级域名。
如果网站有很多子域名,只需要 申请真正需要的 域名证书。
按验证的类别分:
- 域名认证(Domain Validation):认证你的域名所有权和网站,申请验证简单,几分钟即可。
- 组织机构认证(Organization Validation):认证的域名和公司信息,需要提交公司资料认证。
- 扩展认证(Extended Validation,简称 EV):这种证书会在浏览器中出现“很明显”的绿色地址栏,给用户的可信度最高。有安全评估保证。
个人或小站点可用一类或二类,企业一般用二类认证,少数企业会用到 EV 认证。
2. 证书价格
看了看网上 SSL 证书的价格,便宜的一般都是 10 美元左右一个子域名 / 每年,按不同类别、不同品牌等价格在几十美元到几百美元一年。比如能显示绿色地址栏的 EV 证书和通配符证书贵一些。国内自己的或代理的,比国外贵不少,动辄几千元。其实就是由可信源认证了一下,类似于办证,用起来没什么差别,并非越贵越好。
3. 签发机构(“卖家”)
![SSL 证书与 HTTPS 应用部署小结 SSL 证书与 HTTPS 应用部署小结](https://s.xgss.net/sky/2022-01-21/83e515cefe385e80d08814e5f1eb6b32.jpg)
StartSSL、Go Daddy 的比较便宜,GeoTrust、Comodo 的价格适中,Thawte 和 VeriSign 的价格较贵。
VeriSign 现在归属 赛门铁克,在国内是由天威诚信代理的。世界真小,天威诚信就在我很多年以前的东家(启明星辰)大楼里,地下一层是他们的机房,我还进去过一次。
4. 免费的StartSSL
唯一免费的是 StartSSL, 其它的一般只提供 30 免费试用。
但 StartSSL 提供的免费证书是一类的、仅对域名和 email 进行验证,不对组织做验证(也就是面向自然人的,非面向组织机构的),不过
仅作为域名验证和数据加密也够了,并且浏览器也认它,一般人也不会去看你的证书级别。适合个人和初创网站使用,以后有钱了再申请个收费的替换即可。
我很顺利地申请到了免费的 StartSSL 证书,分别用在两个子域上。
最后,证书签发给你后,最主要是保护好私钥证书,这个丢失或泄漏就完了。因为如果被别人利用也就毫无安全性了,需要向证书签发机构申请撤销证书并申请新的证书,这当然也是要收费的。
应用规划、配置和调整
并不是说有了 SSL 证书就没事了,还要考虑应用中的使用问题,需要规划、服务器配置、应用调整等多个环节。
SSL 比 http 要消耗更多 cpu 资源(主要是在建立连接的阶段,之后还要对内容加密),所以对一般网站,只需要对部分地方采用 https,大部分开放内容是没必要的,具体取决于你的业务要求。比如对于很多 安全要求较低的 网站,完全不用 https 也是可接受的。
某些页面是同时支持 http 和 https,还是只支持 https、强制 https?
同时支持就是用户用什么协议访问都可以,那么用户的请求主要就是由页面本身的链接引导来的,因为一般用户不会自己特意去修改地址栏的。
一般我们的网站可以做成同时支持 http 和 https,都可以访问。但是这就容易有后面说的混合内容或混合脚本的问题。
还可以规划为部分页面支持 https,一般公开页面不用 https,只是将部分地方的链接改为 https 就可以了。专门期望以 https 访问的页面中,引用的绝对 URL 可以明确的使用 https 链接。
是否强制 https?对于安全性高的网站或网站中的部分页面,可以强制使用 https 访问, 即使 用户 在地址栏里手工把 https 改为 http, 也会被自动重定向回 https 上。比如 可以通过配置 web 服务器 rewrite 规则 将这些 http url 自动重定向到对应的 https url 上(这样维护比较简单),而不用改应用。
解决混合内容问题(http 和 https)
混合内容是指:在 https 的页面中混合了非 https 的资源请求,比如图片、css、js 等等。如果是混合了非 https 的 js 代码,则被称为混合脚本。
混合内容的危害:如果只是混合了不安全的图片和 css,那么受中间人攻击篡改,一般只会影响页面的显示,危害相对小一点。如果是混合了不安全的 js 代码,则这个不安全的 js 可以完全访问和修改页面中的任何内容,这是非常危险的。
另请参看,Chrome 对混合脚本危害的说明与提示:Trying to end mixed scripting vulnerabilities
所以,只有页面本身和所有引用的资源都是 https 的浏览器才认为是安全的,只要其中引用了非安全资源(即使图片),浏览器都会给出 不安全 的提示,特别是有 js 的情况。如果浏览器提示不安全,那样我们就达不到原来目的了。我们费了半天功夫去申请 SSL 证书,配置 Web 服务器,最后如果因为混合内容而前功尽弃就太糟了。咱继续努力吧,想办法让所有引用资源都是安全的。
理论上,混合了第三方的内容,即使是 SSL 的第三方内容也不是很好。因为用户信任的是你,而不是第三方,即使第三方也支持 https,但你能保证第三方就绝对安全吗。不引用任何第三方才是绝对安全的,但这样太严格了,安全其实也是一个 tradeoff 的问题,需要考虑很多方面的平衡。还好,起码 现在 浏览器认为已经是安全的了。
引用第三方文件的问题(如 CDN 分发的文件)
简单地说,这个问题要么有第三方提供 https 支持,要么不用它(用自己本地的)。
一般我们会引用由 CDN 分发的文件,比如某个 js 库文件,而不用访问自己网站上的,这样借助 CDN 网络可以加快速度,这当然很好。
但是,如果我们在页面中使用绝对 URL 直接引用这个文件就无法自动使用 https 了!出现了混合协议内容,浏览器又该“变脸”了。
当 SSL 遇上 CDN 或 其它第三方文件就有点麻烦,因为很多 CDN 还不支持 SSL。如果支持 https 的话就可以直接用 https 的绝对 URL 了,即使是同时支持 http 和 https 的页面,这样做也不算太浪费,起码解决了问题。
因为 CDN 的云文件提供者,一般为每个 cdn 用户创建一个单独的子域名来使用,这样的话,CDN 提供者要想支持 https 就必须支持所有可能的子域名,因此要求 CDN 提��方使用那种通配符子域名的证书。
相对 URL、绝对 URL 与 只缺协议的 URL(Protocol Relative URL)
相对路径比较简单,自动匹配用户请求的 http 或 https 协议。
但是绝对 url 则不成,因为绝对 url 已经明确地写上了协议:http://www.example.com/jquery.js。
这个问题还有一个办法解决,你一定没见过这种形式: //www.example.com/jquery.js
哈哈,一个缺少协议的 URL(实际上还算是相对 URL),这种形式可以在浏览器中被正确补充上合适的协议!很多人都用这种方法。
但是,这里有点小问题,IE7 和 IE8 处理这种缺少协议的 URL 的 css 文件时,同一个 css 文件会下载两次,详见Steve 的文章。
JS 自动判断当前协议
现在我们经常用 js 来加载其它 js 文件或 其它别的文件,如果是请求是相对 URL 则没问题,如果是绝对 URL 怎么办?
其实 js 脚本可以这样:document.location.protocol 等于 ‘http:’ 还是 ‘https:’ 来判断。例如在 Google Analytics 的嵌入代码中:
ga.src = (‘https:’ == document.location.protocol ? ‘https://ssl’ : ‘http://www’) + ‘.google-analytics.com/ga.js’;
应用程序中如何判断访问协议
对于动态页面,如 jsp、php 等,也是可以动态判断当前是否使用了 https 协议的。所以应用可以根据动态判断,来生成不同的引用 URL。这样虽然有点麻烦,但也算是解决了自动识别协议的问题,当然相对路径总是不需要处理的。
比如在 jsp 中:
- request.isSecure() 为 true 表示当前为 https,false 表示 http 访问
- request.getScheme() 返回字符串 https 或 http
注意,如果 tomcat 部署在其它 web 服务器代理的后面,需要正确配置好才能返回正确结果,见本文最后一部分。
同源策略的问题
最后提醒一点:http 和 https 是不同源的!即使后面的内容都一样。所以 ajax 发请求的时候要使用正确协议的绝对 URL 才行。
相对 URL 的 ajax 请求没关系。
Nginx 配置
小结一下 Nginx 配置 SSL 注意的问题,详细安装配置内容请参考其它资料,如官方 SSL 模块 和 https 配置文档。
1. 首先检查一下是否已安装了 SSL 模块,因为默认是不包含的。
用 nginx -V 命令检查一下。如果没有 ssl 模块则需要重新安装(建议升级到最新版本),注意安装时加上 ssl 选项:
./configure –with-http_ssl_module
另外,nginx 需要依赖 openssl 提供 ssl 支持,这个也要有。
2. nginx.conf 中的典型配置示例
listen 80;
listen 443 ssl;
ssl_certificate cert.pem; #修改具体文件
ssl_certificate_key ssl.key; #修改具体文件
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;
上面第 2 - 4 项是关键。这些配置放在 server 块就可以对其中的所有 location 生效了,并且同时支持 http 和 https。或者把 http 和 https 分开配置也很常见。
3. 合并证书配置文件
和 Apache 配置不同,Nginx 需要将服务器证书和 ca 证书链合并到一个文件中,作为 ssl_certificate 配置的内容。
例如,按照证书链从下向上的顺序,我有三个证书:
- ssl.crt(自己域名的服务器证书)
- sub.class1.server.ca.pem(startssl 的一类证书)
- ca.pem(startssl 的根证书)
把它们的内容按顺序连接到的一个文件中,每个内容另起一行,中间没有空行或空格。
4. 避免启动时输入密码
配好之后,启动 nginx 要你输入密钥的密码。这是因为 ssl_certificate_key 配置对应的文件(也就是 startssl 给你的私钥文件)内容是加密的,需要输入你创建这个时设置的密码才能解密。这样私钥虽然很安全,但是每次重启服务都要输入一次密码也太麻烦了。其实,只要 证书改为解密了的内容,就可以避免每次输入密码。用如下命令即可:
openssl rsa -in ssl.key -out newssl.key 输入密码,就生成了解密后的私钥内容,使用这个就 OK 了。
但是就像前面说的,一定要在服务器上保护好它,例如:
chmod 400 ssl.key(仅 root 可读)
5. 优化 SSL 配置
SSL 很消耗 CPU 资源,尤其是在建立连接的握手阶段。一是通过开启 keepalive 可以重用连接。二是可以重用和共享 ssl session,见上面 ssl_session 相关配置。
独立 Tomcat+SSL
Tomcat 是很常见的 Java 应用服务器,当然也可以作为独立的 Web 服务器,所有用户请求直接访问 tomcat。
如果 Tomcat 作为独立的 Web 服务器,那么就需要配置 Tomcat 就可以了,文档参考这里 和 这个。主要是配置存放证书的 Keystore 和 连接器 Connector。
Java 的 keystore
keystore 是 Java 中专用并内置的一个类似于 openssl 的工具,一个 keystore 文件就是一个“保险箱”(database),专门存放证书和密钥,和相关的管理功能:生成自签发的证书、密钥、导入导出等。可以通过 keytool 命令或 Java api 交互。
利用 keytool 命令将你的证书导入进去。
Tomcat 中Connector
tomcat 中有三种 Connector 实现:block、nio 和 APR。前两者使用 Java SSL(这需要 keystore 的配置),APR 使用 OpenSSL(不需要用 keystore,直接指定证书),配置略有不同。
Nginx+Tomcat+SSL
实际上,大 规模的网站都有很多台 Web 服务器和应用服务器组成,用户的请求可能是经由 Varnish、HAProxy、Nginx 之后才到应用服务器,中间有好几层。而中小规模的典型部署常见的是 Nginx+Tomcat 这种两层配置,而 Tomcat 会多于一台,Nginx 作为静态文件处理和负载均衡。
如果 Nginx 作为前端代理的话,则 Tomcat 根本不需要自己处理 https,全是 Nginx 处理的。用户首先和 Nginx 建立连接,完成 SSL 握手,而后 Nginx 作为代理以 http 协议将请求转给 tomcat 处理,Nginx 再把 tomcat 的输出通过 SSL 加密发回给用户,这中间是透明的,Tomcat 只是在处理 http 请求而已。因此,这种情况下不需要配置 Tomcat 的 SSL,只需要配置 Nginx 的 SSL 和 Proxy。
在代理模式下,Tomcat 如何识别用户的直接请求(URL、IP、https 还是 http )?
在透明代理下,如果不做任何配置 Tomcat 认为所有的请求都是 Nginx 发出来的,这样会导致如下的错误结果:
- request.getScheme() // 总是 http,而不是实际的 http 或 https
- request.isSecure() // 总是 false(因为总是 http)
- request.getRemoteAddr() // 总是 nginx 请求的 IP,而不是用户的 IP
- request.getRequestURL() // 总是 nginx 请求的 URL 而不是用户实际请求的 URL
- response.sendRedirect(相对 url) // 总是重定向到 http 上 (因为认为当前是 http 请求)
如果程序中把这些当实际用户请求做处理就有问题了。解决方法很简单,只需要分别配置一下 Nginx 和 Tomcat 就好了,而不用改程序。
配置 Nginx 的转发选项:
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
配置 Tomcat server.xml 的 Engine 模块下配置一个 Value:
<Valve className=”org.apache.catalina.valves.RemoteIpValve” remoteIpHeader=”X-Forwarded-For”protocolHeader=”X-Forwarded-Proto” protocolHeaderHttpsValue=”https”/>
配置双方的 X-Forwarded-Proto 就是为了正确地识别实际用户发出的协议是 http 还是 https。X-Forwarded-For 是为了获得实际用户的 IP。
这样以上 5 项测试就都变为正确的结果了,就像用户在直接访问 Tomcat 一样。
本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-12/138671.htm
正文完
星哥说事-微信公众号
![post-qrcode](https://www.xgss.net/wp-content/uploads/2023/07/二维码-微信公众号256x256.jpg)