模块-证书透明度查询-censys 接口过期(不可用)了
Author: gaogzhenCreated Sep 15, 2026Updated Sep 15, 2026
Labelsbug
是否使用了最新代码 是
Bug描述 censys 子域名查询接口不可用
运行环境
- 系统:[例如Windows 10 x64](必写)
- Python版本:[例如3.7.1](必写)
- OneForAll版本:[例如0.3.0](必写)
如何复现 复现步骤(选写)
复现命令(必写)
报错文本 复制完整的报错文本(必写)
预期结果 清晰而简洁的预期结果描述(选写,如正常情况应该是怎么样的)
实际结果 清晰而简洁的实际结果描述(选写,如出现什么错误)
屏幕截图
日志上传 上传oneforall.log日志文件(复杂问题建议上传)
其他补充 1 censys 不在支持传统搜索,详情地址https://censys.com/blog/legacy-search-deprecation 2 新注册账号没有API ID 和Secret,个人免费账号权限标志改为:persional_access_token
通过python sdk 简单改写代码:
import traceback
from censys_platform import SDK
from censys_platform.models.errormodel import ErrorModel
from common.query import Query
from config import settings
from config.log import logger
class CensysPlatformAPI(Query):
"""
tag: 当前不适合免费用户
"""
def __init__(self, domain):
Query.__init__(self)
self.domain = domain
self.module = 'Certificate'
self.source = "CensysPlatformAPIQuery"
self.personal_access_token = settings.censys_personal_access_token
self.delay = 3.0 # Censys 接口查询速率限制 最快2.5秒查1次
self.sdk = SDK(settings.censys_personal_access_token)
# 模块启用标志:遇到 403/401 等永久性错误时置为 False
self.enabled = True
# ---------------------------------------------------------------
# 查询入口
# ---------------------------------------------------------------
def query(self):
"""
向接口查询子域并做子域匹配
分层异常处理策略:
1. ErrorModel —— Censys 业务错误(401/403/429/5xx)
2. AttributeError —— 响应结构异常
3. TimeoutError —— 网络超时
4. Exception —— 兜底捕获
"""
# ---------- 0. 提前退出:模块已被禁用 ----------
if not self.enabled:
logger.log('DEBUG', f'[{self.source}] Module disabled, skip query')
return
# ---------- 1. 构造查询参数 ----------
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
escaped_domain = self.domain.replace('.', r'\.')
query_str = f"cert.names=~`.*\\..*\\.{escaped_domain}$`"
params = {"query": query_str}
logger.log('DEBUG', f'[{self.source}] Censys query: {query_str}')
# ---------- 2. 调用 API(分层捕获) ----------
try:
# todo 后续接口返回数据适配
resp = self.sdk.global_data.search(search_query_input_body=params)
except ErrorModel as e:
# -------- 2.1 Censys 业务错误 --------
e = e.data
status = getattr(e, 'status', None)
detail = getattr(e, 'detail', None) or getattr(e, 'title', None)
if status == 403:
# 权限错误:永久性,禁用模块并提前退出
logger.log('FATAL', f'[{self.source}] API access forbidden: {detail}')
self.enabled = False
return
if status == 401:
# 认证失败:永久性,禁用模块
logger.log('FATAL', f'[{self.source}] Authentication failed: {detail}')
self.enabled = False
return
if status == 429:
# 限流:临时性,记录后返回
logger.log('ALERT', f'[{self.source}] Rate limited (429): {detail}')
logger.log('DEBUG', traceback.format_exc())
return
if status and 500 <= status < 600:
# 服务端错误:临时性
logger.log('ALERT', f'[{self.source}] Server error ({status}): {detail}')
logger.log('DEBUG', traceback.format_exc())
return
# 其他业务错误
logger.log('ALERT', f'[{self.source}] Censys API error '
f'(status={status}): {detail}')
logger.log('DEBUG', traceback.format_exc())
return
except AttributeError as e:
# -------- 2.2 响应结构异常 --------
logger.log('ERROR', f'[{self.source}] Invalid response structure: {e.args}')
logger.log('DEBUG', traceback.format_exc())
return
except TimeoutError as e:
# -------- 2.3 网络超时 --------
logger.log('ALERT', f'[{self.source}] Request timeout: {e.args}')
logger.log('DEBUG', traceback.format_exc())
return
except Exception as e:
# -------- 2.4 兜底捕获 --------
logger.log('ERROR', f'[{self.source}] Unexpected error while querying '
f'{self.domain}: {e.args}')
logger.log('DEBUG', traceback.format_exc())
return
# ---------- 3. 空响应检查 ----------
if not resp:
logger.log('ALERT', f'[{self.source}] Empty response for {self.domain}')
return
# ---------- 4. 提取可解析文本 ----------
try:
text = self._extract_text(resp)
if not text:
logger.log('ALERT', f'[{self.source}] Response has no parsable text')
return
except Exception as e:
logger.log('ERROR', f'[{self.source}] Failed to extract text: {e.args}')
logger.log('DEBUG', traceback.format_exc())
return
# ---------- 5. 匹配子域名 ----------
try:
subdomains = self.match_subdomains(text)
if not subdomains:
logger.log('DEBUG', f'[{self.source}] No subdomain matched '
f'for {self.domain}')
return
self.subdomains.update(subdomains)
logger.log('INFOR', f'[{self.source}] Found {len(subdomains)} '
f'subdomains for {self.domain}')
except Exception as e:
logger.log('ERROR', f'[{self.source}] Failed to match subdomains: {e.args}')
logger.log('DEBUG', traceback.format_exc())
return
# ---------------------------------------------------------------
# 辅助方法
# ---------------------------------------------------------------
@staticmethod
def _extract_text(resp):
"""
从响应对象中提取可解析的文本。
兼容多种响应形态:
- 带 .text 属性的对象
- dict
- 其他可转换为字符串的对象
"""
if resp is None:
return None
if hasattr(resp, 'text'):
return resp.text
if isinstance(resp, dict):
return resp.get('text') or str(resp)
return str(resp)
# ---------------------------------------------------------------
# 执行入口
# ---------------------------------------------------------------
def run(self):
"""
类执行入口
"""
if not self.have_api(self.personal_access_token):
return
# 若模块在之前的调用中已被禁用,直接跳过
if not self.enabled:
logger.log('DEBUG', f'[{self.source}] Module disabled, skip run')
return
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def run(domain):
"""
类统一调用入口
:param str domain: 域名
"""
query = CensysPlatformAPI(domain)
query.run()
if __name__ == '__main__':
run('baidu.com')
# query = CensysPlatformAPI("baidu.com")
# query.begin()测试结果: 13:36:48,993 [FATAL] censys_platform_api:69 - [CensysPlatformAPIQuery] API access forbidden: This endpoint requires an organization ID for API access. Free users can only access this endpoint through the Platform UI. 13:36:48,994 [INFOR] module:63 - CensysPlatformAPIQuery module took 1.6 seconds found 0 subdomains 总结:查询子域名接口对免费用户不适用,选项一:通过web UI爬虫爬取;选项二:升级plan,即付费
Source: shmilylty/OneForAll