Quake 服务索引数据检索
接口说明
按 Quake 查询语句(QRule DSL)在 quake_service(服务维度网络测绘资产)索引中检索资产,返回以单条服务记录为单位(IP + 端口 + 协议 + 服务)的结构化数据,支持分页、时间范围过滤、返回字段裁剪与多种输出格式。 quake_service 是 Quake 最核心、数据量最大的索引,每条记录对应一个具体的开放服务,粒度比 quake_host(主机维度)更细。
请求地址
https://api.f.360.cn/api/v3/search/quake_service
请求方法
POST
请求参数
| 名称 | 类型 | 位置 | 是否必填 | 示例值 | 描述 |
|---|---|---|---|---|---|
| query | string | body | 是 | - | 查询语句(支持的字段为获取服务数据筛选字段接口返回的字段) |
| rule | string | body | 否 | - | 类型为IP列表的服务数据收藏名称 |
| ip_list | List[string] | body | 否 | - | IP列表 |
| start | int | body | 否 | 0 | 分页起始 |
| size | int | body | 否 | 10 | 分页大小 |
| ignore_cache | bool | body | 否 | false | 是否忽略缓存 |
| start_time | string | body | 否 | - | 查询起始时间,接受2020-10-14 00:00:00格式的数据,时区为UTC |
| end_time | string | body | 否 | - | 查询截止时间,接受2020-10-14 00:00:00格式的数据,时区为UTC |
| include | List(string) | body | 否 | - | 包含字段 |
| exclude | List(string) | body | 否 | - | 排除字段 |
| latest | string | body | 否 | - | 是否只统计最新数据(去重库 unique);false 使用全量库 |
响应参数
| 一级字段 | 二级字段 | 类型 | 可选值 | 描述 |
|---|---|---|---|---|
| code | int | 0 | 响应状态码,0 表示成功 | |
| message | string | 响应说明 | ||
| data | array | 查询结果列表 | ||
| data | id | string | 资产唯一标识 | |
| data | ip | string | IP 地址 | |
| data | port | integer | 服务端口 | |
| data | transport | string | tcp/udp | 传输协议 |
| data | hostname | string | 主机名 | |
| data | org | string | 所属组织 | |
| data | os_name | string | 操作系统名称 | |
| data | asn | integer | ASN 编号 | |
| data | is_ipv6 | boolean | true/false | 是否为 IPv6 |
| data | time | string | ISO8601 | 数据更新时间 |
| data | version | string | 协议版本 |
components
| 一级字段 | 二级字段 | 类型 | 说明 |
|---|---|---|---|
| data.components | array | 识别出的产品组件列表 | |
| data.components | product_level | string | 产品层级 |
| data.components | product_type | array | 产品类型 |
| data.components | product_vendor | string | 产品厂商 |
| data.components | product_name_cn | string | 产品中文名称 |
| data.components | product_name_en | string | 产品英文名称 |
| data.components | version | string | 产品版本 |
| data.components | product_catalog | array | 产品分类 |
| data.components | id | string | 产品唯一标识 |
service
| 一级字段 | 二级字段 | 类型 | 说明 |
|---|---|---|---|
| data.service | object | 服务信息 | |
| data.service | name | string | 服务名称 |
| data.service | version | string | 服务协议版本 |
| data.service | response | string | 服务 Banner 信息 |
| data.service | response_hash | string | Banner 哈希值 |
service.ssh
| 一级字段 | 二级字段 | 类型 | 说明 |
|---|---|---|---|
| data.service.ssh | object | SSH 服务信息 | |
| data.service.ssh | key_types | array | 支持的主机密钥算法 |
| data.service.ssh | server_keys | array | 服务端公钥信息 |
| data.service.ssh | ciphers | array | 支持的加密算法 |
| data.service.ssh | digests | array | 支持的 MAC 算法 |
| data.service.ssh | compression | array | 支持的压缩算法 |
| data.service.ssh | kex | array | 支持的密钥交换算法 |
net
| 一级字段 | 二级字段 | 类型 | 说明 |
|---|---|---|---|
| data.net | object | 网络探测信息 | |
| data.net | service_probe_name | string | 服务探测方式 |
| data.net | router_ip | string | 路由 IP |
| data.net.tcp | window | integer | TCP 窗口大小 |
| data.net.ip | distance | integer | 网络跳数 |
| data.net.ip | initial_ttl | integer | 初始 TTL |
| data.net.ip | ttl | integer | 当前 TTL |
| data.net.ip | tos | integer | TOS 值 |
location
| 一级字段 | 二级字段 | 类型 | 说明 |
|---|---|---|---|
| data.location | object | 地理位置信息 | |
| data.location | country_cn | string | 国家(中文) |
| data.location | country_en | string | 国家(英文) |
| data.location | country_code | string | 国家代码 |
| data.location | province_cn | string | 省份(中文) |
| data.location | province_en | string | 省份(英文) |
| data.location | city_cn | string | 城市(中文) |
| data.location | city_en | string | 城市(英文) |
| data.location | district_cn | string | 区县(中文) |
| data.location | district_en | string | 区县(英文) |
| data.location | street_cn | string | 街道(中文) |
| data.location | street_en | string | 街道(英文) |
| data.location | gps | array | 经纬度坐标 [longitude, latitude] |
| data.location | radius | number | 定位精度(公里) |
| data.location | isp | string | 网络运营商 |
| data.location | owner | string | 资产所有者 |
| data.location | asname | string | ASN 名称 |
| data.location | scene_cn | string | 场景类型(中文) |
| data.location | scene_en | string | 场景类型(英文) |
meta
| 一级字段 | 二级字段 | 类型 | 说明 |
|---|---|---|---|
| meta | object | 元数据 | |
| meta.pagination | object | 分页信息 | |
| meta.pagination | count | integer | 当前页返回数量 |
| meta.pagination | page_index | integer | 当前页码 |
| meta.pagination | page_size | integer | 每页数量 |
| meta.pagination | total | integer | 总记录数 |
请求示例
支持cUrl、Python、Java、PHP、Go语言的请求,示例如下
#!/usr/bin/env bash
set -euo pipefail
APPID="" # apikey
SECRET="" # salt
URL="https://api.f.360.cn/api/v3/search/quake_service"
BODY='{"query":"ip: 193.36.119.50","start":0,"size":1}'
NONCE=$(printf '%08d' $((RANDOM % 100000000)))
TIMESTAMP=$(date +%s)
BODY_MD5=$(printf '%s' "$BODY" | openssl dgst -md5 -hex | awk '{print $NF}')
SIGNATURE=$(printf '%s' "${BODY_MD5}${APPID}${NONCE}${TIMESTAMP}${SECRET}" | openssl dgst -md5 -hex | awk '{print $NF}')
SIGNATURE=${SIGNATURE:16}
curl --location --request POST "$URL" \
--header "Content-Type: application/json" \
--header "X-360-Key: ${APPID}" \
--header "X-360-Nonce: ${NONCE}" \
--header "X-360-Timestamp: ${TIMESTAMP}" \
--header "X-360-Signature: ${SIGNATURE}" \
--data "${BODY}"# -*- coding:utf-8 -*-
import hashlib
import json
import random
import time
import requests
APPID = "" # apikey
SECRET = "" # salt
def make_headers(appid, secret, body=""):
headers = {}
headers["X-360-Key"] = appid
headers["X-360-Nonce"] = str(random.randint(0, 99999999)).zfill(8)
headers["X-360-Timestamp"] = str(int(time.time()))
s = (
hashlib.md5(body.encode("utf8")).hexdigest()
+ headers["X-360-Key"]
+ headers["X-360-Nonce"]
+ headers["X-360-Timestamp"]
+ secret
)
md5 = hashlib.md5(s.encode("utf8")).hexdigest()
headers["X-360-Signature"] = md5[16:]
headers["Content-Type"] = "application/json"
return headers
def do_post(url, body):
res = requests.post(
url,
data=json.dumps(body),
headers=make_headers(APPID, SECRET, json.dumps(body)),
)
print(res.text)
if __name__ == "__main__":
url = "https://api.f.360.cn/api/v3/search/quake_service"
params = {
"query": "ip: 193.36.119.50",
"start": 0,
"size": 1,
}
do_post(url, params)import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.concurrent.ThreadLocalRandom;
public class Demo {
static String md5(String input) throws Exception {
byte[] digest = MessageDigest.getInstance("MD5")
.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b & 0xff));
}
return sb.toString();
}
public static void main(String[] args) throws Exception {
String appid = ""; // apikey
String secret = ""; // salt
String url = "https://api.f.360.cn/api/v3/search/quake_service";
String body = "{\"query\":\"ip: 193.36.119.50\",\"start\":0,\"size\":1}";
String nonce = String.format("%08d", ThreadLocalRandom.current().nextInt(100000000));
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String signature = md5(md5(body) + appid + nonce + timestamp + secret).substring(16);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/json")
.header("X-360-Key", appid)
.header("X-360-Nonce", nonce)
.header("X-360-Timestamp", timestamp)
.header("X-360-Signature", signature)
.POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8))
.build();
HttpClient client = HttpClient.newHttpClient();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
System.out.println(response.body());
}
}<?php
// apikey
$appid = "";
// salt
$secret = "";
$url = 'https://api.f.360.cn/api/v3/search/quake_service';
$body = json_encode(['query' => 'ip: 193.36.119.50', 'start' => 0, 'size' => 1], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$nonce = str_pad((string) random_int(0, 99999999), 8, "0", STR_PAD_LEFT);
$timestamp = (string) time();
$signature = substr(md5(md5($body) . $appid . $nonce . $timestamp . $secret), 16);
$ch = curl_init($url);
$options = [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-360-Key: {$appid}",
"X-360-Nonce: {$nonce}",
"X-360-Timestamp: {$timestamp}",
"X-360-Signature: {$signature}",
],
];
$options[CURLOPT_POST] = true;
$options[CURLOPT_POSTFIELDS] = $body;
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
if ($response === false) {
echo curl_error($ch), PHP_EOL;
} else {
echo $response, PHP_EOL;
}
curl_close($ch);package main
import (
"bytes"
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"math/rand"
"net/http"
"time"
)
func md5Hex(s string) string {
sum := md5.Sum([]byte(s))
return hex.EncodeToString(sum[:])
}
func main() {
appid := "" // apikey
secret := "" // salt
url := "https://api.f.360.cn/api/v3/search/quake_service"
body := []byte(`{"query":"ip: 193.36.119.50","start":0,"size":1}`)
nonce := fmt.Sprintf("%08d", rand.Intn(100000000))
timestamp := fmt.Sprintf("%d", time.Now().Unix())
signature := md5Hex(md5Hex(string(body)) + appid + nonce + timestamp + secret)[16:]
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body))
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-360-Key", appid)
req.Header.Set("X-360-Nonce", nonce)
req.Header.Set("X-360-Timestamp", timestamp)
req.Header.Set("X-360-Signature", signature)
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(string(respBody))
}响应示例
业务正常响应
{
"code": 0,
"message": "Successful.",
"data": [
{
"components": [
{
"product_level": "服务协议层",
"product_type": ["常见网络服务"],
"product_vendor": "openssh",
"product_name_cn": "OpenSSH",
"product_name_en": "openssh",
"id": "5e831df9421aa99e9885e4bc",
"product_catalog": ["网络设备与服务"],
"version": "8.9p1 Ubuntu 3ubuntu0.15"
},
{
"product_level": "操作系统层",
"product_type": ["常见操作系统"],
"product_vendor": "Canonical Ltd.",
"product_name_cn": "Ubuntu",
"product_name_en": "Ubuntu",
"id": "5f4611f03cde3400c2cb2494",
"product_catalog": ["操作系统"],
"version": ""
}
],
"hostname": "",
"org": "EstNOC OY",
"port": 8022,
"service": {
"response": "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.15\r\n",
"response_hash": "67a0dfef592b7e06c2688110ed6fb293",
"name": "ssh",
"version": "2.0",
"ssh": {
"key_types": ["rsa-sha2-512", "rsa-sha2-256", "ecdsa-sha2-nistp256", "ssh-ed25519"],
"server_keys": [
{
"fingerprint": "39:e1:36:cd:27:01:c5:53:7d:85:81:ca:47:09:f7:8d",
"type": "ecdsa-sha2-nistp256",
"key": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAy..."
}
],
"ciphers": ["chacha20-poly1305@openssh.com", "aes128-ctr", "aes256-ctr"],
"digests": ["umac-64-etm@openssh.com", "hmac-sha2-256-etm@openssh.com"],
"compression": ["none", "zlib@openssh.com"],
"kex": ["curve25519-sha256", "ecdh-sha2-nistp256", "diffie-hellman-group-exchange-sha256"]
},
"net": {
"service_probe_name": "NULL",
"tcp": {"window": 64240},
"router_ip": "63.223.29.246",
"ip": {"distance": 17, "initial_ttl": 64, "tos": 0, "ttl": 47}
}
},
"ip": "193.36.119.50",
"os_name": "Ubuntu",
"location": {
"province_cn": "香港",
"isp": "EstNOC OY",
"province_en": "Hongkong",
"country_en": "China",
"country_cn": "中国",
"city_cn": "香港",
"city_en": "Hongkong",
"district_cn": "",
"district_en": "",
"street_cn": "",
"street_en": "",
"gps": [114.184921, 22.350617],
"country_code": "CN",
"asname": "EstNOC-GLOBAL",
"scene_cn": "数据中心",
"scene_en": "Hosting",
"radius": 40.0088,
"owner": ""
},
"is_ipv6": false,
"transport": "tcp",
"time": "2026-07-03T05:08:19.848Z",
"asn": 206804,
"id": "193.36.119.50_8022_tcp"
}
],
"meta": {
"pagination": {
"count": 1,
"page_index": 1,
"page_size": 1,
"total": 4
}
}
}业务异常响应
{
"code": "t6003",
"message": "查询请求出错,请检查查询语句,或者与管理员联系",
"data": {},
"meta": {}
}{
"code": "GC00105",
"src": "apiholder",
"msg": "appid over field"
}{
"code": "q2001",
"message": "用户当前积分不足以完成本次查询操作。",
"data": {},
"meta": {}
}Quake 检索类接口业务错误码(HTTP 状态码均为 200,通过 code 字段区分):
| code | message | 说明 |
|---|---|---|
| 0 | Successful. | 查询成功 |
| q2001 | 用户当前积分不足以完成本次查询操作。 | 账号积分不足,无法完成本次检索,需充值或联系管理员提升积分 |
| q2001 | 筛选字段传参错误: [xxx], 请调用可筛选字段接口查看可筛选字段 | include/exclude 传入了不在白名单内的字段,先调用对应索引的可筛选字段接口获取白名单 |
| q3005 | 调用API过于频繁 | 触发接口调用频率限制,降低调用频率(建议两次检索间隔15秒以上)后重试 |