{{item}}
{{item.title}}
{{items.productName}}
{{items.price}}/年
{{item.title}}
部警SSL证书可实现网站HTTPS加密保护及身份的可信认证,防止传输数据的泄露或算改,提高网站可信度和品牌形象,利于SEO排名,为企业带来更多访问量,这也是网络安全法及PCI合规性的必备要求
前往SSL证书在SSL证书全生命周期管理中,“状态检测” 是风险预警的第一道防线 —— 通过实时检测证书的有效期、吊销状态、加密配置等核心指标,可提前发现潜在风险(如证书即将过期、启用弱加密算法),避免HTTPS服务中断或数据泄露。传统人工检测方式效率低、易遗漏,而自动化检测脚本结合监控平台的方案,能实现 “实时检测 - 异常告警 - 数据可视化” 的闭环管理。本文将从技术选型、脚本实现、平台接入三个维度,详细讲解如何构建SSL证书状态检测体系,适用于运维工程师、安全人员开展自动化监控落地。
检测维度 | 核心指标 | 检测目标(示例) |
---|---|---|
有效性 | 证书有效期、证书链完整性 | 1. 检测证书是否在有效期内,计算剩余天数(如≤30 天标记为高风险);2. 验证证书链是否完整(无缺失中间证书 / 根证书) |
安全性 | TLS 协议版本、加密套件、吊销状态 | 1. 检测是否启用弱 TLS 版本(≤1.1)或不安全加密套件(如 RC4、DES);2. 校验证书是否被吊销(通过 CRL/OCSP 查询) |
可用性 | HTTPS 握手成功率、响应时间 | 1. 检测目标域名 HTTPS 服务是否正常(握手是否成功);2. 统计握手响应时间(如>500ms 标记为异常) |
根据检测场景(单机脚本 / 批量检测)、执行环境(Linux/Windows)选择合适的技术栈,兼顾效率与兼容性:
技术类型 | 工具 / 语言推荐 | 优势 | 适用场景 |
---|---|---|---|
脚本开发语言 | Python(推荐)、Bash | Python 支持丰富的 SSL 库(如ssl、cryptography),易处理证书解析;Bash 依赖系统工具(如openssl),轻量无依赖 | Python 适合批量检测 / 复杂逻辑;Bash 适合单机快速检测 |
证书解析工具 | OpenSSL(系统工具)、cryptography(Python 库) | OpenSSL 支持命令行快速获取证书信息;cryptography 支持 Python 代码层面解析证书字段(如有效期、主题) | 命令行脚本用 OpenSSL;Python 脚本用 cryptography |
吊销状态查询 | OCSP 协议(实时)、CRL 文件(离线) | OCSP 可实时查询证书吊销状态(需联网);CRL 可离线查询(需提前下载 CRL 文件) | 实时检测用 OCSP;无网络环境用 CRL |
监控平台 | Prometheus+Grafana、Zabbix、Nagios | Prometheus 适合时序数据存储与告警;Zabbix 支持多维度监控与自动发现;Nagios 轻量易部署 | 企业级监控用 Prometheus+Grafana;中小型场景用 Zabbix/Nagios |
适用于单域名 / 少量域名的快速检测,依赖系统自带的OpenSSL工具,无需额外安装依赖,脚本核心功能为 “获取有效期、检测TLS版本、验证握手状态”。
1. 脚本代码实现(ssl_check_single.sh)
#!/bin/bash
# 功能:检测单个域名的SSL证书状态
# 参数:$1=目标域名(如example.com),$2=端口(默认443)
# 初始化参数
DOMAIN=$1
PORT=${2:-443}
TIMEOUT=10 # 超时时间(秒)
TODAY=$(date +%s) # 当前时间戳(秒)
# 1. 检测HTTPS握手是否成功
if ! openssl s_client -connect "${DOMAIN}:${PORT}" -servername "${DOMAIN}" -tlsextdebug -no_ticket -timeout "${TIMEOUT}" < /dev/null > /dev/null 2>&1; then
echo "【ERROR】${DOMAIN}:${PORT} HTTPS握手失败,服务不可用"
exit 1
fi
# 2. 获取证书有效期(开始时间、结束时间、剩余天数)
# 提取证书的"Not Before"和"Not After"字段(时间格式:Jan 1 00:00:00 2024 GMT)
CERT_INFO=$(openssl s_client -connect "${DOMAIN}:${PORT}" -servername "${DOMAIN}" -timeout "${TIMEOUT}" < /dev/null 2>/dev/null | openssl x509 -noout -dates)
NOT_BEFORE=$(echo "${CERT_INFO}" | grep "notBefore" | awk -F'=' '{print $2}')
NOT_AFTER=$(echo "${CERT_INFO}" | grep "notAfter" | awk -F'=' '{print $2}')
# 转换为时间戳(秒)
NOT_AFTER_TIMESTAMP=$(date -d "${NOT_AFTER}" +%s 2>/dev/null)
if [ -z "${NOT_AFTER_TIMESTAMP}" ]; then
echo "【ERROR】${DOMAIN}:${PORT} 证书有效期解析失败"
exit 1
fi
# 计算剩余天数
REMAIN_DAYS=$(( (NOT_AFTER_TIMESTAMP - TODAY) / 86400 ))
# 3. 检测支持的TLS版本(是否包含弱版本)
TLS_VERSIONS=$(openssl s_client -connect "${DOMAIN}:${PORT}" -servername "${DOMAIN}" -timeout "${TIMEOUT}" < /dev/null 2>&1 | grep -E "TLSv1\.[01]|SSLv" | wc -l)
WEAK_TLS="无"
if [ "${TLS_VERSIONS}" -gt 0 ]; then
WEAK_TLS="有(启用TLS1.0/TLS1.1/SSL)"
fi
# 4. 输出检测结果
echo "====================================="
echo "域名:${DOMAIN}:${PORT}"
echo "证书状态:有效"
echo "有效期开始:${NOT_BEFORE}"
echo "有效期结束:${NOT_AFTER}"
echo "剩余天数:${REMAIN_DAYS} 天"
echo "弱TLS版本:${WEAK_TLS}"
echo "====================================="
# 风险判断(剩余天数≤30天或存在弱TLS)
if [ "${REMAIN_DAYS}" -le 30 ]; then
echo "【WARNING】证书即将过期(剩余${REMAIN_DAYS}天),请及时更新!"
exit 2
fi
if [ "${WEAK_TLS}" = "有(启用TLS1.0/TLS1.1/SSL)" ]; then
echo "【WARNING】存在弱TLS版本,建议禁用TLS1.0/TLS1.1!"
exit 2
fi
2. 脚本使用与参数说明
适用于多域名批量检测(如企业内部数十个业务域名),支持证书吊销状态查询(OCSP)、证书链完整性校验,功能更全面,可输出结构化数据(JSON)便于后续接入监控平台。
1. 环境依赖安装
# 安装Python依赖库
pip install cryptography requests pyopenssl
2. 脚本代码实现(ssl_check_batch.py)
import ssl
import json
import time
import socket
import requests
from datetime import datetime
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.x509.oid import NameOID
from OpenSSL import SSL
# 配置参数
TIMEOUT = 10 # 超时时间(秒)
DOMAINS = [ # 批量检测的域名列表(域名:端口)
("example.com", 443),
("pay.example.com", 443),
("api.example.com", 443)
]
OCSP_URL = "http://ocsp.digicert.com" # 示例:DigiCert的OCSP服务器(需根据CA调整)
def get_certificate_chain(domain, port):
"""获取域名的证书链(叶子证书+中间证书)"""
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE # 先不验证,后续手动校验证书链
try:
with socket.create_connection((domain, port), timeout=TIMEOUT) as sock:
with context.wrap_socket(sock, server_hostname=domain) as secure_sock:
# 获取证书链(PEM格式)
cert_chain = secure_sock.getpeercert(binary_form=True)
# 解析叶子证书
leaf_cert = x509.load_der_x509_certificate(cert_chain, default_backend())
# 获取中间证书(部分环境需额外处理,此处简化)
return leaf_cert, []
except Exception as e:
return None, f"获取证书链失败:{str(e)}"
def check_cert_validity(leaf_cert):
"""检查证书有效期"""
now = datetime.utcnow()
not_before = leaf_cert.not_valid_before_utc
not_after = leaf_cert.not_valid_after_utc
# 计算剩余天数(UTC时间)
remain_days = (not_after - now).days
# 判断有效性
is_valid = now >= not_before and now <= not_after
return {
"is_valid": is_valid,
"not_before": not_before.strftime("%Y-%m-%d %H:%M:%S UTC"),
"not_after": not_after.strftime("%Y-%m-%d %H:%M:%S UTC"),
"remain_days": remain_days
}
def check_ocsp_revocation(leaf_cert, ocsp_url):
"""通过OCSP查询证书吊销状态"""
try:
# 提取证书的OCSP相关信息( issuer_name_hash、issuer_key_hash、serial_number )
issuer_name_hash = ssl._ssl._test_decode_cert(leaf_cert.public_bytes(x509.Encoding.DER))["issuer_name_hash"]
issuer_key_hash = ssl._ssl._test_decode_cert(leaf_cert.public_bytes(x509.Encoding.DER))["issuer_key_hash"]
serial_number = hex(leaf_cert.serial_number)[2:].upper() # 转为十六进制(大写)
# 构造OCSP查询URL(示例格式,需根据CA调整)
ocsp_query = f"{ocsp_url}/?serial={serial_number}&hash={issuer_name_hash}:{issuer_key_hash}"
response = requests.get(ocsp_query, timeout=TIMEOUT)
# 解析OCSP响应(简化判断:200且包含"good"表示未吊销)
if response.status_code == 200 and "good" in response.text.lower():
return {"is_revoked": False, "status": "未吊销"}
else:
return {"is_revoked": True, "status": "已吊销或查询失败"}
except Exception as e:
return {"is_revoked": None, "status": f"OCSP查询异常:{str(e)}"}
def check_tls_ciphers(domain, port):
"""检测支持的TLS版本与加密套件"""
weak_tls_versions = ["TLSv1", "TLSv1.1", "SSLv3"] # 弱TLS版本列表
supported_tls = []
has_weak_tls = False
# 尝试连接不同TLS版本
for tls_version in [SSL.TLSv1_METHOD, SSL.TLSv1_1_METHOD, SSL.TLSv1_2_METHOD, SSL.TLSv1_3_METHOD]:
try:
context = SSL.Context(tls_version)
with socket.create_connection((domain, port), timeout=TIMEOUT) as sock:
with context.wrap_socket(sock, server_hostname=domain) as secure_sock:
tls_ver = secure_sock.version()
supported_tls.append(tls_ver)
if tls_ver in weak_tls_versions:
has_weak_tls = True
except:
continue
return {
"supported_tls_versions": supported_tls,
"has_weak_tls": has_weak_tls
}
def batch_check():
"""批量检测入口"""
results = []
for domain, port in DOMAINS:
result = {
"domain": domain,
"port": port,
"check_time": datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC"),
"status": "success"
}
# 1. 获取证书链
leaf_cert, error = get_certificate_chain(domain, port)
if not leaf_cert:
result["status"] = "failed"
result["error"] = error
results.append(result)
continue
# 2. 检查证书有效期
validity = check_cert_validity(leaf_cert)
result["validity"] = validity
# 3. 检查吊销状态(OCSP)
revocation = check_ocsp_revocation(leaf_cert, OCSP_URL)
result["revocation"] = revocation
# 4. 检查TLS版本与加密套件
tls_info = check_tls_ciphers(domain, port)
result["tls_info"] = tls_info
# 5. 风险标记
result["risk_level"] = "low"
if not validity["is_valid"]:
result["risk_level"] = "high"
result["risk_msg"] = "证书已过期或未生效"
elif validity["remain_days"] <= 30:
result["risk_level"] = "medium"
result["risk_msg"] = f"证书即将过期(剩余{validity['remain_days']}天)"
if revocation["is_revoked"]:
result["risk_level"] = "high"
result["risk_msg"] = "证书已被吊销"
if tls_info["has_weak_tls"]:
result["risk_level"] = "medium"
result["risk_msg"] = "启用了弱TLS版本(TLS1.0/TLS1.1)"
results.append(result)
# 输出JSON格式结果(便于接入监控平台)
print(json.dumps(results, indent=2, ensure_ascii=False))
# 保存结果到文件
with open(f"/var/log/ssl_batch_check_{int(time.time())}.json", "w", encoding="utf-8") as f:
json.dump(results, f, indent=2, ensure_ascii=False)
if __name__ == "__main__":
batch_check()
3. 脚本核心功能说明
修改Python批量检测脚本的batch_check函数,在输出JSON结果后,增加Prometheus指标打印逻辑,确保指标格式符合 “指标名{标签键=标签值,...} 指标值” 规范:
# 输出Prometheus指标(标准格式)
prom_metrics = []
for res in results:
domain = res["domain"]
port = res["port"]
# 1. 证书剩余天数指标( gauge 类型,支持任意数值)
if res["status"] == "success":
remain_days = res["validity"]["remain_days"]
prom_metrics.append(f'ssl_cert_remain_days{{domain="{domain}",port="{port}"}} {remain_days}')
# 2. 证书有效性指标(0=无效,1=有效,counter 类型)
is_valid = 1 if res["validity"]["is_valid"] else 0
prom_metrics.append(f'ssl_cert_is_valid{{domain="{domain}",port="{port}"}} {is_valid}')
# 3. 弱TLS版本指标(0=无,1=有)
has_weak_tls = 1 if res["tls_info"]["has_weak_tls"] else 0
prom_metrics.append(f'ssl_cert_has_weak_tls{{domain="{domain}",port="{port}"}} {has_weak_tls}')
# 4. 证书吊销状态指标(0=未吊销,1=已吊销,2=查询异常)
if res["revocation"]["is_revoked"] is False:
is_revoked = 0
elif res["revocation"]["is_revoked"] is True:
is_revoked = 1
else:
is_revoked = 2
prom_metrics.append(f'ssl_cert_is_revoked{{domain="{domain}",port="{port}"}} {is_revoked}')
else:
# 检测失败时,指标值设为-1标记异常
prom_metrics.append(f'ssl_cert_remain_days{{domain="{domain}",port="{port}"}} -1')
prom_metrics.append(f'ssl_cert_is_valid{{domain="{domain}",port="{port}"}} -1')
prom_metrics.append(f'ssl_cert_has_weak_tls{{domain="{domain}",port="{port}"}} -1')
prom_metrics.append(f'ssl_cert_is_revoked{{domain="{domain}",port="{port}"}} -1')
# 打印指标(Prometheus抓取时需读取stdout)
print("# HELP ssl_cert_remain_daysSSL证书剩余有效天数")
print("# TYPE ssl_cert_remain_days gauge")
print("\n".join(prom_metrics))
[Unit]
Description=SSL Certificate Status Exporter
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/bin/python3 /opt/ssl_scripts/ssl_check_batch.py | /usr/bin/nc -lk 0.0.0.0 9292
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
scrape_configs:
- job_name: 'ssl_cert_status'
scrape_interval: 5m # 每5分钟抓取一次(根据需求调整)
static_configs:
- targets: ['localhost:9292'] # Exporter地址(若部署在其他机器,替换为对应IP)
1. 导入Prometheus数据源:进入Grafana UI(http://grafana-ip:3000),在 “Configuration> Data Sources” 中添加 Prometheus,填写Prometheus地址(如http://localhost:9090);
2. 创建仪表盘:
3. 配置告警规则:
Zabbix支持通过 “外部脚本” 采集自定义指标,结合 “自动发现” 功能可实现多域名批量监控。
chmod +x /usr/lib/zabbix/externalscripts/ssl_check_batch.py
chown zabbix:zabbix /usr/lib/zabbix/externalscripts/ssl_check_batch.py
1. 创建模板:进入Zabbix UI,在 “Configuration> Templates” 中新建模板(如 “Template SSL Certificate Status”);
2. 创建监控项原型:
(1)添加 “Discovery Rule”(自动发现规则),设置 “Type” 为 “External check”,“External check” 为ssl_check_batch.py --discover(脚本需返回 JSON 格式的域名列表,如{"data":[{"{#DOMAIN}":"example.com","{#PORT}":"443"},...]});
(2)基于发现规则创建 “Item Prototypes”(监控项原型),如:
3. 创建触发器:添加触发器原型(如 “SSL Certificate Remain Days <= 30”),设置触发级别为 “Warning”;“SSL Certificate Is Revoked” 触发级别为 “High”。
Nagios通过 “插件” 扩展监控能力,可将检测脚本封装为Nagios插件,实现状态监控与告警。
#!/bin/bash
# Nagios插件:检测SSL证书状态
# 参数:$1=域名,$2=端口
DOMAIN=$1
PORT=$2
# 调用单机检测脚本,获取返回码与输出
RESULT=$(bash /opt/ssl_scripts/ssl_check_single.sh $DOMAIN $PORT 2>&1)
EXIT_CODE=$?
# 按Nagios格式输出
case $EXIT_CODE in
0)
echo "SSL OK - $RESULT"
exit 0
;;
1)
echo "SSL CRITICAL - $RESULT"
exit 2
;;
2)
echo "SSL WARNING - $RESULT"
exit 1
;;
*)
echo "SSL UNKNOWN - 检测异常:$RESULT"
exit 3
;;
esac
define command{
command_name check_ssl_cert
command_line $USER1$/check_ssl_cert.sh $ARG1$ $ARG2$
}
define service{
use generic-service
host_name localhost
service_description SSL Certificate Status (example.com)
check_command check_ssl_cert!example.com!443
check_interval 60 # 每60分钟检测一次
retry_interval 10 # 异常时每10分钟重试一次
notification_interval 30 # 告警通知间隔30分钟
}
# crontab配置(执行后记录日志,若执行失败发送邮件)
0 * * * * /usr/bin/python3 /opt/ssl_scripts/ssl_check_batch.py >> /var/log/ssl_batch_check.log 2>&1 || echo "SSL检测脚本执行失败" | mail -s "SSL脚本告警" admin@example.com
① 目标域名HTTPS服务是否正常(ping example.com+telnet example.com 443检查网络连通性);
② OpenSSL版本是否过低(执行openssl version,建议≥1.1.1,旧版本可能无法解析新型证书格式);
① 修复网络问题(如防火墙开放 443 端口);
② 升级OpenSSL(如 CentOS系统通过yum update openssl,Ubuntu系统通过apt install --upgrade openssl)。
① Exporter服务是否正常运行(systemctl status ssl-exporter,查看是否有 “端口被占用” 报错);
② Prometheus与Exporter之间是否有网络隔离(如防火墙禁止 9292 端口访问);
① 重启 Exporter 服务,若端口被占用,修改监听端口(如改为 9293);
② 开放防火墙端口(firewall-cmd --add-port=9292/tcp --permanent && firewall-cmd --reload)。
① OCSP服务器地址是否正确(不同CA机构的OCSP地址不同,如 Let's Encrypt为http://r3.o.lencr.org,需从证书的 “Authority Information Access” 字段获取);
② 服务器是否能访问OCSP地址(curl http://ocsp.digicert.com检查连通性);
① 从证书中提取正确的OCSP地址(通过openssl x509 -in cert.pem -noout -ocsp_uri);
② 若服务器无法联网,切换为CRL离线查询(提前下载CA的CRL文件,脚本中读取本地CRL文件校验吊销状态)。
SSL证书状态检测脚本与监控平台的结合,是实现证书风险 “早发现、早处理” 的关键手段。在实际落地中,需根据企业规模选择合适的监控平台(大型企业首选Prometheus+Grafana,中小型企业可选Zabbix/Nagios),通过 “脚本输出标准化指标→平台抓取与可视化→异常告警” 的流程,构建完整的监控闭环。
Dogssl.com拥有20年网络安全服务经验,提供构涵盖国际CA机构Sectigo、Digicert、GeoTrust、GlobalSign,以及国内CA机构CFCA、沃通、vTrus、上海CA等数十个SSL证书品牌。全程技术支持及免费部署服务,如您有SSL证书需求,欢迎联系!