Quake服务索引聚合统计可聚合字段列表
接口说明
返回 quake_service(服务维度网络测绘资产)索引中支持作为聚合维度的字段名列表。该列表即 POST /api/v3/aggregation/quake_service 聚合接口 aggregation_list 参数的合法取值范围。 quake_service 是数据量最大的核心索引,可聚合维度最丰富,涵盖服务指纹、HTTP 特征、应用组件、地理位置,以及一批去重基数字段(unique_*)。调用方在执行服务聚合统计前,应先调用本接口拿到可聚合字段清单,再据此构造聚合请求。
请求地址
https://api.f.360.cn/api/v3/aggregation/quake_service/aggregation_list
请求方法
GET
请求参数
| 参数名 | 类型 | 位置 | 是否必填 | 示例值 | 说明 |
|---|---|---|---|---|---|
| — | — | — | — | — | 本接口无请求参数 |
响应参数
| 名称 | 类型 | 示例值 | 描述 |
|---|---|---|---|
| code | Integer | String | 0 | 响应状态码,0 为正常,其他为异常 |
| message | String | Successful. | 响应状态信息 |
| data | Array(String) | ["ip","port",...] | 可聚合字段名称列表 |
| meta | Object | {} | 元信息 |
data 字段说明
| 字段标识 | 说明 |
|---|---|
ip | IP 地址 |
port | 端口号 |
service | 服务名称 |
product | 产品名称 |
os | 操作系统 |
asn | ASN 编号 |
org | 组织名称 |
title | HTTP 页面标题 |
server | HTTP Server 头 |
app | 应用名称 |
catalog | 产品分类 |
type | 产品类型 |
level | 产品层级 |
vendor | 产品厂商 |
isp | 运营商 |
status_code | HTTP 状态码 |
powered_by | Powered-By 头 |
meta_keywords | 页面 meta keywords |
page_type | 页面类型 |
icp | ICP 备案号 |
app_and_version | 应用及版本 |
service_and_version | 服务及版本 |
unique_ip | 去重 IP 数 |
unique_domain | 去重域名数 |
unique_port | 去重端口数 |
unique_product | 去重产品数 |
unique_asn | 去重 ASN 数 |
unique_org | 去重组织数 |
unique_isp | 去重运营商数 |
unique_title | 去重标题数 |
unique_server | 去重 Server 数 |
unique_app | 去重应用数 |
unique_catalog | 去重产品分类数 |
unique_type | 去重产品类型数 |
unique_level | 去重产品层级数 |
unique_vendor | 去重厂商数 |
unique_country | 去重国家数 |
unique_province | 去重省份数 |
unique_city | 去重城市数 |
province | 省份(英文) |
province_cn | 省份(中文) |
country | 国家(英文) |
country_cn | 国家(中文) |
country_code | 国家代码 |
city | 城市(英文) |
city_cn | 城市(中文) |
district | 区县(英文) |
district_cn | 区县(中文) |
province_of_china | 中国省份 |
请求示例
支持cUrl、Python、Java、PHP、Go语言的请求,示例如下
#!/usr/bin/env bash
set -euo pipefail
APPID="" # apikey
SECRET="" # salt
URL="https://api.f.360.cn/api/v3/aggregation/quake_service/aggregation_list"
BODY=""
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 GET "$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}"# -*- coding:utf-8 -*-
import hashlib
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_get(url, params):
res = requests.get(url, headers=make_headers(APPID, SECRET), params=params)
print(res.text)
if __name__ == "__main__":
url = "https://api.f.360.cn/api/v3/aggregation/quake_service/aggregation_list"
params = {}
do_get(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/aggregation/quake_service/aggregation_list";
String body = "";
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)
.GET()
.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/aggregation/quake_service/aggregation_list';
$body = "";
$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_HTTPGET] = true;
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 (
"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/aggregation/quake_service/aggregation_list"
body := []byte("")
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.MethodGet, url, nil)
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": [
"ip", "port", "service", "product", "os", "asn", "org",
"title", "server", "app", "catalog", "type", "level", "vendor",
"isp", "status_code", "powered_by", "meta_keywords", "page_type",
"icp", "app_and_version", "service_and_version",
"unique_ip", "unique_domain", "unique_port", "unique_product",
"unique_asn", "unique_org", "unique_isp", "unique_title",
"unique_server", "unique_app", "unique_catalog", "unique_type",
"unique_level", "unique_vendor", "unique_country", "unique_province",
"unique_city", "province", "province_cn", "country", "country_cn",
"country_code", "city", "city_cn", "district", "district_cn",
"province_of_china"
],
"meta": {}
}业务异常响应
{
"code": "q3005",
"message": "调用API过于频繁",
"data": {},
"meta": {}
}{
"code": "GC00105",
"src": "apiholder",
"msg": "appid over field"
}