Chromeで旧シマンテック/ジオトラスト社のSSL証明書が信頼されたものでなくなる日に向けて...
昨年から、なんだかゴタゴタしていたやつ。 長めの期間で証明書を取得していると、再発行の該当になっているパターンがある。
信頼されなくなっちゃうパターン
ref: https://security.googleblog.com/2017/09/chromes-plan-to-distrust-symantec.html
対象1:2016年5月31日以前に発行されたSSLサーバ証明書(ラピッドSSL、クイックSSLプレミアム)
Chrome66からSSLサーバ証明書が無効化される(2018年4月17日に正式版リリース予定)
- 有効期間が2018年3月14日以前の場合は、対応不要
- 有効期間が2018年3月15日以降の場合は、2017年12月1日以降に再発行が必要
対象2:2016年6月1日2017年11月30日までに発行されたSSLサーバ証明書(ラピッドSSL、クイックSSLプレミアム、セキュア・サーバID、セキュア・サーバID EV、グローバル・サーバID)
Chrome70からSSLサーバ証明書が無効化される(2018年10月23日に正式版リリース予定)
- 有効期間が2018年9月12日以前の場合は、対応不要
- 有効期間が2018年9月13日以降の場合は、2017年12月1日以降に再発行が必要
簡易的なチェカースクリプト作った
管理対象の証明書がそれなりにあると、手作業チェックは漏れもありそうなので、確認の意味もこめて、チェックスクリプト(簡易版)を作ってみた。 Gist:kacchan822/check_cert_chrome.py 記事末尾にも載っけてある
以下の具合で、対象1または2で対応が必要だと、それぞれCRITICAL-1、CRITICAL-2と教えてくれる。対象1または2だけど、対応不要な場合も、一応WARNINGになる。
$ python3 check_cert_chrome.py www.example.com [ OK ] www.example.com: Cert was published at 2017-12-15 20:03:18. (Let's Encrypt, Let's Encrypt Authority X3) $ python3 check_cert_chrome.py www.example.com [ WARNING ] www.example.com: Cert was published at 2016-03-16 00:00:00; before 2016-05-31. (GeoTrust Inc., RapidSSL SHA256 CA) $ python3 check_cert_chrome.py www.example.com [ WARNING ] www.example.com: Cert was published at 2017-06-10 00:00:00; between 2016-06-01 and 2017-11-30. (Symantec Corporation, Symantec Class 3 Secure Server CA - G4) $ python3 check_cert_chrome.py www.example.com [ CRITICAL-1 ] www.example.com: Cert was published at 2016-03-16 00:00:00; before 2016-05-31. (GeoTrust Inc., RapidSSL SHA256 CA) $ python3 check_cert_chrome.py www.example.com [ CRITICAL-2 ] www.example.com: Cert was published at 2017-04-01 00:00:00; between 2016-06-01 and 2017-11-30. (Symantec Corporation, Symantec Class 3 Secure Server CA - G4)
すべての証明書を正しく判定できるわけではないので、あしからず…。
Gist:kacchan822/check_cert_chrome.py
#!/usr/bin/env python3 # This software is released under the MIT License. # http://opensource.org/licenses/mit-license.php import datetime import re import socket import ssl import sys timeout = 5 def get_cert(cn, port=443): """ return: dict {'OCSP': ('http://ocsp.int-x3.letsencrypt.org',), 'caIssuers': ('http://cert.int-x3.letsencrypt.org/',), 'issuer': ((('countryName', 'US'),), (('organizationName', "Let's Encrypt"),), (('commonName', "Let's Encrypt Authority X3"),)), 'notAfter': 'Mar 15 20:46:19 2018 GMT', 'notBefore': 'Dec 15 20:46:19 2017 GMT', 'serialNumber': '0467F6AD9DE0A1D776DC5688B22569094A49', 'subject': ((('commonName', 'ip.ksn.cloud'),),), 'subjectAltName': (('DNS', 'ip.ksn.cloud'), ('DNS', 'ip4.ksn.cloud'), ('DNS', 'ip6.ksn.cloud')), 'version': 3} """ context = ssl.create_default_context() try: with context.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM), server_hostname=cn) as conn: conn.settimeout(timeout) conn.connect((cn, port)) return conn.getpeercert() except ssl.CertificateError as e: print('Errorr:', e) sys.exit(1) def _conv_dt(datetime_string): cert_date_format = '%b %d %H:%M:%S %Y %Z' return datetime.datetime.strptime(datetime_string, cert_date_format) def _get_cert_datetime(cert): return _conv_dt(cert['notBefore']), _conv_dt(cert['notAfter']) def check_issuer(cert): if cert.get('issuer'): issuer = {key_val[0][0]: key_val[0][1] for key_val in cert.get('issuer')} else: issuer = {} issuer['checkFlag'] = True if re.match(r'(Symantec|GeoTrust)', issuer.get('organizationName', '')): issuer['checkFlag'] = False if re.match(r'RapidSSL', issuer.get('commonName', '')): issuer['checkFlag'] = False return issuer def check_cert_datetime(cert): start, end = _get_cert_datetime(cert) if start < datetime.datetime(2016, 6, 1, 0, 0, 0): flag = 1 msg = 'Cert was published at {}; before 2016-05-31.' elif (datetime.datetime(2016, 5, 31, 23, 59, 59) < start and start < datetime.datetime(2017, 12, 1, 0, 0, 0)): flag = 2 msg = 'Cert was published at {}; between 2016-06-01 and 2017-11-30.' else: flag = 0 msg = 'Cert was published at {}.' return flag, msg.format(str(start)) def main(cn): try: cn, port = cn.strip().split(':') except ValueError: cn = cn.strip() port = 443 cert = get_cert(cn, int(port)) issuer = check_issuer(cert) cert_datetime = check_cert_datetime(cert) cert_start, cert_end = _get_cert_datetime(cert) if not issuer['checkFlag']: if (cert_datetime[0] == 1 and cert_end > datetime.datetime(2016, 6, 1, 0, 0, 0)): state_msg = 'CRITICAL-1' elif (cert_datetime[0] == 2 and cert_end > datetime.datetime(2018, 9, 12, 23, 59, 59)): state_msg = 'CRITICAL-2' else: state_msg = 'WARNING' else: state_msg = 'OK' message = '[{: ^12}] {}: {} ({}, {})'.format( state_msg, cn, cert_datetime[1], issuer.get('organizationName', '-'), issuer.get('commonName', '-') ) return message if __name__ == '__main__': print(main(sys.argv[1]))