HPKP 格式及说明
HTTP Public Key Pinning(HPKP)的格式如下:
Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubdomains][; report-uri="reportURI"]
pin-sha256
即证书指纹,允许出现多次(实际上最少应该指定两个);
max-age
和 includeSubdomains
分别是过期时间和是否包含子域,它们在 HSTS(HTTP Strict Transport Security)中也有,格式和含义一致;
report-uri
用来指定验证失败时的上报地址,格式和含义跟 CSP(Content Security Policy)中的同名字段一致;
includeSubdomains
和 report-uri
均为可选。
为了验证合法性,pin-sha256 必须由网站当前证书链中的证书生成。例如我的网站证书有三级,用根证书、中间证书、站点证书中的任何一个生成指纹都可以。用站点证书生成指纹的好处是安全性最高,缺点是证书重签之后指纹就变了,如果之前没提供备用指纹,老用户无法访问;用根证书生成指纹安全性最差,因为每个根证书都对应很多中间证书,攻击者只要攻破其中一个,就可以签出能被 HPKP 策略信任的站点证书。
综合考虑安全性和易用性,一般推荐使用中间证书生成指纹;用知名 CA 的根证书也可以;不推荐使用站点证书,除非充分了解后果并且指定了有效的备用指纹。
备用指纹是为那些必须更换中间或者根证书的场景准备的 —— 例如原来的 CA 突然倒闭或者被黑了。本站当前使用的证书由 RapidSSL SHA256 CA – G4 签发,我的pin-sha256 包含了它的指纹;同时我还用 Let’s Encrypt Authority X1 这个中间证书生成了备用指纹。这样如果后续本站改为使用 Let’s Encrypt Authority X1 签发的证书,老用户也不会受影响。
生成 pin-sha256
从已知的密钥对(*.key)生成:
openssl rsa -in lzw.me_ssl.key -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64
从已知的证书申请文件(*.csr)生成:
openssl req -in lzw.me.csr -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
从已知的证书(*.crt)生成:
openssl x509 -in lzw.me_bundle.crt -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
nginx配置:
add_header Public-Key-Pins 'pin-sha256="第一个base64"; pin-sha256="备用base64"; max-age=时长; includeSubDomains';
# 如
Public-Key-Pins:
pin-sha256="GRAH5Ex+kB4cCQi5gMU82urf+6kEgbVtzfCSkw55AGk=";
pin-sha256="lERGk61FITjzyKHcJ89xpc6aDwtRkOPAU0jdnUqzW2s=";
max-age=15768000; includeSubDomains
一般来说,应至少提供两个 fingerprint(其中一个留做备用),而 max-age 应该尽量地长。
相关参考
https://developer.mozilla.org/en-US/docs/Web/Security/Public_Key_Pinning
https://www.delphij.net/wiki/notes/t/hpkp_howto
http://www.yidianzixun.com/home?page=article&id=0CZteTdI