分类目录归档:WEB服务

fredzneg与你一起对apache,nginx,squid,trafficserver学习相关知识及探讨!

Caddy nginx服务器QUIC部署

Caddy 简介

Caddy是一个Go语言写的,易于使用的通用Web服务器。它具有如下的一些功能:

  • 配置简单:Caddy服务器的运行可以通过Caddyfile配置文件进行配置,Web服务配置起来非常简单。
  • 自动的HTTPS:它可以自动地为我们申请 Let’s Encrypt 域名证书,管理所有的密码学设施,并进行配置。
  • HTTP/2:默认支持HTTP/2(由Go标准库支持)
  • 虚拟主机托管:Caddy支持TLS的SNI。SNI是在2006年加入TLS的一个TLS扩展。客户端在TLS握手的Client Hello消息中,通过SNI扩展将请求的资源的域名发送给服务器,服务器根据SNI的域名来下发TLS证书。这样就可以在具有单个公网IP的同一台主机上部署多个不同的域名的服务。可以为Caddy服务的不同域名配置不同的证书和密钥。
  • QUIC支持:Caddy实验性地支持QUIC协议,以获取更好的性能。
  • TLS session ticket key rotation for more secure connections
  • 良好的可扩展性:因此Caddy非常方便针对自己的需求做定制。
  • 随处运行:这主要与Go应用程序的特性有关。Go的模块都被编译为静态库,这使得Go的应用程序在编译为可执行文件时都是静态链接的,因而依赖的动态库极少,这使得部署使用非常方便。

自动的HTTPS、HTTP/2支持、QUIC支持和随处运行这些特性非常有吸引力,特别是对QUIC的支持。

此外,Caddy的性能非常好。下面两幅图是我的静态个人博客站点,分别是用Caddy和nginx作为Web服务器,打开主页所需的加载时间对比:
Service with Caddy

Service with nginx

上面的图显示了以Caddy作为Web服务器,主页的加载时间只有680ms;下面的图显示以nginx作为Web服务器,主页的加载时间则长达1.99s,要慢接近2倍。

Caddy部署

Caddy应用程序不依赖于其它组件,且官方已经为不同的平台提供了二进制可执行程序。可以通过如下三种方式之一安装Caddy:

  • 在 下载页,通过浏览器定制自己需要的功能集,并下载相应的二进制可执行程序。
  • 预编译的 最新发行版 二进制可执行程序。
  • curl getcaddy.com 来自动安装:curl https://getcaddy.com | bash

将caddy的路径加如PATH环境变量中。之后可以 cd 进入网站的文件夹,并运行 caddy来提供服务。默认情况下,Caddy在2015端口上为网站提供服务。

要定制网站提供服务的方式,可以为网站创建名为Caddyfile的文件。当运行 caddy 命令时,它会自动地在当前目录下寻找并使用Caddyfile文件来为自己做配置。

要了解更多关于Caddyfile文件的写法,可以参考 Caddyfile 文档

注意生产环境网站默认是通过HTTPS提供服务的。

Caddy还有命令行接口。运行caddy -h 可以查看基本的帮助信息,或参考 CLI文档 来了解更多详情。

以Root运行:建议不要这样做。但依然可以通过像这样使用setcap来监听端口号小于1024的端口:sudo setcap cap_net_bind_service=+ep ./caddy

由源码运行

注意:需要安装 Go 1.7或更新的版本才可以。

  1. go get github.com/mholt/caddy/caddy
  2. cd 进入网站的目录
  3. 执行caddy(假设 $GOPATH/bin 已经在 $PATH 中了)

Caddy的 main() 再caddy子目录下。要编译Caddy,可以使用在那个目录下找到的 build.bash

在生产环境运行

Caddy项目官方不维护任何系统特有的集成方法,但下载的文档中包含了社区共享的 非官方资源,用以帮助在生产环境运行Caddy。

以何种方式运行Caddy全由自己决定。许多用户使用 nohup caddy & 就可以满足需求了。其他人使用 screen。有些用户需要再重启之后就运行Caddy,可以在触发重启的脚本中来做到这一点,通过给init脚本添加一个命令,或给操作系统配置一个service。

可以看一下我的个人博客站点的完整Caddyfile内容:

					
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
					
wolfcstech.com:80 www.wolfcstech.com:80 {
root /home/www-data/www/hanpfei-documents/public
redir 301 {
if {>X-Forwarded-Proto} is http
/ https://{host}{uri}
}
}
wolfcstech.com:443 www.wolfcstech.com:443 {
tls /home/www-data/www/ssl/chained.pem /home/www-data/www/ssl/domain.key
#tls test@admpub.com
root /home/www-data/www/hanpfei-documents/public
gzip
log ../access.log
}

启用QUIC

Caddy 0.9 已经实验性地提供了对QUIC的支持,这主要通过 lucas-clemente/quic-go 来实现。要尝试这个特性,可以在运行caddy时加上 -quic 标记:

					
1
					
$ caddy -quic

这样执行之后,则带有TLS加密的Web服务,在客户端支持QUIC时,将默认通过QUIC协议来完成数据的传输。

不启用QUIC时,在启动caddy之后,在服务器端查看已打开的端口号:

					
1
2
3
4
5
6
7
					
# lsof -i -P
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
AliYunDun 1120 root 10u IPv4 2023899 0t0 TCP 139.196.224.72:40309->106.11.68.13:80 (ESTABLISHED)
. . . . . .
caddy 6163 root 6u IPv6 2338478 0t0 TCP *:80 (LISTEN)
caddy 6163 root 8u IPv6 2338479 0t0 TCP *:443 (LISTEN)
. . . . . .

而在通过如下命令:

					
1
					
# nohup ./caddy -quic &

启用QUIC提供Web服务之后,在服务器端查看已打开端口号,则可以看到如下内容:

					
1
2
3
4
5
6
7
8
9
					
# lsof -i -P
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
AliYunDun 1120 root 10u IPv4 2023899 0t0 TCP 139.196.224.72:40309->106.11.68.13:80 (ESTABLISHED)
. . . . . .
caddy 6222 root 6u IPv6 2338880 0t0 TCP *:80 (LISTEN)
caddy 6222 root 8u IPv6 2338881 0t0 TCP *:443 (LISTEN)
caddy 6222 root 9u IPv6 2338883 0t0 UDP *:80
caddy 6222 root 10u IPv6 2338885 0t0 UDP *:443
. . . . . .

Caddy 除了监听http的TCP 80端口和https 的TCP 443端口之外,还监听了UDP的80和443端口。

客户端支持

Chrome 52+ 支持QUIC而无需白名单,但需要确认 #enable-quic 标记已经被启用了。通过在Chrome浏览器的地址栏输入chrome://flags/

Enable QUIC

并根据需要启用QUIC。

然后通过Chrome打开你的网站,则它应该是以QUIC提供服务的!可以通过打开inspector 工具并进入Security tab来验证这一点。重新加载页面并点击来查看连接详情:

caddy005.png

如果你使用老版的Chrome,则为了省事,可以升级一下。

如果你不想升级,则可以:你将需要以特殊的参数来运行Chrome。再Mac上 (将YOUR_SITE替换为你的网站的实际域名)执行如下命令:

					
1
2
3
4
5
					
$ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--user-data-dir=/tmp/chrome \
--no-proxy-server \
--enable-quic \
--quic-host-whitelist="YOUR_SITE" "YOUR_SITE"

QUIC的好处

QUIC是基于UDP的TLS+HTTP的可靠传输协议。它加速了TLS握手为只有一个往返,避免了TCP慢启动,并提供了网络切换时的可靠性。通过QUIC可以让网站加载更快且更可靠。

问题排解

首先,确保在Caddyfile文件中为域名做了适当的设置,还要确保在启动Chrome的命令行中为域名做了适当的设置。

接着,网站必须使用一个真实的可信的证书(至少,是在写的时候)。

如果那都是好的,而且你对Go语言比较了解,则你可以添加 import "github.com/lucas-clemente/quic-go/utils",并在Caddy的main()函数的某个地方调用utils.SetLogLevel(utils.LogLevelDebug)。那将提供非常详细的输出。(注意这个log设施不是一个公共的API)。

当你进入chrome://net-internals/#events,你应该看到一些QUIC事件被标为红色。

Net Events

坚持原创技术分享,您的支持将鼓励我继续创作!

https://www.wolfcstech.com/2017/01/09/Caddy%20Web%E6%9C%8D%E5%8A%A1%E5%99%A8QUIC%E9%83%A8%E7%BD%B2/

nginx 对文件(动态页面) 进行缓存 ( nginx content caching)

