前几天看见新闻说Chrome浏览器弹出推荐大陆用户使用搜狗,觉得搜狗的技术可能不错,就打开浏览器,输入sogou.com,结果吓一跳……如图:
搜狗用的HTTP,被运营商插广告插的他妈都认不出来了……而如果通过HTTPS访问https://m.sogou.com,整个世界清净了:
HTTP是明文传输的,你搜什么看什么密码多少,路由器的所有者(宽带运营商、伪造WiFi者)全知道,它们可以干很多坏事,以前是抓取密码盗号,现在是流量劫持插广告、抓取身份证图片和手机号去卖。而HTTPS是加密的,能避免这些问题。
但像搜狗这样有HTTP也有HTTPS,但没有强制跳转,是没用的……下面我们看看各个网站是否部署了https和强制https:
网站 | http | https |
---|---|---|
github.com | 跳转https | OK |
google.com.hk | OK | OK |
microsoft.com | OK | OK |
wechat.com | OK | OK |
qq.com | OK | 打不开 |
alipay.com | 跳转https | OK |
tmall.com | 跳转https | OK |
jd.com | 跳转https | OK |
you.163.com | OK | 跳转http |
163.com | OK | 打不开 |
weiyang.cn | OK | 打不开 |
weibo.com | OK | 跳转http |
可以看出:
- qq.com和163.com这种老网站没部署https,历史遗留原因可以理解,新的产品比如微信的wechat.com部署了https。
- 购物金融产品为了安全保密必须使用https,支付宝天猫京东各家都开启了强制跳转https,是正确的。而网易严选(you.163.com)竟然强制使用http,技术落后于行业平均水平。
- 微博竟然把https跳回了http,技术实在是太差了……
正好玛雅(maya.io)之前部署在AWS,最近迁移到阿里云国内服务器,开启了HTTP强制跳转HTTPS,记录一下过程,供后来者参考。
购买SSL证书
在甘地(gandi.net)买域名送1年证书,买根域名得到的一个证书会同时支持根域名和www子域名。文档:
If you have a single-address certificate to activate, if you create the CSR with the bare domain (e.g. example.com), the www subdomain is added automatically by the CA, for example, example.com will secure both example.com andwww.example.com.
”www” 子域名会由认证机构(CA)自动新增;举例来说,”example.com” 将同时保障 “example.com” 及 ”www.example.com” 的安全性。
不管用的是不是阿里云的服务器,都可以在阿里云买证书(aliyun.com),对于创业公司来说,免费的DV SSL就可以了(不知第二年是否免费续费)。实测买根域名证书也支持www,证书还可下载拿到别的地方用。
部署SSL证书
目前成熟的Web架构是负载均衡(load balance,简称LB)+服务器(web server),现在AWS和阿里云的LB都提供了HTTPS证书解析功能,把证书和私钥贴进去,创建443端口的监听(记得勾选“高级配置——SLB监听协议 X-Forwarded-Proto”),和80一样转发到web server即可。
开启强制跳转HTTPS
AWS是从LB的80和443转发到web的80,阿里云是从LB的80和443转发到web的9080,如果我们能知道用户是通过http 80访问的,跳转https就行了。
阿里云官方文档里推荐的方法是把80和443转发到两个端口……这非常不方便,尤其是用docker时,难道再部署一个docker专门用来跳转?
通过debug/抓包可以看到,阿里云的LB在header里添加了“X-Forwarded-Proto”,值为http或https,用来表示用户请求是否加密,这是业界惯例,AWS也是如此。
所以,在代码/apache里判断X-Forwarded-Proto即可,如果不是https,则进行307跳转(不是301)。比如nodejs代码如下:
server.enable('trust proxy');
server.use((req, res, next) => {
// req.secure 包含了 req.get('X-Forwarded-Proto') == 'https'
// see http://expressjs.com/en/api.html#req.secure
if (req.secure) {
if (process.env.HSTS) {
// set HSTS. 非加密传输时设置的HSTS字段无效
res.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
}
} else if (process.env.FORCE_HTTPS) {
res.redirect(307, `https://${process.env.DOMAIN}${req.url}`);
return true;
}
next();
return true;
});
nginx代码如下:
server {
listen 80;
server_name example.com;
if ($http_x_forwarded_proto != 'https') {
return 307 https://$host$request_uri;
}
}
阿里云配置有两个地方要注意:
- 删除默认的TCP转发,改成HTTP的
- HTTP和HTTPS都要勾选“高级配置”里的“SLB监听协议 X-Forwarded-Proto”
配置完成,用curl测试结果如下: