# 在线API

# 一. 交付方式

邮箱画像部署在云端,通过HTTPS的RESTful接口查询使用。每个注册客户会分配一个snuser和snkey。(在控制台-配置 (opens new window)页面可以查看)

邮箱画像服务端对查询请求实行IP白名单机制,只有白名单中IP地址允许查询。支持添加IP段“127.0.0.1/16 ”。(配置管理地址控制台-配置 (opens new window)

# 二. 接口说明

# 2.1请求详情

# 2.1.1 请求参数说明

请求参数 说明
请求方法 POST
请求地址 https://black-mailbox.yazx.com/v1/api/check/full/mailbox/sha1
并发 200
QPS 1000
延时情况 100ms以内

# 2.1.2 请求说明

请求头

参数名称 参数类型 是否必传 参数说明
x-snuser string 用户识别id(配置管理页面中可以查看)

请求体

传参方式 参数类型 是否必传 参数说明
请求体 string aes加密data后的请求数据内容,直接传入请求体

data内容如下

参数名称 参数类型 是否必传 参数说明
email_prefix_sha1 string

邮箱前缀(sha1加密)

示例:hunter@yazx.com,取hunter的sha1

结果: 6e2f9e6111e77edd0c446ea7a84e25323d137a61

email_suffix string

邮箱后缀

示例:hunter@yazx.com,取yazx.com

# 2.2 返回详情

# 2.2.1 返回参数说明

参数名称 参数类型 是否必传 参数说明
status int 状态码
data json_object 威胁猎人识别的数据内容
errmsg string 错误详情

data内容如下

参数名称 参数类型 是否必传 参数说明
email_prefix_sha1 string 查询邮箱的前缀(sha1加密)
email_suffix string 查询邮箱的后缀
type integer

邮箱类型

0 未知邮箱:未能识别该邮箱类型

1 公共邮箱:指在邮箱门户网站注册的邮箱,使用者多为正常用户。

2 临时邮箱:指无需注册登录且只有几分钟到几小时不等有效性即可收发邮件的邮箱,使用者多为恶意用户。

3 企业邮箱:指通过企业认证的邮箱 ,使用者为企业在职员工。

4 校园邮箱:指通过校园认证的邮箱 ,使用者为学校的校园师生。

5 无效邮箱:指不能收发邮件的邮箱。

6 自建邮箱:指私人搭建邮件服务器,所有者可以随意增加或更换邮箱账号。

ban integer

0 未命中邮箱黑名单库

1 命中邮箱黑名单库

risk integer

0 无风险,该邮箱未命中邮箱黑名单库且邮箱类型非临时邮箱

1 有风险,该邮箱命中邮箱黑名单库或邮箱类型为临时邮箱

type_update_time datetime

该邮箱类型最后一次更新的时间

示例:2018-01-06T12:21:50+00:00

ban_update_time datetime

该邮箱黑名单最后一次更新的时间

示例:2018-01-06T12:21:50+00:00

# 2.3 错误码

2.3.1当响应的status_code为422时,响应体格式如下:

参数名称 参数类型 是否必返 参数说明
status int 状态码
errmsg string 错误描述
detail String 错误详情

status和errmsg示例如下:

status errmsg
10001 bad_data

2.3.2当响应的status_code为400时,响应体格式如下:

参数名称 参数类型 是否必返 参数说明
status int 状态码
errmsg string 错误描述

status和errmsg示例如下:

status errmsg
11000 X-Snuser header is empty
11001 unknown value in X-Snuser header
11002 Service not valid
11003 request not allow to access from this client by service(请添加ip白名单)

2.3.3当响应的status_code为404时,响应体格式如下:

参数名称 参数类型 是否必返 参数说明
status int 状态码
errmsg string 错误描述

status和errmsg示例如下:

status errmsg
9999 Not Found

# 三. 使用建议

3.1 临时邮箱:type返回值为“2”

由于临时邮箱具有一次性、用完即毁的特点,决定了使用该类邮箱的用户绝非对业务有价值的用户。 同时由于临时邮箱的创建成本极其低廉,黑灰产很容易就能批量创建大量临时邮箱账号从而进行垃圾注册 。

3.2 命中邮箱黑名单:ban返回“1”

该类邮箱是我们明确发现在被黑产持有,而非被自然人所持有的电子邮箱,产生的误判概率极低。

综上所述,对risk返回值为“1”的用户可以考虑在单一策略下直接拦截。对risk返回值为“0”的用户可以使用邮箱类型补充用户的画像。

# 四. 代码示例

# python

# -*- encoding: utf-8 -*-

"""
python 版本
# 3.7及以上

# 依赖包
pip install pycryptodome
pip install requests

"""

import requests
import json
from Crypto.Cipher import AES
from Crypto import Random

SNUSER: str = "test"
SNKEY: str = "test"


def aes_encrypt_seg(content: bytes) -> bytes:
    remainder: int = len(content) % AES.block_size
    padded_value: bytes
    if remainder:
        padded_value = content + b'' * (AES.block_size - remainder)
    else:
        padded_value = content
    # 随机 16 字节的密钥
    iv: bytes = Random.new().read(AES.block_size)
    # CFB 模式
    cipher = AES.new(bytes(SNKEY, encoding='utf8'), AES.MODE_CFB, iv, segment_size=128)
    value: bytes = cipher.encrypt(padded_value[:len(content)])
    ciphertext: bytes = iv + value
    return ciphertext


def check_full_mailbox_sha1():
    POST_URL: str = "https://black-mailbox.yazx.com/v1/api/check/full/mailbox/sha1"
    # 通过请求头告诉我们snuser
    headers: dict = {'X-Snuser': SNUSER, "content-type": "text/plain"}
    # 通过snkey加密请求体
    payload: dict = {
        "email_prefix_sha1": "6058a57e3783ec526dbabc461bdc9bce718385bb",
        "email_suffix": "truthfinderlogin.com",
    }
    data: bytes = aes_encrypt_seg(json.dumps(payload).encode())
    r = requests.post(url=POST_URL, headers=headers, data=data)
    rjson: dict = r.json()
    if rjson["status"] != 200:
        print(rjson['errmsg'])
    else:
        print(rjson['data'])


if __name__ == "__main__":
check_full_mailbox_sha1()

# java


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import okhttp3.*;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;

public class EmailMain {

    private static String SNUSER = "****";
    private static String SNKEY = "****";
    private static final String URL = "https://black-mailbox.yazx.com/v1/api/check/full/mailbox/sha1";

    private static byte[] encryptAES(String data) {
        try {
            SecretKeySpec key = new SecretKeySpec(SNKEY.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
            byte[] databyteContent = data.getBytes();

            byte[] ivbytes = new byte[16];
            Random rand = new Random();
            rand.nextBytes(ivbytes);

            IvParameterSpec ivSpec = new IvParameterSpec(ivbytes);

            cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
            byte[] result = cipher.doFinal(databyteContent);

            byte[] finalbytes = new byte[ivbytes.length + result.length];
            System.arraycopy(ivbytes, 0, finalbytes, 0, ivbytes.length);
            System.arraycopy(result, 0, finalbytes, ivbytes.length, result.length);

            return finalbytes;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static void checkEmail(Map<String, Object> payload) throws IOException {

        JSONObject jsObj = new JSONObject(payload);
        System.out.println("request plain =>" + jsObj);

        byte[] content = encryptAES(jsObj.toString());

        httpPostText(content);

        OkHttpClient client = new OkHttpClient();

        MediaType mediaType = MediaType.parse("text/plain");
        System.out.println("request:"+ new String(content));
        RequestBody body = RequestBody.create(mediaType, content);
        Request request = new Request.Builder()
                .url(URL)
                .post(body)
                .addHeader("X-Snuser", SNUSER)
                .build();

        try (Response response = client.newCall(request).execute()) {
            String jsonData = response.body().string();
            System.out.println("response plain =>" + jsonData);

            JSONObject object = JSON.parseObject(jsonData);
            String encryptData = object.getString("data");
            System.out.println("result => success: " + encryptData);
        } catch (Exception e) {
            System.out.println("result => failed");
            e.printStackTrace();
        }
    }

    public static void httpPostText(byte[] payload) throws IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();

        HttpPost httpPost = new HttpPost(URL);
        httpPost.setHeader("Content-Type", "text/plain");
        httpPost.setHeader("X-Snuser", SNUSER);
        ByteArrayEntity entity1 = new ByteArrayEntity(payload);
        entity1.setContentType("text/plain");
        httpPost.setEntity(entity1);
        CloseableHttpResponse execute = httpClient.execute(httpPost);
        if (execute != null && execute.getStatusLine().getStatusCode() == 200) {
            HttpEntity entity = execute.getEntity();
            String result = new BufferedReader(new InputStreamReader(entity.getContent())).lines().collect(Collectors.joining());
            System.out.println("result => success:" + result);
        } else {
            System.out.println("result => faild:" + execute.getStatusLine());
        }
    }

    public static void main(String[] args) throws IOException {
        HashMap<String, Object> map = new HashMap<>();

        map.put("email_suffix", "truthfinderlogin.com");
        map.put("email_prefix_sha1", "6058a57e3783ec526dbabc461bdc9bce718385bb");
        checkEmail(map);
    }
}

Last Updated: 2/22/2023, 11:55:14 AM