refer to: http://nginx.com/resources/admin-guide/caching/ 首先看一个完整的例子:

  proxy_cache_path /tmp/nginx_cache keys_zone=cache_one:100m
                       loader_threshold=300 loader_files=200 max_size=200m;
  server {
          listen 80;
          server_name  www.tidev.in tidev.in;
          client_max_body_size 500m;
          charset utf-8;
          location / {
              proxy_hide_header "cache-control";
              proxy_hide_header Expires;

              proxy_ignore_headers Set-Cookie;
              proxy_ignore_headers Cache-Control;
              proxy_ignore_headers Expires;
              proxy_ignore_headers X-Accel-Expires;

              proxy_cache cache_one;
              proxy_cache_valid any 60s; # 任何内容,都缓存60秒钟 proxy_pass          http://tidev_servers; proxy_redirect default;
              proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header    X-Real-IP $remote_addr;
              proxy_set_header    Host $http_host;
              proxy_next_upstream http_502 http_504 error timeout invalid_header;

              proxy_cache_key $host$uri$is_args$args;
          }
          location ~ ^/assets/ {
              root /opt/app/tidev.in/public;
              expires 1y;
              add_header Cache-Control public;
              add_header ETag ""; break;
          }
  }

  upstream tidev_servers{
         server localhost:3600;
         server localhost:3601;
         server localhost:3602;
         server localhost:3603;
  }

注意:  

1. nginx 建议使用 1.7.9 以上版本, 亲测 1.7.9, 1.8.0 可用. 1.4.x 不可用. 所以,不要使用ubuntu 默认的 apt-get install 方式的nginx. 它是1.4的.

2. proxy_cache_path 务必出现在 proxy_cache 关键字之前, (也就是说,如果你用了include sites-enabled/*; , 那么,这句话要放在 proxy_cache_path之后!) 否则会报错: nginx: [emerg] the size 10485760 of shared memory zone “cache_one” conflicts with already declared size 0  

3. proxy_cache_key 是关键. 

          location / { proxy_hide_header "cache-control"; proxy_hide_header Expires; proxy_ignore_headers Set-Cookie; proxy_ignore_headers Cache-Control; proxy_ignore_headers Expires; proxy_ignore_headers X-Accel-Expires; proxy_cache cache_one; proxy_cache_valid any 60s; # 任何内容,都缓存60秒钟 proxy_pass http://tidev_servers; proxy_redirect default; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $http_host; proxy_next_upstream http_502 http_504 error timeout invalid_header; proxy_cache_key $host$uri$is_args$args;
          }

nginx可以对某个请求进行缓存,

例子:

http {
    ...
    proxy_cache_path /data/nginx/cache keys_zone=one:10m; server {
        proxy_cache one;
        location / {
            proxy_pass http://localhost:8000;
        }
    }
}

设置好了允许缓存后,进一步可以设置它的过期时间: (iteration 如何解释。。需要动手弄一下)

参与cache 过程 的,有两个角色, cache manager 和 cache loader:

1. cache manager 会循环的检查 cache的状态。当它发现 缓存的文件超过了 max_size 这个数目后,就会删掉最少访问的cache page. 

2. cache loader: 仅仅在nginx启动后 随之启动一次。它把之前的cache 信息加载到 shared memory中去。这在nginx启动的前几分钟会拖累nginx的速度。

以上的iteration, 比 loader_threshold(默认是200ms) 要少。 每次加载的文件数目小于 loader_files(默认是100),每个iteration 间隔 loader_sleeps (默认50ms)。 

下面是个例子: 

proxy_cache_path /data/nginx/cache keys_zone=one:10m
                 loader_threshold=300 loader_files=200; 

指定某个URL 要缓存

如果某个response来自 proxy_server, 并且request是 GET/HEAD 方法,则nginx 默认会把它做缓存. 

而且默认使用的key就是 url ,你也可以指定这个key, 例如:

proxy_cache_key “$host$request_uri$cookie_user”;

如果我们希望某个 url 至少被请求5次之后才被缓存,就这样:

proxy_cache_min_uses 5;

如果希望对POST和 DELETE进行缓存:

proxy_cache_methods GET HEAD POST;

下面的例子:对于 200 , 302的response, 缓存 10分钟, 

proxy_cache_valid 200 302 10m;  # 对于 200, 302,缓存10分钟
proxy_cache_valid 404 1m;       # 缓存1分钟 
proxy_cache_valid any 10m;      # 对于所有的响应,都缓存10分钟。

也可以根据条件来判断是否使用cache: ( cookie 中的变量:nocache, parameter中的变量:nocache 或者 comment, 只要有一个 不是空,也不是 0, 那么这个request就不会使用cache) 

proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment;

对于下面的例子:压根就不使用cache:

proxy_no_cache $http_pragma $http_authorization;

下面是一个更大的例子:

http {
    ... # 定义了一个 proxy_cache_path, : proxy_cache_path /data/nginx/cache keys_zone=one:10m
                     loader_threshold=300 loader_files=200 max_size=200m; # 这个server中有两个backend, 对应两种不同的cache策略 server {
        listen 8080; # cache的名字叫做 one (注意上面的 keys_zone=one:10m) proxy_cache one; # 对所有的 / 请求,都尽可能长久的缓存,不存在过期 location / {
            proxy_pass http://backend1; }

        
        location /some/path {
            proxy_cache_valid any 1m; # 任何内容,都缓存1分钟 proxy_cache_min_uses 3; # 访问3次后,触发缓存 proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment; # 设置好不使用缓存的规则 proxy_pass http://backend2; }
    }
}

注意: 如何调试呢?

1. 要设置log format, 把日志打印出来. 例如,配置文件为: (注意其中的 $upstream_cache_status, 这个变量最重要, 从它我们可以知道, 是HIT 还是MISS )

    log_format my_format '$remote_addr - $remote_user [$time_local]  ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" $upstream_cache_status'; access_log logs/my_access.log my_format;

2. 要有对应的 ignore headers, 如果后端返回的结果中,增加了 cache-control (也有一说是 set-cookie) 或者 啥的,就不行了. 

server{
            proxy_ignore_headers "cache-control";
            proxy_hide_header "cache-control";
} 

下面是一个完整的 nginx.conf例子;

http{ # 其他内容  proxy_cache_path /tmp/nginx_cache keys_zone=cache_one:10m
                     loader_threshold=300 loader_files=200 max_size=200m; log_format my_format '$remote_addr - $remote_user [$time_local]  ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" $upstream_cache_status';

    access_log logs/my_access.log my_format;

    server {
        listen 80; 

        location / { 
            proxy_ignore_headers "cache-control";
            proxy_hide_header "cache-control";
            proxy_cache cache_one;
            proxy_cache_valid any 10s; # 任何内容,都缓存10秒钟 proxy_pass http://rails_api;
        }

    }
    upstream rails_api{
        server localhost:3000;
    }

缓存用的哪些文件?

我们可以在 proxy_cache_path中设置, 例如:

    proxy_cache_path /tmp/nginx_cache keys_zone=cache_one:10m loader_threshold=300 loader_files=200 max_size=200m;

然后, 找到 /tmp/nginx_cache 目录, 如果某个 cache被命中过, 就会看到出现一个以md5 结果命名的文件:

:/tmp/nginx_cache$ ll
total 20 drwxrwxrwx 2 nobody sg552 4096 Sep 10 11:39 ./
drwxrwxrwt 10 root   root 12288 Sep 10 11:38 ../
-rw------- 1 nobody nogroup 594 Sep 10 11:39 f8924891f34a941a8342ccd19c4cf290 

上面中, 这个文件 “f89…” 就是缓存文件. 它的内容如下.

���U���������Ud����0""b4945c5f2d4b62faae53f44f44a5e946"
KEY: http://rails_api/prices/say_hi
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: text/html; charset=utf-8
ETag: "b4945c5f2d4b62faae53f44f44a5e946" Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 90fbc91b-e5a4-4279-8832-5d484cba7ba8
X-Runtime: 0.007120 Connection: close Server: thin 1.6.2 codename Doc Brown time is: 2015-09-10 11:39:58 +0800 

可以看出, 该静态文件, 以文本的形式缓存了 所有的response信息.


来源;http://siwei.me/blog/posts/nginx-nginx-content-caching

HTTPS 安全最佳实践(三)之服务器nginx,OpenSSL

我们在最佳实践文章中建议大家如何去配置协议和密码套件,但是如果服务器软件(nginx、apache等)所使用的ssl协议库存在SSL漏洞,或者不支持那些现代化的密码套件和特性,那么无论你如何去修改配置都无法改善现在的安全问题。

所以我们在配置前,或者发现按照推荐配置进行了调整《SSL/TLS安全评估报告》还是无法满足要求,那么可以检查下所使用的OpenSSL等加密库是否版本过低。

如何检查OpenSSL版本

nginx

nginx -V

nginx version: nginx/1.10.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)
built with OpenSSL 1.0.1e-fips 11 Feb 2013
TLS SNI support enabled 

或者通过openssl命令查看(适用于非自己通过openssl源码编译的)

openssl version

推荐的OpenSSL版本

  1. OpenSSL 1.0.2用户需更新到1.0.2h 以上。
  2. OpenSSL 1.0.1用户需更新到1.0.1t 以上。
  3. OpenSSL官方已停止对 0.9.8和 1.0.0 两个版本的升级维护,请使用这两个版本的用户将其升级至1.0.2h版本以上。

OpenSSL 1.0.1以下不支持tls1.2

升级前请做好测试

漏洞事件

https://www.trustasia.com/openssl-heartbleed

OpenSSL 1.0.1g 已修复该漏洞

OpenSSL 1.0.0 分支版本不受此漏洞影响

OpenSSL 0.9.8 分支版本不受此漏洞影响

OpenSSL 1.0.2 Beta2 不受此漏洞影响

Heartbleed检测工具>>

https://www.trustasia.com/OpenSSL-DROWN-attack

OpenSSL 1.0.1h+

OpenSSL 1.0.0m+

OpenSSL 0.9.8za+

https://www.trustasia.com/OpenSSL-CVE-2016-2107-Padding-Oracle

1、OpenSSL 1.0.2用户需更新到1.0.2h 。

2、OpenSSL 1.0.1用户需更新到1.0.1t 。

3、使用包管理系统的用户可以直接更新到2016年5月3日 之后的版本。

CBC padding oracle检测 检测工具>>

OpenSSL CCS漏洞

此漏洞是 OpenSSL ChangeCipherSpec 设计缺陷造成,被称为 CCS 注入漏洞。

https://www.trustasia.com/openssl-ccs

OpenSSL 1.1.0 应升级到 1.1.0a 或更高版本。

OpenSSL 1.0.2 应升级到 1.0.2i 或更高版本。

OpenSSL 1.0.1 应升级到 1.0.1u 或更高版本。

OpenSSL CCS 检测工具>>

注意:

OpenSSL官方已停止对 0.9.8和 1.0.0 两个版本的升级维护,请使用这两个版本的用户将其升级至更高版本。

HTTPS 安全最佳实践(二)之安全加固

当你的网站上了 HTTPS 以后,可否觉得网站已经安全了?这里 提供了一个 HTTPS 是否安全的检测工具,你可以试试。

本篇正文讲述的是 HTTP 安全的最佳实践,着重在于 HTTPS 网站的 Header 的相关配置。

1 连接安全性和加密

1.1 SSL/TLS

传输层安全(TLS)及其前身安全套接字层(SSL),通过在浏览器和 web 服务器之间提供端到端加密来促进机密通信。没有 TLS,就谈不上什么安全。TLS 是 HTTP 安全性的基础。

想要部署 TLS 是非常容易的,但其难点在于如何使用安全的配置来保障站点的安全。

尤其是 Protocol 版本和 Cipher 需要小心选择和配置。你可以通过本站 工具 体检你的网站,发现并解决这些细节的问题。

建议

所有本地和链接的资源需要正确的配置,且要使用 TLS。

1.2 HTTP Strict Transport Security (HSTS)

指示浏览器只使用 HTTPS 连接到目标服务器。这可以防止一些潜在的中间人攻击,包括 SSL 剥离,会话 cookie 窃取(如果没有被 适当保护)。如果遇到任何与证书相关的错误,它还可以阻止浏览器连接到网站。当浏览器访问一个设置相应 HTTP header 的 HTTPS 网站时,HSTS 将被激活。

HSTS 有一个固定期限,由 max-age 字段值控制。这个值可以是静态的,也可以是相对于将来某个特定日期的,你可以设置成 SSL 证书的过期时间。

在浏览器中,HSTS 首选项可以通过提交到 Chromium’s HSTS preload list 来硬编码,这是所有实现 HSTS 使用的浏览器。

注意,HSTS 确实有陷阱。它提供了 include subdomains 选项,这在实践中可能是太宽泛了。此外,客户端错误可能会造成严重的后果——客户端错误的时钟导致它认为服务器的 SSL 证书无效或过期,或者缺少根 CA 证书——将不再导致浏览器中的证书错误。浏览器将完全拒绝访问页面,并且可能会显示让安全专家之外的完全无法理解的错误。

建议

设置 HSTS header 长的生命周期,最好是半年及以上。

Strict-Transport-Security: max-age=31536000 

1.3 Public Key Pins

HTTP PKP(HPKP)指示浏览器只与提供的 SSL/TLS 的 HASH 相符或存在于同一证书链的服务器相连接。换句话说,如果 SSL/TLS 证书以一种意想不到的方式发生了变化,浏览器就无法连接到主机。这主要是针对受信任证书颁发机构(CA)或流氓 CA 证书颁发的伪造证书,用户可能会被骗安装。

例如,浏览器连接到 https://example.com,它存在这个头。header 告诉浏览器,如果证书 key 匹配,或者在发出证书链中有一个 key 匹配,那么在将来才会再次连接。其他的指令组合是可能的。它们都极大地减少了攻击者在客户端和合法主机之间模拟主机或拦截通信的可能性。

像 HSTS 一样,HPKP 在实现之前需要仔细的思考和计划。错误可以将用户锁定在您的站点之外,并且不容易修复。

像 HSTS 一样,HPKP 在实现之前需要仔细的思考和计划。错误可以将用户锁定在您的站点之外,并且不容易修复。

建议

确定是否需要为您的站点使用 PKP。如果是这样的话,那么从一个较小的实践开始,如果在一段时间之后没有遇到问题,就增加它。如果 SSL/TLS 密钥需要更新,建立备份计划。优先创建备份密钥和离线存储。

示例HTTP头:

Public-Key-Pins: max-age=5184000; pin-sha256="+oZq/vo3Kcv0CQPjpdwyInqVXmLiobmUJ3FaDpD/U6c="; pin-sha256="47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=" 

1.4 Mixed HTTPS and HTTP Content

主站点通过 HTTPS 安全地服务,但是在 HTTP 上加载一些文件(images、js、css)。这是一个巨大的安全漏洞,破坏了 HTTPS 提供的安全性。受影响的站点可能会泄漏会话 cookie 或用户行为信息。它们也可能容易受到注入和其他 MITM 攻击的攻击,而 HTTPS 通常会阻止这种攻击。

建议

如果 HTTPS 部署在主站上,请将任何地方的所有内容都 HTTPS 化(全站 HTTPS)。

2 Content security

2.1 Content Security Policy

为浏览器提供关于网站内容类型和行为的明确说明。良好的内容安全策略(CSP)可以帮助抵御跨站点脚本(XSS)和其他注入攻击等攻击。CSP 支持所有主要的浏览器,尽管只是部分地之前在 IE 11。

一个好的 CSP 是基于白名单的方法,不允许任何东西,除了明确允许的内容。它还限制了 javascript 的来源和允许操作。

CSP 很难启用遗留代码库。为了简化实现,CSP 提供了一个 report-only 模式,在浏览器中,CSP 的违规被发送到一个网站端点,但是该策略没有被强制执行。

新项目应该从一开始就使用 CSP。

建议

从限制性政策开始,在必要时放松。禁止所有的例子:

Content-Security-Policy: default-src 'none'; 

现在让我们允许自托管 scripts、images、CSS、fonts 和 AJAX,以及 jQuery CDN 托管脚本和 Google Analytics:

Content-Security-Policy: default-src 'none'; script-src 'self' https://code.jquery.com https://www.google-analytics.com; img-src 'self' https://www.google-analytics.com; connect-src 'self'; font-src 'self'; style-src 'self'; 

要注意的是,不要让所有的东西都破坏你的网站,例如,如果你使用 child-src 指令,而浏览器不支持它。一个不那么严格的政策可能从以下开始:

Content-Security-Policy: default-src 'self'; 

甚至更少的限制性政策甚至可以使用 default-src '*',然后添加限制。我建议你不要这么做,除非你完全明白其中的含义。否则,你可能会依赖 CSP,它只会给你一种错误的安全感。

2.2 Frame Options

控制站点是否可以放置在 <iframe><frame> 或 <object> 标签。不允许使用框架可以防止 clickjacking 攻击。例如,从 2015 年 2 月起,Internet Explorer’s universal cross-site-scripting bug 就被这个消息头减轻了。

X-Frame-Options 是一个非标准的 header,在内容安全策略级别 2 中被 frame ancestor指令所取代。然而,frame ancestor 还没有得到普遍的支持,而 X-Frame-Options 得到了广泛的支持。

建议

确定你的网站是否需要被允许呈现在一个 frame 中。完全不允许使用 sameorigin 拒绝或允许同源框架的选项。避免由于受限或 bug 浏览器支持而允许的选项。示例 HTTP 头:

X-Frame-Options: deny 

2.3 XSS Protection

跨站点脚本(XSS 或 CSS)的保护被构建到大多数流行的浏览器中,除了 Firefox 之外。这种保护是用户可配置的,可以关闭。因此,明确要求浏览器在你的网站上使用它的 XSS 过滤器是个好主意。

相反,网站可以要求 XSS 保护在页面的基础上被禁用。这绝对不是一个好主意。

建议

使用入校 HTTP header:

X-Xss-Protection: 1; block 

2.4 Cache Control

表示缓存页面输出的首选项。适当的值随网站数据的性质而变化,但强烈推荐使用偏好。否则,它取决于浏览器和代理来选择是否缓存内容。不恰当的选择可能会导致性能问题、安全问题,或者两者都有。

建议

开发缓存策略,然后将缓存首选项包括为 HTTP 头。

Cache-Control: public* 

其中的一个 public,private,no-cache 或 no-store。如果允许缓存,则应该将 max-age 值包含在 Cache-Control 以及 Etag 头文件中,以允许客户端缓存验证。

2.5 Content Type Options

当浏览器以不同的方式处理来自服务器的文件时,MIME 嗅探就是服务器指令。当一个网站承载不受信任的内容(如用户提供的)时,这是很危险的。假设服务器允许用户上传 image。如果用户上传 HTML 文档,浏览器可能会将其呈现为 web 执行 scriptpage,即使服务器明确表示正在发送 image。非标准的标头 X-Content-Type-Options选项指示浏览器不做任何模仿指定类型的 MIME。

建议

总是设置 header:

X-Content-Type-Options: nosniff 

2.6 Subresource Integrity

浏览器通常从外部域加载大量资源、javascript 和样式表。内容交付网络经常被使用。如果外部资源被破坏,依赖站点的安全性也可以。子资源完整性允许浏览器验证 javascript 或样式表未被意外修改。

建议

设置外部 javascript 和样式表的完整性属性。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js" integrity="sha384-6ePHh72Rl3hKio4HiJ841psfsRJveeS+aLoaEf3BWfS+gTF0XdAqku2ka8VddikM"></script> 

注意

您应该始终提供外部脚本的本地副本,并实现一种方法,以便在外部负载失败的情况下重新加载它们。否则你的网站可能会崩溃。例子:

<script>window.jQuery || document.write('<script src="/jquery.min.js"><\/script>')</script> 

2.7 Iframe Sandbox

iframe 在 WWW 上随处可见。网站平均有 5.1 iframe,主要用于装载第三方内容。这些 iframe 有很多方法来伤害托管网站,包括运行脚本和插件和重新引导访问者。sandbox属性允许对 iframe 中可以进行的操作进行限制。

建议

设置 iframe 的 sandbox 属性,然后添加所需的权限。

<iframe src="https://example.com" sandbox="allow-same-origin allow-scripts"></script> 

2.8 Server Clock

服务器包括所有响应的时间戳。不准确的时钟不会给客户机浏览器带来问题。然而,当与其他系统或服务交互时,问题就会出现。

建议

使用网络时间协议(NTP)来保持服务器时钟的准确性。

3 Information disclosure

3.1 Server Banner

大多数 web 服务器设置报头来识别自己和他们的版本号。这只服务于信息目的和实际用途是非常有限的。去掉整个头,而完全可以接受,通常是不必要的。但是,建议从头中删除版本号。在特定 web 服务器版本中存在 bug 的情况下,包括版本号可以作为对脚本 kiddy 的邀请来尝试对服务器的攻击。

建议

包含服务器名称但去掉版本号;

Server: nginx 

3.2 Web Framework Information

许多 web 框架设置 HTTP 头,识别框架或版本号。除了满足用户的好奇心,而且主要作为技术堆栈的广告,这几乎没有什么作用。这些头是不标准的,对浏览器渲染站点的方式没有影响。

虽然它们没有什么实际用途,但对于搜索运行过时版本的软件的机器人或蜘蛛来说,这些标头是无价的,因为这些软件可能包含安全漏洞。如果没有定期更新,这些头文件可以使网站的目标变得容易。

建议

从服务器响应中删除这些标头: X-Powered-ByX-RuntimeX-Version 和 X-AspNet-Version

4 Cookies

4.1 Cookie Security

包含敏感信息的 cookie,特别是会话 id,需要标记为安全的,假设网站是通过 HTTPS 传输的。这会阻止 cookie 通过 HTTP 发送明文文本。另一种方法是通过 HSTS 来阻止非安全 cookie 在 HTTP 上传输。建议使用安全 cookie 和 HSTS。

会话 cookie 应该与 HttpOnly 值进行标记,以防止它们被 javascript 访问。这可以防止攻击者利用 XSS 窃取会话 cookie。其他 cookie 可能不需要这样标记。但是,除非有明确的需要从 javascript 中访问他们的值,否则最好还是呆在安全的一边,把所有cookie标记为 HttpOnly

建议

标记所有 cookie 安全和 HttpOnly

Set-Cookie: Key=Value; path=/; secure; HttpOnly, Key2=Value2; secure; HttpOnly

https://blog.myssl.com/https-security-best-practices/

HTTPS 安全最佳实践(一)之SSL/TLS部署

SSL/TLS 是一种简单易懂的技术,它很容易部署及运行。但想要部署的安全通常是不容易的。这也使系统管理员和开发者不得不去了解 SSL 和 TLS 相关的技术,掌握如何配置一个安全的 web 服务器或应用。无疑会耗费很大的精力去看相关的技术文档,乏味且宽泛。

本篇文档的目的在于如何让系统管理员或开发者用尽可能少的时间部署一个安全的 web 站点或应用,即 SSL 和 TLS 部署最佳实践。

1 证书和私钥

在TLS中,所有的安全性都从服务器的密码标识开始;需要一个强大的私钥来防止攻击者进行模拟攻击。同样重要的是要有一个有效的和强大的证书,这将授予私有密匙作为一个特定主机名的权利。没有这两个基本的构建块,就没有其他东西可以安全了。

1.1 使用 2048 位私钥

对于大多数的 web 站点,提供一个 2048 的 RSA key 是足够安全的。RSA 的公钥算法是被普遍支持的,这使得这个类型的 key 作为默认是足够安全的。对于 2048 位,这些 key 提供了大约 112 位的安全性。如果您想要比这更多的安全性,请注意 RSA key 的伸缩性不太好。想要获得 128 位的安全性,你需要 3072 位 RSA key,这会很大的影响性能。ECDSA key 提供了一种提供更好安全性和更好性能的替代方法。对于 256 位,ECDSA key 提供 128 位安全性。少数古董客户端不支持 ECDSA,但现代客户端是支持的。如果您不介意管理这样一个设置的开销,那么您可以同时部署 RSA 和 ECDSA 密钥。

1.2 保护你的私钥

把你的私钥视为一项重要的资产,尽可能最大的使用你的私钥,限制最小的员工的访问。建议的政策包括以下内容:

  • 在可信计算机上用足够的熵生成私有密钥。一些 CA 为您提供生成私钥的功能,请尽量不要这样做。
  • 密码保护 key 最初就不要存储在备份系统中。私钥密码在生产环境中起不了什么作用,因为有知识的攻击者总是能够从进程内存中检索密钥。有硬件设备(被称为硬件安全模块,或 HSMs),即使在服务器折衷的情况下,也可以保护私有密匙,但是它们是昂贵的,因此仅适用于具有严格安全性需求的组织。
  • 妥协后,撤销旧证书并生成新密钥。
  • 每年更新证书,如果您可以自动化过程,则更频繁。大多数网站都应该假定不可靠的妥协证书被撤销; 因此,具有较短使用寿命的证书在实践中更加安全。
  • 除非保持相同的密钥对于公钥密钥很重要,否则每当获得新证书时,还应该生成新的私钥。

1.3 覆盖您的域名

确保您的证书涵盖您希望与网站一起使用的所有名称。您的目标是避免无效的证书警告,这会混淆用户,削弱他们的信心。

即使您期望只使用一个域名,请记住,您无法控制用户到达该网站的方式或其他人如何链接到该网站。在大多数情况下,您应该确保该证书与 www 前缀有关(例如,它适用于 example.com 和

www.example.com )。经验法则是,安全的 Web 服务器应该具有对配置为指向它的每个 DNS 名称有效的证书。

通配符证书能满足更广泛的需求,但如果准备将密钥暴露给更多的人员,特别是跨团队或部门,则避免使用它们。换句话说,访问私钥的人越少越好。还要注意,证书共享会创建一个可以被滥用的将漏洞从一个网站或服务器传输到使用相同证书的所有其他站点和服务器(即使底层私钥不同,只要证书域名匹配)的绑定。

1.5 从可信 CA 获取证书

选择对其证书业务和安全性可靠和认真的认证中心(CA)。选择 CA 时,请考虑以下条件:

安全状态 所有CA都经过定期审核,但有些则比其他 CA 更为严重。弄清哪些在这方面做的更好并不容易,但一个选择是检查他们的安全历史,更重要的是,他们如何反应妥协,如果他们从错误中学到了经验,这将更有利。

业务重点 CA 的活动构成其业务的重要组成部分,如果事情发生严重错误,其所有事情都将丢失,并且在其他地方追逐潜在的更有利可图的机会可能不会忽视其证书部门。

提供的服务 至少,您选择的 CA 应提供对证书吊销列表(CRL)和在线证书状态协议(OCSP)撤销方法的支持,具有稳定的网络可用性和性能。许多网站对域验证的证书感到满意,但您也应该考虑是否需要扩展验证(EV)证书。在任一种情况下,您都应该选择公钥算法。大多数网站今天使用 RSA,但由于其性能优势,ECDSA 在未来可能会变得重要。

证书管理 选项如果您需要大量证书并在复杂环境中运行,请选择一个 CA,为您提供良好的管理工具。

支持选择一个 CA,如果需要的话可以给您很好的支持。

注意

为了获得最佳效果,请提前获得证书,并在部署到生产之前至少一周。这种做法(1)有助于避免在计算机上没有正确时间的一些用户的证书警告;(2)有助于避免与 CA 需要额外时间的 CA 失败的撤销检查,以向 OCSP 响应者传播有效的新证书。随着时间的推移,尝试将这个“热身”期延长至 1-3 个月。同样,不要等到你的证书即将到期以替换它们。留下额外的几个月也会帮助时钟不正确的人在另一个方向。

1.6 使用强签名算法

证书安全性取决于(1)用于签署证书的私钥的强度,(2)签名中使用的散列函数的强度。直到最近,大多数证书都依赖于 SHA1 散列函数,现在被认为是不安全的。因此,我们正在向 SHA256 转型。截至 2016

年 1 月,您无法从公共 CA 获取 SHA1 证书。现有的 SHA1 证书将继续工作(在某些浏览器中有警告),但只能到 2016 年底。

2 配置

使用正确的 TLS 服务器配置,您可以确保将凭据正确呈现给站点的访问者,仅使用安全的加密原语,并减轻所有已知的缺陷。

2.1 使用完整的证书链

在大多数部署中,仅服务器证书不够的; 需要两个或多个证书来建立完整的信任链。当部署具有有效证书但没有所有必要的中间证书的服务器时,会发生常见的配置问题。为避免这种情况,只需使用 CA 提供给您的所有证书。

无效的证书链有效地使服务器证书无效并导致浏览器警告。实际上,这个问题有时难以诊断,因为一些浏览器可以重构不完整的链,有些浏览器不能重建。所有浏览器都倾向于缓存和重用中间证书。

2.2 使用安全的协议

SSL/TLS 系列中有五种协议:SSL v2,SSL v3,TLS v1.0,TLS v1.1和TLS v1.2:

  • SSL v2 是不安全的,不能使用。此协议版本非常糟糕,即使它们位于完全不同的服务器(DROWN 攻击)上也可以用来攻击具有相同名称的RSA 密钥和站点。
  • 当与 HTTP(POODLE 攻击)一起使用时,SSL v3 是不安全的,当与其他协议一起使用时,SSL v3 是弱的。它也是过时的,不应该被使用。
  • TLS v1.0 也是不应该使用的传统协议,但在实践中通常仍然是必需的。其主要弱点(BEAST)在现代浏览器中得到缓解,但其他问题仍然存在。
  • TLS v1.1 和 v1.2 都没有已知的安全问题,只有 v1.2 提供了现代的加密算法。

TLS v1.2 应该是您的主要协议,因为它是唯一提供现代认证加密(也称为 AEAD)的版本。如果您今天不支持 TLS v1.2,则缺乏安全性。

为了支持较旧的客户端,您可能需要继续支持 TLS v1.0 和TLS v1.1。但是,您应该计划在不久的将来退出 TLS v1.0。例如,PCI DSS 标准将要求所有接受信用卡付款的网站在 2018 年 6 月之前移除对 TLS v1.0 的支持。

目前正在开展设计 TLS v1.3 的工作,其目的是消除所有过时和不安全的功能,并进行改进,以保持我们的通信在未来几十年内的安全。

2.3 使用安全的套件

为了安全通信,您必须首先确定您正在与所需方(而不是通过将窃听的其他人)直接沟通并安全地交换数据。在 SSL 和 TLS 中,密码套件定义了如何进行安全通信。它们由不同的建筑组成,通过多样性实现安全。如果发现其中一个构建块软弱或不安全,那么您应该可以切换到另一个。

您应该主要依靠提供强身份验证和密钥交换,前向保密和至少 128 位加密的 AEAD 套件。还有一些其他较弱的套房可能仍然得到支持,只要它们只能与不支持任何更好的老客户进行协商。

有几个过时的加密原语必须避免:

  • 匿名 Diffie-Hellman(ADH)套件不提供身份验证。
  • NULL 密码套件不提供加密。
  • 导出密码套件在连接中协商时不安全,但也可以针对更喜欢更强大的套件(FREAK攻击)的服务器使用。
  • 弱密码(通常为 40 和 56 位)的套件使用可以轻松破坏的加密。
  • RC4 是不安全的。
  • 3DES 缓慢而虚弱。

使用以RSA和ECDSA键为基础的以下套件配置,作为起点:

TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 

警告

我们建议您始终首先在分段环境中测试TLS配置,仅在确定所有内容按预期工作时将更改应用到生产环境。请注意,以上是一个通用列表,并不是所有系统(特别是较旧的)支持所有套件。这就是为什么测试很重要,推荐您使用《SSL/TLS安全评估》进行检查。

上述示例配置使用标准 TLS 套件名称。一些平台使用非标准名称; 有关详细信息,请参阅您的平台的文档。例如,以下套件名称将与OpenSSL 一起使用:

ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-ECDSA-AES128-SHA
ECDHE-ECDSA-AES256-SHA
ECDHE-ECDSA-AES128-SHA256
ECDHE-ECDSA-AES256-SHA384
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-RSA-AES128-SHA
ECDHE-RSA-AES256-SHA
ECDHE-RSA-AES128-SHA256
ECDHE-RSA-AES256-SHA384
DHE-RSA-AES128-GCM-SHA256
DHE-RSA-AES256-GCM-SHA384
DHE-RSA-AES128-SHA
DHE-RSA-AES256-SHA
DHE-RSA-AES128-SHA256
DHE-RSA-AES256-SHA256 

2.4 选择合适的协议

在SSL v3及更高版本的协议版本中,客户端提交他们支持的密码套件列表,服务器从列表中选择一个用于连接的套件。然而,并不是所有的服务器都做得很好,有些将从客户端列表中选择第一个支持的套件。使服务器主动选择最佳可用加密套件对于实现最佳安全性至关重要。

2.5 使用 FS

前向保密(有时也称为完全前向保密)是一种协议功能,可实现不依赖服务器私钥的安全对话。对于不提前向保密的密码套件,可以恢复服务器的私钥的人就可以解密所有较早记录的加密对话(也就是可以先大量记录密文,再解密,比如您的证书到期后没有正确销毁,它的私钥就能用来解密非PFS的密文)。您需要支持并喜欢 ECDHE 套件,以便通过现代网络浏览器实现前向保密。为了支持更广泛的客户,您还应该使用 DHE 套件作为 ECDHE 后备。避免 RSA 密钥交换,除非绝对必要。我在2.3节中提出的默认配置只包含提供前向保密的套件。

2.6 使用强的密钥交换算法

对于密钥交换,公共站点通常可以选择经典的短暂的 Diffie-Hellman密钥交换(DHE)和其椭圆曲线变体 ECDHE。还有其他的密钥交换算法,但是它们通常是以某种方式不安全的。RSA 密钥交换仍然很受欢迎,但不提供前向保密。

2015 年,一批研究人员发表了对 DHE 的新攻击; 他们的工作被称为Logjam 攻击。[2] 研究人员发现,较低强度的 DH 密钥交换(例如768 位)容易被破坏,一些知名的 1024 位 DH 组可被国家机构破坏。为了安全起见,如果部署 DHE,请至少配置 2048 位的安全性。一些较老的客户端(例如Java 6)可能不支持这种强度。出于性能原因,大多数服务器应该更喜欢 ECDHE,这是更强大和更快。在这种情况下,secp256r1命名曲线(也称为 P-256)是一个很好的选择。

3 减轻已知问题

近几年来已经发生了几次严重的 SSL 和 TLS 攻击,但是如果您正在运行最新的软件并遵循本指南的建议,那么它们通常不会关心您。(如果没有,我建议您使用 MYSSL 测试您的系统,并从中进行测试)。但是,没有什么是完全安全的,所以为了保持对安全性的了解,这是一个很好的做法。如果供应商补丁可用,请及时提供; 否则,依靠解决方案进行缓解。

4 性能

安全是我们在本指南中的主要重点,但我们也要注意表现; 一个不符合性能标准的安全服务无疑将被丢弃。通过正确配置,TLS 可以相当快。使用现代协议(例如 HTTP/2),甚至可能比明文通信更快。

4.1 避免过度安全

用于建立安全连接的密码握手是一种操作,其费用受私钥大小的高度影响。使用太短的密钥是不安全的,但使用太长的密钥将导致“太多”的安全性和缓慢的操作。对于大多数网站,使用超过 2048 位的 RSA 密钥和强大于 256 位的 ECDSA 密钥会浪费 CPU 功耗,并可能会损害用户体验。类似地,增加短暂密钥交换的强度对于 DHE 为 2048 位以及 ECDHE 为 256 位几乎没有什么好处。使用高于 128 位的加密没有明显的好处。

4.2 使用 session 恢复

会话恢复是一种性能优化技术,可以节省昂贵的密码操作的结果,并重复使用一段时间。残疾或非功能性会话恢复机制可能会引起显着的性能损失。

4.3 使用 WAN 优化和 HTTP/2

这些天,TLS 开销不是来自 CPU 饥饿的加密操作,而是来自网络延迟。只有在 TCP 握手完成后才能启动TLS握手,需要进一步交换数据包,并且离开服务器的距离更远。最小化延迟的最佳方法是避免创建新的连接 – 换句话说,保持现有的连接长时间(keep-alives)。提供良好结果的其他技术包括支持现代协议(如HTTP / 2)和使用WAN优化(通常通过内容传送网络)。

4.4 隐藏公共内容

通过TLS进行通信时,浏览器可能会认为所有流量都是敏感的。它们通常会使用内存来缓存某些资源,但一旦关闭浏览器,所有内容可能会丢失。为了获得性能提升,并能够长期缓存一些资源,将公共资源(例如图像)标记为公开。

4.5 使用 OCSP Stapling

OCSP 装订是 OCSP 协议的扩展,可以直接从服务器提供撤销信息作为 TLS 握手的一部分。因此,客户端不需要联系 OCSP 服务器进行带外验证,并且总体 TLS 连接时间显着减少。OCSP 装订是一种重要的优化技术,但您应该注意,并不是所有的网络服务器都提供了可靠的 OCSP 装订实现。结合具有缓慢或不可靠的 OCSP 响应者的 CA,这样的 Web 服务器可能会产生性能问题。为了获得最佳效果,请模拟故障条件,看看它们是否会影响您的可用性。

4.6 使用快速加密

除了提供最佳的安全性,我推荐的密码套件配置也提供了最好的性能。尽可能使用支持硬件加速 AES 的 CPU。之后,如果您真的想要进一步的性能优势(大多数网站可能不需要),请考虑使用 ECDSA 密钥。

5 HTTP 和 应用安全

HTTP 协议和 Web 应用交付的周边平台在 SSL 诞生后继续快速发展。作为这一进化的结果,该平台现在包含可用于打败加密的功能。在本节中,我们列出了这些功能,以及安全使用它们的方法。

5.1 加密无处不在

加密是可选的事实可能是今天最大的安全问题之一。我们看到以下问题:

  • 没有 TLS 需要它的网站
  • 具有 TLS 但不执行 TLS 的站点
  • 混合 TLS 和非 TLS 内容的网站,有时甚至在同一网页内
  • 编程错误的网站会颠覆 TLS

尽管如果您确切了解您正在做的事情,许多这些问题可以被缓解,可靠地保护网站通信的唯一方法是无一例外地执行加密。

5.2 消除混合内容

混合内容页面是通过 TLS 传输但是包含不通过 TLS 传输的资源(例如,JavaScript 文件,images,CSS 文件)的页面。这样的页面不安全。一个活跃的中间人(MITM)攻击者可以搭载一个单独的未受保护的 JavaScript 资源,例如劫持整个用户会话。即使您遵循上一节的建议并对整个网站加密,您仍然可能会最终从第三方网站中检索未加密的一些资源。

5.3 使用可信第三方

网站通常使用通过从另一个服务器下载的 JavaScript 代码激活的第三方服务。这种服务的一个很好的例子是 Google Analytics(分析),用于 Web 的大部分。这种包含第三方代码创建一个隐含的信任连接,有效地使对方完全控制您的网站。第三方可能不是恶意的,但是这些服务的大型提供商越来越被视为目标。推理很简单:如果大型提供程序受到威胁,攻击者将被自动访问所有依赖该服务的站点。

如果您遵循第4.2节的建议,至少您的第三方链接将被加密,从而避免 MITM 攻击。但是,您应该进一步了解:了解您使用的服务和删除服务,将其替换为更安全的替代方案,或接受其继续使用的风险。一种称为子资源完整性(SRI)的新技术可用于通过第三方资源来减少潜在的风险。[3]

5.4 安全 cookie

要正确安全,网站需要 TLS,而且所有的 Cookie 在创建时都被明确标记为安全的。未能保护 cookies 可以让活跃的 MITM 攻击者通过聪明的技巧来挑逗一些信息,即使在 100% 加密的网站上也是如此。为了获得最佳效果,请考虑为您的 Cookie 添加加密完整性验证或甚至加密。

5.5 安全 HTTP 压缩

2012 年 CRIME 攻击显示 TLS 压缩无法安全实施。唯一的解决方案是完全禁用 TLS 压缩。次年,随后再发生两次攻击。TIME 和 BREACH 专注于使用 HTTP 压缩压缩的 HTTP 响应实体中的秘密。与 TLS 压缩不同,HTTP 压缩是必需的,不能关闭。因此,为了解决这些攻击,需要对应用程序代码进行更改。[4]

TIME 和 BREACH 攻击并不容易实现,但是如果某人有足够的动力使用它们,则这种影响大致相当于成功的跨站点请求伪造(CSRF)攻击。

5.5 部署 HSTS

HTTP 严格传输安全(HSTS)是 TLS 的安全网。它旨在确保即使在配置问题和实施错误的情况下,安全性仍然保持不变。要激活 HSTS 保护,您可以向您的网站添加一个新的响应头。之后,支持 HSTS(此时所有现代浏览器)的浏览器执行它。

HSTS 的目标很简单:激活后,它不允许与使用它的网站进行任何不安全的通信。通过自动将所有明文链接转换为安全的链接,实现了这一目标。作为奖励,它还会禁用点击式证书警告。(证书警告是活动的 MITM 攻击的指标,研究表明大多数用户点击这些警告,所以绝对不要让他们感兴趣)。

添加对 HSTS 的支持是您可以为您的网站的 TLS 安全性做出的最重要的改进。新站点始终应设计为 HSTS,旧站点转换为尽可能快地支持。为了获得最佳安全性,请考虑使用 HSTS 预加载[5],将HSTS配置嵌入到现代浏览器中,从而使您的网站的第一个连接安全。

以下配置示例将在主主机名及其所有子域上激活一段时间为一年的 HSTS,同时还允许预加载:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 

5.6 部署 CSP

内容安全策略(CSP)是网站可以用来限制浏览器操作的安全机制。尽管最初旨在解决跨站点脚本(XSS),CSP 不断发展,并支持对增强TLS安全性有用的功能。特别地,它可以用于限制混合内容,当涉及到第三方网站,HSTS没有帮助。

要部署CSP以防止第三方混合内容,请使用以下配置:

Content-Security-Policy: default-src https: 'unsafe-inline' 'unsafe-eval';
                         connect-src https: wss: 

注意

这不是部署 CSP 的最佳方法。为了提供不破坏混合内容以外的任何内容的示例,我不得不禁用某些默认安全功能。随着时间的推移,当您了解 CSP 的更多信息时,您应该更改您的策略以使其恢复。

5.7 不要缓存敏感内容

所有敏感内容必须仅传达给预定方,并由所有设备进行相应处理。虽然代理没有看到加密的流量,并且不能在用户之间共享内容,但是使用基于云的应用交付平台正在增加,这就是为什么在指定什么是公共的时候需要非常小心的是什么。

5.8 考虑其它威胁

TLS 旨在仅解决安全机密和您与用户之间通信的完整性的一个方面,但还有许多其他威胁需要处理。在大多数情况下,这意味着确保您的网站没有其他弱点。

6 验证

有许多配置参数可用于调整,预先知道某些变化会产生什么影响。此外,有时会意外地进行更改; 软件升级可以静默地引入更改。因此,我们建议您最初使用全面的 SSL/TLS 评估工具来验证您的配置,以确保您开始安全,然后定期确保您保持安全。对于公共网站,我们建议您免费使用SSL实验室服务器测试。[6]

6.1 高级主题

以下高级主题目前不在我们的指南范围之内。他们需要更深入地了解 SSL/TLS 和公钥基础设施(PKI),而且他们仍然被专家辩论。

6.2 使用 HPKP

公共密钥固定旨在使网站运营商有权限制哪些 CA 可以为其网站颁发证书。Google 已经部署了这个功能了一段时间(硬编码到他们的浏览器,Chrome),并且已被证明是非常有用的,以防止攻击并使公众了解它们。在 2014 年,Firefox 还增加了对硬编码固定的支持。现在可以使用一种称为 HTTP [7]的公钥固定扩展标准。公钥绑定解决了 PKI 最大的弱点(事实上,任何 CA 都可以为任何网站发布证书),但是这是一个成本; 部署需要大量精力和专业知识,并造成失去对您站点控制的风险(如果最终导致无效的固定配置)。你应该考虑固定很大程度上只有当你

6.2 使用 DNSSEC 和 DANE

域名系统安全扩展(DNSSEC)是一种增加域名系统完整性的技术。今天,一个活跃的网络攻击者可以轻松地劫持任何 DNS 请求并伪造任意的响应。使用 DNSSEC,所有响应都可以加密地跟踪到 DNS 根目录。命名实体的基于 DNS 的身份验证(DANE)是建立在 DNSSEC 之上的单独标准,用于提供 DNS 和 TLS 之间的绑定。DANE 可用于增强现有基于 CA 的 PKI 生态系统的安全性,或者完全绕过它。

即使不是每个人都同意,DNSSEC 是互联网的一个很好的方向,但对其的支持仍在继续改善。浏览器还不支持 DNSSEC 或 DANE(更喜欢 HSTS 和 HPKP 提供的类似功能),但有一些迹象表明它们正在开始用于提高电子邮件传递的安全性。

Nginx配置支持HTTP2

从 2015 年 5 月 14 日 HTTP/2 协议正式版的发布到现在已经快有一年了,越来越多的网站部署了 HTTP2,HTTP2 的广泛应用带来了更好的浏览体验,只要是 Modern 浏览器都支持,所以部署 HTTP2 并不会带来太多困扰。

虽然 h2 有 h2c (HTTP/2 Cleartext) 可以通过非加密通道传输,但是支持的浏览器初期还是比较少的,所以目前部署 h2 还是需要走加密的,不过由于 Let’s Encrypt 大力推行免费证书和证书的廉价化,部署 h2 的成本并不高。

介绍

HTTP 2.0即超文本传输协议 2.0,是下一代HTTP协议。是由互联网工程任务组(IETF)的Hypertext Transfer Protocol Bis (httpbis)工作小组进行开发。是自1999年http1.1发布后的首个更新。

HTTP/2 协议是从 SPDY 演变而来,SPDY 已经完成了使命并很快就会退出历史舞台(例如 Chrome 将在「2016 年初结束对 SPDY 的支持」;Nginx、Apache 也已经全面支持 HTTP/2 ,并也不再支持 SPDY)。

一般的大家把 HTTP2 简称为 h2,尽管有些朋友可能不怎么愿意,但是这个简称已经默认化了,特别是体现在浏览器对 HTTP2 都是这个简写的。

配置

普通的 HTTPS 网站浏览会比 HTTP 网站稍微慢一些,因为需要处理加密任务,而配置了 h2 的 HTTPS,在低延时的情况下速度会比 HTTP 更快更稳定!

现在电信劫持事件频发,网站部署了 HTTPS 加密后可以杜绝大部分劫持,但不是完全。像电子商务行业对 HTTPS 加密可是标配啊,因此部署 h2 更是势在必行。

证书

这里是 免费和便宜 SSL 证书介绍 ,大家可以从这里购买或者申请免费的 SSL 证书,免得 Chrome 弹出红色的页面令人不悦,从而拒绝了大多数访客。

Web 服务器

说明

默认编译的 Nginx 并不包含 h2 模块,我们需要加入参数来编译,截止发文,Nginx 1.9 开发版及以上版本源码需要自己加入编译参数,从软件源仓库下载的则默认编译。 Tengine 可以同时部署 h2 和 SPDY 保证兼容性,Nginx 则是一刀切不再支持 SPDY。

安装/编译

如果你编译的 Nginx 不支持,那么在 ./configure 中加入:--with-http_v2_module ,如果没有 SSL 支持,还需要加入 --with-http_ssl_module

然后 make && make install 即可。

配置

主要是配置 Nginx 的 server 块, 。
修改相关虚拟机的 .conf 文件,一般在 /usr/local/nginx/conf/vhost/ 或者 /etc/nginx/conf/,具体参考你的环境指导,不懂请回复。

server { listen 443 ssl http2 default_server; server_name www.mf8.biz; ssl_certificate /path/to/public.crt; ssl_certificate_key /path/to/private.key; 

注:将 server_name www.mf8.biz; 中的 www.mf8.biz 替换为你的域名。

然后通过 /usr/local/nginx/sbin/nginx -t 或者 nginx -t 来检测是否配置正确,然后重启 Nginx ,即可。

检验

在 Chrome 浏览器上可以通过,HTTP/2 and SPDY indicator 来检验,如果地址栏出现蓝色的闪电就是 h2

也可以在 chrome://net-internals/#http2 中检查。注意版本要新,姿势要帅!

配置进阶

大家都知道去年的心血漏洞将 SSL 推到了风口浪尖,所以单单支持了 h2 ,我们任然需要对 SSL 做一些安全的优化!

配置赫尔曼密钥

openssl dhparam -out dhparam.pem 2048 // 在 ssh 运行, openssl 生成 2048 位的密钥而不是当作参数写入 nginx.conf 文件。 ssl_dhparam /path/to/dhparam.pem; //在 .conf 中配置 

禁止不安全的 SSL 协议,使用安全协议

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

禁止已经不安全的加密算法

ssl_ciphers
'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4';

也可以参考:ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE;

缓解 BEAST 攻击

ssl_prefer_server_ciphers on;``` **启用 HSTS**

此举直接跳过 301 跳转,还降低了中间人攻击的风险!配置在 .conf 中即可 `add_header Strict-Transport-Security max-age=15768000;` **301 跳转** 80 端口跳转到 443 端口

server {
listen 80;
add_header Strict-Transport-Security max-age=15768000;
return 301 https://www.yourwebsite.com$request_uri;
}

 **缓存连接凭据** 

ssl_session_cache shared:SSL:20m;
ssl_session_timeout 60m;

 **OCSP 缝合** 

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/cert/trustchain.crt;
resolver 119.29.29.29 233.6.6.6 valid=300s;

nginx支持http2,openssl 升级openssl-1.1.0h https A+

首先升级openssl 到 openssl-1.1.0h

wget https://www.openssl.org/source/openssl-1.1.0h.tar.gz

tar -zxvf openssl-1.1.0h.tar.gz
cd openssl-1.1.0h

./config –prefix=/usr

make

make install

openssl version -a

nginx配置参考:

listen 443 ssl http2;
add_header Strict-Transport-Security max-age=15768001;
ssl_certificate /usr/local/nginx/conf/vhost/Nginx/1_www.dnsdizhi.com_bundle.crt;
ssl_certificate_key /usr/local/nginx/conf/vhost/Nginx/2_www.dnsdizhi.com.key;
ssl_session_cache    shared:SSL:10m;
ssl_session_timeout  10m;
ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE;
ssl_prefer_server_ciphers   on;

完毕后可以用:https://www.ssllabs.com/ssltest/  或者 https://myssl.com/ 检查就是能达到A+,当然其实达到A就不错了。如facebook、v.qq.com、www.google.com都是A。区别在哪里?就是没有配置add_header Strict-Transport-Security max-age=15768001;,说真的就是不用配置的,没必要。

Nginx负载均衡的优缺点

Nginx的优点是:
1、工作在网络的7层之上,可以针对http应用做一些分流的策略,比如针对域名、目录结构,它的正则规则比HAProxy更为强大和灵活,这也是它目前广泛流行的主要原因之一,Nginx单凭这点可利用的场合就远多于LVS了。
2、Nginx对网络稳定性的依赖非常小,理论上能ping通就就能进行负载功能,这个也是它的优势之一;相反LVS对网络稳定性依赖比较大,这点本人深有体会;
3、Nginx安装和配置比较简单,测试起来比较方便,它基本能把错误用日志打印出来。LVS的配置、测试就要花比较长的时间了,LVS对网络依赖比较大。
3、可以承担高负载压力且稳定,在硬件不差的情况下一般能支撑几万次的并发量,负载度比LVS相对小些。
4、Nginx可以通过端口检测到服务器内部的故障,比如根据服务器处理网页返回的状态码、超时等等,并且会把返回错误的请求重新提交到另一个节点,不过其中缺点就是不支持url来检测。比如用户正在上传一个文件,而处理该上传的节点刚好在上传过程中出现故障,Nginx会把上传切到另一台服务器重新处理,而LVS就直接断掉了,如果是上传一个很大的文件或者很重要的文件的话,用户可能会因此而不满。
5、Nginx不仅仅是一款优秀的负载均衡器/反向代理软件,它同时也是功能强大的Web应用服务器。LNMP也是近几年非常流行的web架构,在高流量的环境中稳定性也很好。
6、Nginx现在作为Web反向加速缓存越来越成熟了,速度比传统的Squid服务器更快,可以考虑用其作为反向代理加速器。
7、Nginx可作为中层反向代理使用,这一层面Nginx基本上无对手,唯一可以对比Nginx的就只有lighttpd了,不过lighttpd目前还没有做到Nginx完全的功能,配置也不那么清晰易读,社区资料也远远没Nginx活跃。
8、Nginx也可作为静态网页和图片服务器,这方面的性能也无对手。还有Nginx社区非常活跃,第三方模块也很多。
淘宝的前端使用的Tengine就是基于nginx做的二次开发定制版。
  Nginx的缺点是:
1、Nginx仅能支持http、https和Email协议,这样就在适用范围上面小些,这个是它的缺点。
2、对后端服务器的健康检查,只支持通过端口来检测,不支持通过url来检测不支持Session的直接保持,但能通过ip_hash来解决

Nginx下利用rewrite实现强制跳转https

Nginx下利用rewrite实现强制跳转https

if ($scheme = http) {
rewrite ^(.*) https://$server_name$1 permanent;
}

以上代码直接放入主机给的rewrite规则中即可,其中

scheme #HTTP的方法(如http,https)

$1 permanent #规则为http://domains/xxx 跳转到https://domains/xxx

项目前期使用http,后期为了安全方面的考虑,启用了https。

项目架构:前端使用nginx作为多个tomcat实例的反向代理和负载均衡。

实际上只需要在nginx上启用https即可,使客户端与nginx之后使用https方式通信,而nginx与tomcat之间依然以http方式通信。

现在需要将之前客户端所有的http请求全部都自动重定向为https,只需要在nginx上添加相应配置即可。

如下配置实现来源于Nginx HTTP 跳转至 HTTPS,但是我都实践验证过。

另外,也加入了一些自己的理解整理而成。

方式1:使用rewrite指令

server {
    listen 80;
    server_name domain.com;
    rewrite ^(.*) https://$server_name$1 permanent;
}
server {
    listen 443 ssl;
    server_name domain.com;
    ssl on;
    ssl_certificate     /etc/nginx/ssl/domain.com.crt;
    ssl_certificate_key /etc/nginx/ssl/domain.com.crt;
    # other
}

如果此时nginx作为Tomcat的前端反向代理的话,需要将相应配置放在配置ssl的server块中。

方式2:使用return指令

server {
    listen 80;
    server_name domain.com;
    return 301 https://$server_name$request_uri;
}
server {
    listen 443 ssl;
    server_name domain.com;
    ssl on;
    ssl_certificate     /etc/nginx/ssl/domain.com.crt;
    ssl_certificate_key /etc/nginx/ssl/domain.com.crt;
    # other
}

如果此时nginx作为Tomcat的前端反向代理的话,需要将相应配置放在配置ssl的server块中。

方式三:使用error_page指令

只允许HTTP来访问时,用HTTP访问会让Nginx报497错误,然后利用error_page将链接重定向至HTTPS上。

server {
    listen 80;
    listen 443 ssl;
    server_name domain.com;
    ssl on;
    ssl_certificate     /etc/nginx/ssl/domain.com.crt; 
    ssl_certificate_key /etc/nginx/ssl/domain.com.crt;
    # other
    error_page 497 https://$server_name$request_uri;
}

使用error_page指令时,将http和https的监听配置写在同一个server块中,对应的其他配置也需要在该server配置块中完成。

需要注意的是,此时需要将error_page指令语句写在最后,否则不能生效。

深入分析HTTP状态码502(nginx+php-fpm)

我们的一个web项目,由于新上城市增多,导致访问量增大,DB压力增大,作为提供接口的业务方,最近被下游反馈大量请求“502”。

502,bad gateway,一般都是upstream(这里就是php)出错,对于php,造成502的原因常见的就是脚本执行超过timeout设置时间,或者timeout设置过大,导致php进程长时间不能被释放,没有空闲worker进程来接客。

我们的项目就是php执行时间设置过短导致的,对于这种情况,可以先适当增大php的执行时间,先保证清除502,优化的事情毕竟要花更多的时间。

控制php执行时间的选项有两个,在php.ini中 max_execution_time 和php-fpm中 request_terminate_timeout,其中 request_terminate_timeout 可以覆盖 max_execution_time,所以如果不想改全局的php.ini,那只改php-fpm的配置就可以了。

 

下边我就来详细的分析一下为什么php脚本执行超出设置时间会导致nginx返回502。

先来布景,让问题复现:

nginx和php分别只启动一个worker,方便追踪。

php-fpm的request_terminate_timeout设置为3S。

测试脚本test.php

sleep(20); echo 'ok';

go go go:

在浏览器访问www.v.com/test.php,3S后如期出现…404???what???

出师不利啊,赶紧看看nginx的配置文件

这个location配置是当发生5xx错误时跳转到一个好看点的界面,但是我在/usr/share/nginx/html下并没有50x.html这个文件。所以搞了个404出来。这不是很影响我判断问题的准确性?直接注释掉!再次访问,等待3S,终于’正常’的界面出来了。

 

环境好了,下边就上套路,按照web问题的排查套路走一遍,先看看错误日志吧:

nginx:

报错都是 recv() failed (104: Connection reset by peer。

recv时失败了,连接被重置了。为啥连接被重置了?难道一言不合。

我们在看看php-fpm的错误日志:

(注意php-fpm中php_admin_value[error_log]选项指定php的错误日志,会覆盖php.ini中的。但是这里不是看php的错误,而是看php-fpm的错误。php-fpm的错误日志由php-fpm.conf中的error_log选项指定。)

每一次请求都是产生2个WARNING和1个NOTICE:

WARNING:脚本执行超时了,终止了。

WARNING:子进程收到SIGTERM信号退出了。

NOTICE:启了一个新的子进程(因为我设置的pm.min_spare_servers = 1)

看来如果php的worker进程执行超时,不仅终止脚本执行,而且worker进程也会退出。看来nginx的报错连接被重置是因为php的worker进程退出了(在TCP连接中一方如果断掉的话会发送RST给另一方)

 

通过日志已经可以知道php脚本执行超时,worker子进程退出,导致nginx报错Connection reset by peer,下边我们通过strace来看看php和nginx的情况:

php:

1.accept一个nginx的连接请求(socket,bind,listen都在master中完成 ),可以看到nginx的端口是47039,从FD0中读取数据,就是从标准输入中,这个是fast-cgi协议规定的。accept之后的已连接描述符是3。

2.从FD3中读取nginx传递过来的数据,fastcgi协议格式,接收了856字节。为什么read5次呢?

因为fastcgi协议数据包是8字节对齐,由包头和包体组成。并且都是会先发一个request数据包,包含一些请求ID,版本,typpe等信息(包头包体各占8字节),再发一个params数据包,传递get参数和环境变量(包头8字节,包体变长),最后发送一个没有包体只有包头的params数据包,表示参数发送结束(包头8字节)。所以前3个read用来读出request包的包头和包体,还有params包的包头,第四个read是读取真正的数据,最后一个read是读取最后一个params包的包头。所以nginx传递的数据应该是8+8+8+856+8=896字节(和下边nginx的传输bytes能对应上)。注意如果是post方式,还会发送stdin数据包。

3.设置休眠20S,就是php程序中的sleep(20),之后由于进程被终止了,所以后边就没啦。strace程序也退出啦。

nginx:

1.accept到浏览器的请求,可以看到浏览器端的端口是56434,IP是192.168.1.105,已建立连接的FD是3。

2.从FD3中接收数据,HTTP协议。

3.创建一个socket,FD21,用于和php建立连接。

4.连接到FD21,可以看到连接的是本机的9000端口,这里nginx和php-fpm使用IP socket连接方式,nginx和php-fpm部署在一台机器上可以考虑unix domain socket。

5.向FD21写入数据,fast-cgi协议格式,我们看到写入的长度是896,和上边的php接收的长度是对应的。

6.recvfrom函数从FD21中返回 ECONNRESET (Connection reset by peer)

7.向FD9中写入错误信息,可以推断FD9就是nginx错误日志的文件描述符。

8.关闭和FD21的连接。

9.向FD3写入502 Bad Gateway,就是返回给浏览器的信息。

10.向FD8写入一条访问日志,可以推断FD8就是nginx访问日志的文件描述符。

来验证一下nginx访问日志和错误日志的推断。可以看到的确是FD8,FD9,并处于写入模式。

 

那么在这个过程中整个网络包的传输我们不妨也看一下:

通过tcpdump抓包,用神器看比较方便。

因为只想看nginx和php的通讯,在上边又知道nginx的端口是47039,可以通过tcp.srcport==47039过滤出对应的包。

可以看到nginx和php-fpm数据交互的过程:47039->9000建立三次握手,接着向9000发送数据,9000回复ACK,3S后9000回复RST。没毛病。

注意:

SYN,FIN各占一个序列号

ACK,RST不占序列号(28,29两个包的reqnum和acknum都是相同的)

序列号是每一字节加1(29包发送896字节,同时29包seq为4219146879,30包的ack为4219147775,正好相差896)

RST不需要回复。