GitHub avatar

Fox's Blog

✨ AI Generated Article

I Built an Ultra-Realistic Express Honeypot

328 fake endpoints with on-the-fly generated responses, header spoofing, bot traffic logging -- a deep dive into an Express honeypot middleware designed to fool scanners.

TL;DR

I created an Express middleware that exposes 328 fake endpoints with a choice of two variants: default (concise but believable responses) and complete (very detailed responses). Every response is generated on the fly with fresh timestamps and request IDs, which makes each response unique. There's no mock file on disk. The middleware can also log traffic to a text file and expose unhandled routes on /newBotsRoute to analyze what bots are looking for.

It's way more than just returning 404s. I designed this engineering piece to look like a real server, so scanners waste their time on it. Let me show you how it works.

The problem

Security bots, scrapers, and malicious actors constantly scan web servers for common routes:

  • .env, wp-config.php, admin/, phpinfo.php, etc.
  • API endpoints like /api/version, /api/config
  • Config files: dump.sql, .git/config, docker-compose.yml
  • Banking phishing pages: /lander/sberbank*, /index_sber.php

I saw this limit on my own servers: returning a 404 or 403 immediately tells the scanner the route doesn't exist, so it moves on. I chose the opposite with this honeypot: make the scanner believe everything exists, so it wastes time on fake responses while your real server stays protected behind your known routes.

The architecture

1. A classifier, not a lookup table

The centerpiece is a classify() function in src/services/mockupGenerator.ts that takes an endpoint path and returns a response generator. I built it as a rule cascade:

  1. Exact matches: the most common endpoints get dedicated generators
  2. Pattern matches: regexes on the path to detect patterns like id_rsa, wp-admin, sitemap.xml
  3. Smart classification: paths like /262LBNFp (6+ random chars) are classified as "C2 heartbeats"
  4. Catch-all rules: API paths, .php, .html, .js, .json, and .asmx files get generic generators
  5. Final catchall: genCatchall() produces a generic JSON response with metadata

I like this approach because responses stay fresh on every request: no static files, no caching, every response carries its own timestamp as if it came from a real server.

2. The dual-variant system

Each generator supports two variants:

type Variant = "default" | "complete";
  • default: concise but believable responses. Example for .env:

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete: very detailed responses. Example for .env:

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

The complete variant also includes full HTML pages with styled CSS: admin pages, WordPress login screens, banking phishing pages in Russian, etc. I find it formidable when you really want to keep a scanner busy.

3. On-the-fly generation

I use Date.now() and Date().toISOString() to produce unique timestamps and request IDs on every call. Here's the enrichment function:

function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

For JSON responses, the middleware tries to parse and enrich with those fields. Non-JSON responses are returned as is.

4. The header-spoofing middleware

I added a dedicated middleware that sets realistic HTTP headers based on the requested file extension:

res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// Realistic X-Powered-By based on file extension
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

That makes responses way more believable: a .php endpoint with X-Powered-By: PHP/8.1.12 and Server: nginx/1.24.0 looks exactly like a real WordPress server. I care about that detail because the header is often what gives away a fake server.

5. The PHP Spoofer

The phpSpoofer remains my favorite component. If a request comes in for a .php file and the host is localhost, the middleware proxies the request to your local PHP server, which processes it and returns a real PHP page. If the host is remote, it returns a 404 to block any SSRF attempt.

const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // return the real PHP response
  } else {
    res.status(404).send("404 Not Found");
  }
};

That way, when you're running WordPress locally, the honeypot serves real WordPress pages to bots: the maximum realism in my experience.

6. Traffic logging and analysis

I separated two responsibilities: the TrafficService logs every request in a JSON-lines file. The UnhandledRoutesService then analyzes those logs to identify paths bots request that the honeypot doesn't cover yet.

app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

I love that feedback loop: the longer you let the honeypot run, the more you learn what bots are looking for, and you can add those paths to your known endpoints.

7. Known endpoints and exclusion

The middleware accepts lists of knownPaths, knownPatterns, knownApiPaths, and knownApiPatterns. Any route matching them passes through the middleware to your real app. I find that filter essential: your real endpoints stay intact, the honeypot never intercepts them.

const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

The 328 endpoints in detail

Here's how they're classified (excerpt from the classifier):

Category Example endpoints
Credential leaks .env, secrets.json, aws/credentials, etc/shadow
SSH keys .ssh/id_rsa, .ssh/id_ed25519
Database configs config/database, wp-config.php, docker-compose.yml
Admin panels /admin, /wp-admin, /manage/account/login
API responses /api/version, /api/config, .do, .ashx
Banking phishing /lander/sber*, /index_sber.php
C2 heartbeats Random 6+ char paths (/262LBNFp)
Stock/Trading /stock/mzhishu, /kline/1m/1, /m/allticker/1
Games/Casino /proxy/games, /Ctrls/GetSysCoin, /room/getRoomBangFans
Config files config.json, config.yml, sitemap.xml, ads.txt
Landing pages /about, /contact, /products, /blog

Special codes and endpoints

I added specialized generators for particular cases:

  • genLanding(): banking phishing pages in Russian for СберБанк, Тинькофф, Газпромбанк
  • genC2Heartbeat(): realistic C2 heartbeat response with agent_id, group, version
  • genScannerPath(): traps for specific scanners (nmap NICE ports, Hazelcast, CGI)
  • genChineseApi(): Chinese API responses with msg: "成功" (success)
  • genMerchant(): merchant profiles with balance, fee_rate, tier

How to use it

Install

npm install express-middleware-honeypot

Basic usage with auto-registration

const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

With app.use() (single catch-all middleware)

const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// Your real routes first
app.get("/", (req, res) => res.send("Home"));

// Then the catch-all honeypot
const instance = createHoneypot({ /* options */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

Complete vs. default responses

// Detailed responses with full HTML pages
const instance = createHoneypot({ isCompleteResponses: true });

Traffic logging

const instance = createHoneypot({ logTraffic: true });
// Check the traffic.txt file to see what bots request

Conclusion

This Express honeypot middleware goes way beyond a simple decoy. With 328 endpoints, on-the-fly generation, dynamic header spoofing, traffic logging, and even a PHP proxy for real WordPress responses, I designed it to stay as realistic as possible.

Scanners waste time on fake admin pages, believable .env files, and banking phishing pages while your real app stays protected. I particularly like the dual-variant system and the smart classification: every response stays fresh and matched to the requested path.

The source code is on GitHub: https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

J'ai construit un honeypot Express ultra-réaliste

328 faux endpoints avec réponses générées à la volée, spoofing d'en-têtes, logging du trafic bot -- plongée au cœur d'un middleware honeypot Express conçu pour leurrer les scanners.

TL;DR

J'ai créé un middleware Express qui expose 328 endpoints factices au choix parmi deux variantes : default (réponses sobres mais crédibles) et complete (réponses très détaillées). Chaque réponse est générée à la volée avec des timestamps et des identifiants de requête frais, ce qui rend chaque réponse unique. Il n'y a aucun fichier mock sur le disque. Le middleware peut également journaliser le trafic dans un fichier texte et exposer les routes non gérées sur /newBotsRoute pour analyser ce que les bots cherchent.

C'est bien plus qu'un simple renvoi de 404. J'ai conçu cette pièce d'ingénierie pour ressembler à un vrai serveur, pour que les scanners gaspillent leur temps dessus. Je te montre comment ça marche.

Le problème

Les bots de sécurité, les scrapers et les acteurs malveillants scannent constamment les serveurs web à la recherche de routes courantes :

  • .env, wp-config.php, admin/, phpinfo.php, etc.
  • endpoints d'API comme /api/version, /api/config
  • fichiers de configuration : dump.sql, .git/config, docker-compose.yml
  • pages de phishing bancaire : /lander/sberbank*, /index_sber.php

J'ai vu cette limite sur mes propres serveurs : renvoyer un 404 ou un 403 indique tout de suite au scanner que la route n'existe pas, donc il passe à la suivante. J'ai choisi l'inverse avec ce honeypot : faire croire au scanner que tout existe, pour lui faire perdre du temps sur de fausses réponses pendant que ton vrai serveur reste protégé derrière tes routes connues.

L'architecture

1. Un classifieur, pas une lookup table

La pièce maîtresse est une fonction classify() dans src/services/mockupGenerator.ts qui prend un chemin d'endpoint et retourne un générateur de réponse. Je l'ai construite comme une cascade de règles :

  1. Correspondances exactes : les endpoints les plus courants ont des générateurs dédiés
  2. Correspondances par motif : regex sur le chemin pour détecter des motifs comme id_rsa, wp-admin, sitemap.xml
  3. Classification intelligente : chemins comme /262LBNFp (6+ caractères aléatoires) sont classifiés comme "battements de cœur C2"
  4. Règles fourre-tout : les chemins d'API, les fichiers .php, .html, .js, .json et .asmx ont des générateurs génériques
  5. Catchall final : genCatchall() produit une réponse JSON générique avec des métadonnées

J'aime cette approche parce que les réponses restent fraîches à chaque requête : aucun fichier statique, aucune mise en cache, chaque réponse porte son propre timestamp comme si elle venait d'un vrai serveur.

2. Le système de double variante

Chaque générateur supporte deux variantes :

type Variant = "default" | "complete";
  • default : réponses concises mais crédibles. Exemple pour .env :

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete : réponses très détaillées. Exemple pour .env :

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

La variante complete inclut également des pages HTML complètes avec CSS stylisé : pages d'administration, écrans de connexion WordPress, pages de phishing bancaire en russe, etc. Je la trouve redoutable quand tu veux vraiment occuper un scanner.

3. Génération à la volée

J'utilise Date.now() et Date().toISOString() pour produire des timestamps et des identifiants de requête uniques à chaque appel. Voici la fonction d'enrichissement :

function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

Pour les réponses JSON, le middleware tente de parser et d'enrichir avec ces champs. Les réponses non-JSON sont renvoyées telles quelles.

4. Le middleware de spooofing d'en-têtes

J'ai ajouté un middleware dédié qui définit des en-têtes HTTP réalistes en fonction de l'extension du fichier demandé :

res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// X-Powered-By réaliste basé sur l'extension du fichier
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

Cela rend les réponses beaucoup plus crédibles : un endpoint .php avec X-Powered-By: PHP/8.1.12 et Server: nginx/1.24.0 ressemble exactement à un vrai serveur WordPress. Je tiens à ce détail parce que c'est souvent l'en-tête qui trahit un faux serveur.

5. Le PHP Spoofer

Le phpSpoofer reste mon composant préféré. Si une requête arrive pour un fichier .php et que l'hôte est localhost, le middleware proxifie la requête vers ton serveur PHP local, qui traite et renvoie une vraie page PHP. Si l'hôte est distant, il renvoie un 404 pour bloquer toute tentative SSRF.

const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // renvoie la réponse PHP réelle
  } else {
    res.status(404).send("404 Not Found");
  }
};

Cela permet, quand tu fais tourner WordPress en local, d'avoir le honeypot qui sert de vraies pages WordPress aux bots : le maximum de réalisme selon mon expérience.

6. Journalisation et analyse du trafic

J'ai séparé deux responsabilités : le TrafficService journalise chaque requête dans un fichier JSON-lines. Le UnhandledRoutesService analyse ensuite ces logs pour identifier les chemins demandés par les bots qui restent à couvrir dans le honeypot.

app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

J'adore cette boucle de rétroaction : plus tu laisses tourner le honeypot, plus tu apprends ce que les bots recherchent, et tu peux ajouter ces chemins à tes endpoints connus.

7. Endpoints connus et exclusion

Le middleware accepte des listes de knownPaths, knownPatterns, knownApiPaths et knownApiPatterns. Toute route qui correspond à celle-ci traverse le middleware vers ta vraie application. Je trouve ce filtre indispensable : tes vrais endpoints restent intacts, le honeypot ne les intercepte jamais.

const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

Les 328 endpoints en détail

Voici comment ils sont classifiés (extrait du classifieur) :

Catégorie Exemples d'endpoints
Fuites de credentials .env, secrets.json, aws/credentials, etc/shadow
Clés SSH .ssh/id_rsa, .ssh/id_ed25519
Configs de base de données config/database, wp-config.php, docker-compose.yml
Panneaux d'administration /admin, /wp-admin, /manage/account/login
Réponses API /api/version, /api/config, .do, .ashx
Phishing bancaire /lander/sber*, /index_sber.php
Battements de cœur C2 Chemins aléatoires de 6+ caractères (/262LBNFp)
Actions/Bourse /stock/mzhishu, /kline/1m/1, /m/allticker/1
Jeux/Casino /proxy/games, /Ctrls/GetSysCoin, /room/getRoomBangFans
Fichiers de configuration config.json, config.yml, sitemap.xml, ads.txt
Pages d'atterrissage /about, /contact, /products, /blog

Codes et endpoints spéciaux

J'ai ajouté des générateurs spécialisés pour des cas particuliers :

  • genLanding() : pages de phishing bancaire en russe pour СберБанк, Тинькофф, Газпромбанк
  • genC2Heartbeat() : réponse de battement de cœur C2 réaliste avec agent_id, group, version
  • genScannerPath() : pièges pour scanners spécifiques (ports nmap NICE, Hazelcast, CGI)
  • genChineseApi() : réponses d'API chinoises avec msg: "成功" (succès)
  • genMerchant() : profils de commerçants avec balance, fee_rate, tier

Comment l'utiliser

Installation

npm install express-middleware-honeypot

Usage de base avec auto-enregistrement

const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

Avec app.use() (middleware fourre-tout unique)

const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// Vos vraies routes en premier
app.get("/", (req, res) => res.send("Home"));

// Ensuite le honeypot fourre-tout
const instance = createHoneypot({ /* options */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

Réponses complètes vs. par défaut

// Réponses détaillées avec pages HTML complètes
const instance = createHoneypot({ isCompleteResponses: true });

Journalisation du trafic

const instance = createHoneypot({ logTraffic: true });
// Consultez le fichier traffic.txt pour voir ce que les bots demandent

Conclusion

Ce middleware honeypot Express va bien au-delà d'un simple leurre. Avec 328 endpoints, une génération à la volée, un spoofing d'en-têtes dynamique, une journalisation du trafic et même un proxy PHP pour de vraies réponses WordPress, je l'ai conçu pour rester aussi réaliste que possible.

Les scanners perdent du temps sur des pages d'administration factices, des fichiers .env crédibles et des pages de phishing bancaire pendant que ta vraie application reste protégée. J'apprécie particulièrement le système de double variante et la classification intelligente : chaque réponse reste fraîche et adaptée au chemin demandé.

Le code source est disponible sur GitHub : https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

我写了个超逼真的 Express 蜜罐

328 个假 endpoint,响应实时生成,头部伪造,机器人流量日志,深入一个为溜扫描器设计的 Express 蜜罐中间件。

TL;DR

我写了个 Express 中间件,暴露 328 个假 endpoint,两种变体可选:default(简洁可信)和 complete(超详细)。每个响应都实时生成,带新鲜的时间戳和请求 ID,所以每次响应都唯一。磁盘上没有任何 mock 文件。中间件还能把流量记日志到文本文件,并在 /newBotsRoute 上暴露未覆盖的路由,分析机器人在找什么。

这远不止返回个 404。 我把它做成像真服务器的样子,让扫描器在上面浪费时间。下面拆给你看。

问题

安全机器人、爬虫和坏人一直在扫 web 服务器的常见路径:

  • .env、wp-config.php、admin/、phpinfo.php 等
  • API endpoint 如 /api/version、/api/config
  • 配置文件:dump.sql、.git/config、docker-compose.yml
  • 银行钓鱼页:/lander/sberbank*、/index_sber.php

我自己服务器上见过这个局限:直接回 404 或 403,扫描器立刻知道这路不存在,换下一条。这个蜜罐反着来:让扫描器以为条条路都存在,在假响应上烧时间,你的真服务器躲在已知路由后面安然无恙。

架构

1. 分类器,不是查表

核心是 src/services/mockupGenerator.ts 里的 classify() 函数,输入 endpoint 路径,输出响应生成器。我做成级联规则:

  1. 精确匹配:最常见的 endpoint 有专用生成器
  2. 模式匹配:路径正则,识别 id_rsa、wp-admin、sitemap.xml 这类模式
  3. 智能分类:像 /262LBNFp(6 个以上随机字符)归为"C2 心跳"
  4. 兜底规则:API 路径、.php、.html、.js、.json、.asmx 文件有通用生成器
  5. 最终 catchall:genCatchall() 生成带元数据的通用 JSON 响应

我喜欢这个设计,因为响应每次请求都新鲜:没有静态文件,没有缓存,每个响应自带时间戳,像刚从真服务器出来。

2. 双变体系统

每个生成器支持两种变体:

type Variant = "default" | "complete";
  • default:简洁但可信。比如 .env:

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete:超详细。比如 .env:

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

complete 变体还带完整 HTML 页面和 CSS 样式:管理后台、WordPress 登录页、俄语银行钓鱼页等。想真正拖住扫描器时很好用。

3. 实时生成

我用 Date.now() 和 Date().toISOString() 给每次调用产新鲜时间戳和请求 ID。丰富函数长这样:

function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

JSON 响应中间件会尝试解析并加上这些字段。非 JSON 原样返回。

4. 头部伪造中间件

我加了个专用中间件,按请求文件扩展名设置逼真的 HTTP 头:

res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// 按文件扩展名的逼真 X-Powered-By
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

响应可信度高很多:.php endpoint 配 X-Powered-By: PHP/8.1.12 加 Server: nginx/1.24.0,就是一台真 WordPress。我抠这个细节,因为头部最容易出卖假服务器。

5. PHP Spoofer

phpSpoofer 还是我最喜欢的组件。请求 .php 文件且 host 是 localhost 时,中间件代理到你本地 PHP 服务器,处理并返回真 PHP 页面。host 是远程就回 404,挡掉一切 SSRF 尝试。

const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // 返回真实 PHP 响应
  } else {
    res.status(404).send("404 Not Found");
  }
};

本地跑 WordPress 时,蜜罐给机器人喂真 WordPress 页面:按我的经验逼真度拉满。

6. 流量日志与分析

我把两个职责分开:TrafficService 把每次请求记进 JSON-lines 文件。UnhandledRoutesService 再分析这些日志,找出机器人要的、蜜罐还没覆盖的路径。

app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

我喜欢这个反馈环:蜜罐跑越久,你越知道机器人在找什么,再把这些路径加进已知 endpoint。

7. 已知 endpoint 与放行

中间件接受 knownPaths、knownPatterns、knownApiPaths、knownApiPatterns 列表。匹配的路由穿过中间件,直达你的真应用。我觉得这个过滤必不可少:真 endpoint 原样保留,蜜罐一个不拦。

const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

328 个 endpoint 明细

分类器里是这么分的(摘录):

类别 endpoint 示例
凭证泄露 .env、secrets.json、aws/credentials、etc/shadow
SSH 密钥 .ssh/id_rsa、.ssh/id_ed25519
数据库配置 config/database、wp-config.php、docker-compose.yml
管理后台 /admin、/wp-admin、/manage/account/login
API 响应 /api/version、/api/config、.do、.ashx
银行钓鱼 /lander/sber*、/index_sber.php
C2 心跳 6 字符以上随机路径(/262LBNFp)
行情/股票 /stock/mzhishu、/kline/1m/1、/m/allticker/1
游戏/赌场 /proxy/games、/Ctrls/GetSysCoin、/room/getRoomBangFans
配置文件 config.json、config.yml、sitemap.xml、ads.txt
落地页 /about、/contact、/products、/blog

特殊代码与 endpoint

针对特殊情况我写了专用生成器:

  • genLanding():俄语银行钓鱼页,СберБанк、Тинькофф、Газпромбанк
  • genC2Heartbeat():逼真 C2 心跳响应,带 agent_id、group、version
  • genScannerPath():扫描器陷阱(nmap 端口、Hazelcast、CGI)
  • genChineseApi():中文 API 响应,带 msg: "成功"
  • genMerchant():商户画像,带 balance、fee_rate、tier

怎么用

安装

npm install express-middleware-honeypot

基础用法,自动注册

const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: false,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

用 app.use()(单个兜底中间件)

const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// 真路由先行
app.get("/", (req, res) => res.send("Home"));

// 蜜罐兜底
const instance = createHoneypot({ /* options */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

完整响应 vs 默认

// 详细响应,带完整 HTML 页面
const instance = createHoneypot({ isCompleteResponses: true });

流量日志

const instance = createHoneypot({ logTraffic: true });
// 看 traffic.txt 就知道机器人在要什么

结语

这个 Express 蜜罐中间件远不止一个诱饵。328 个 endpoint、实时生成、动态头部伪造、流量日志,还有给真 WordPress 响应的 PHP 代理,我按尽可能逼真来做的。

扫描器在假管理后台、可信 .env、银行钓鱼页上烧时间,你的真应用安然无恙。我最喜欢双变体系统和智能分类:每个响应都新鲜,且正好长在请求路径该长的样子。

源码在 GitHub:https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

超リアルなExpressハニーポットを作った

その場生成応答付き328偽エンドポイント、ヘッダースプーフィング、ボットトラフィックのロギング。スキャナーを騙すために設計したExpressハニーポットミドルウェアの中身。

TL;DR

2変種から選べる328の偽エンドポイントを出すExpressミドルウェアを作った。default(簡潔だが信用できる応答)とcomplete(とても詳細な応答)だ。各応答は新鮮なタイムスタンプとリクエストID付きでその場生成される。各応答が一意になる。ディスクにモックファイルはない。ミドルウェアはトラフィックをテキストファイルに記録もできて、未処理ルートを/newBotsRouteに出してボットが何を探してるか分析できる。

ただ404を返すのとは段違いだ。 本物のサーバーに見えるよう設計した工学的部品で、スキャナーに時間を無駄遣いさせる。仕組みを見せるよ。

問題

セキュリティボットもスクレイパーも悪性アクターも、よくあるルートを探してウェブサーバーを常にスキャンしてる。

  • .env、wp-config.php、admin/、phpinfo.phpなど
  • /api/version、/api/configみたいなAPIエンドポイント
  • 設定ファイル:dump.sql、.git/config、docker-compose.yml
  • 銀行フィッシングページ:/lander/sberbank*、/index_sber.php

この限界は自分のサーバーで見た。404や403を返すとスキャナーはそのルートがないと即分かって次に行く。このハニーポットでは逆を選んだ。スキャナーに全部あると思わせるんだ。本物のサーバーは既知ルートの後ろで守られてる間に、偽応答に時間を溶かさせる。

アーキテクチャ

1. ルックアップテーブルじゃなく分類器

中心部品はsrc/services/mockupGenerator.tsのclassify()関数で、エンドポイントパスを受けて応答ジェネレータを返す。ルールのカスケードとして作った。

  1. 完全一致:よくあるエンドポイントは専用ジェネレータ
  2. パターン一致:パスへの正規表現でid_rsaやwp-adminやsitemap.xmlみたいな模様を検出
  3. 賢い分類:/262LBNFpみたいなパス(6文字以上のランダム)は「C2ハートビート」に分類
  4. 包括ルール:APIパス、.phpや.htmlや.jsや.jsonや.asmxファイルは汎用ジェネレータ
  5. 最終キャッチオール:genCatchall()がメタデータ付き汎用JSON応答を作る

この手法が好きなのは応答がリクエストごとに新鮮だからだ。静的ファイルなし、キャッシュなし、各応答が本物のサーバーから来たみたいに自分のタイムスタンプを持つ。

2. 二変種システム

各ジェネレータは2変種に対応する。

type Variant = "default" | "complete";
  • default:簡潔だが信用できる応答。.envの例:

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete:とても詳細な応答。.envの例:

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

complete変種はCSS付き完全HTMLページも含む。管理画面、WordPressログイン画面、ロシア語銀行フィッシングページなどだ。スキャナーを本当に足止めしたいときに強力だと思う。

3. その場生成

Date.now()とDate().toISOString()で呼び出しごとに一意のタイムスタンプとリクエストIDを作る。enrich関数はこうだ。

function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

JSON応答ではミドルウェアがパースしてこのフィールドでenrichを試みる。非JSON応答はそのまま返す。

4. ヘッダースプーフィング用ミドルウェア

要求ファイル拡張子に応じた現実的なHTTPヘッダーを立てる専用ミドルウェアを足した。

res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// 拡張子ベースの現実的X-Powered-By
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

応答はずっと信用できるようになる。X-Powered-By: PHP/8.1.12とServer: nginx/1.24.0付き.phpエンドポイントはまさに本物のWordPressサーバーに見える。偽サーバーを裏切るのはたいていヘッダーだから、この詳細にこだわってる。

5. PHPスプーファー

phpSpooferは今もいちばん好きな部品だ。.phpファイル宛てリクエストが来てホストがlocalhostなら、ミドルウェアはリクエストをローカルPHPサーバーへプロキシして、本物のPHPページを処理して返させる。ホストがリモートならSSRF試行を止めるため404を返す。

const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // 本物のPHP応答を返す
  } else {
    res.status(404).send("404 Not Found");
  }
};

ローカルでWordPressを回してると、ハニーポットがボットに本物のWordPressページを出すことになる。私の経験では最大の現実感だ。

6. トラフィックの記録と分析

2つの責務を分けた。TrafficServiceは各リクエストをJSON-linesファイルに記録する。UnhandledRoutesServiceは後でログを分析して、ハニーポットで未対応のままボットが求めたパスを特定する。

app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

このフィードバックループが大好きだ。ハニーポットを回すほどボットが何を探してるか学べて、そのパスを既知エンドポイントに足せる。

7. 既知エンドポイントと除外

ミドルウェアはknownPaths、knownPatterns、knownApiPaths、knownApiPatternsのリストを受け付ける。一致するルートは全部ミドルウェアを通り抜けて本物アプリへ行く。このフィルタは必須だと思う。本物エンドポイントは無傷のままで、ハニーポットは絶対横取りしない。

const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

328エンドポイントの詳細

分類はこうだ(分類器からの抜粋)。

カテゴリ エンドポイント例
認証情報漏れ .env、secrets.json、aws/credentials、etc/shadow
SSHキー .ssh/id_rsa、.ssh/id_ed25519
DB設定 config/database、wp-config.php、docker-compose.yml
管理パネル /admin、/wp-admin、/manage/account/login
API応答 /api/version、/api/config、.do、.ashx
銀行フィッシング /lander/sber*、/index_sber.php
C2ハートビート 6文字以上ランダムパス(/262LBNFp)
株や取引所 /stock/mzhishu、/kline/1m/1、/m/allticker/1
ゲームやカジノ /proxy/games、/Ctrls/GetSysCoin、/room/getRoomBangFans
設定ファイル config.json、config.yml、sitemap.xml、ads.txt
ランディングページ /about、/contact、/products、/blog

特殊コードとエンドポイント

特殊ケース用に専用ジェネレータを足した。

  • genLanding():СберБанк、Тинькофф、Газпромбанк向けロシア語銀行フィッシングページ
  • genC2Heartbeat():agent_idやgroupやversion付き現実的C2ハートビート応答
  • genScannerPath():特定スキャナー用罠(NICEのnmapポート、Hazelcast、CGI)
  • genChineseApi():msg: "成功"(成功)付き中国語API応答
  • genMerchant():balanceやfee_rateやtier付き加盟店プロフィール

使い方

インストール

npm install express-middleware-honeypot

自動登録の基本利用

const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

app.use()付き(単一キャッチオールミドルウェア)

const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// 本物ルートを先に
app.get("/", (req, res) => res.send("Home"));

// 次にキャッチオールハニーポット
const instance = createHoneypot({ /* オプション */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

完全応答とデフォルト応答

// 完全HTMLページ付き詳細応答
const instance = createHoneypot({ isCompleteResponses: true });

トラフィック記録

const instance = createHoneypot({ logTraffic: true });
// traffic.txtを見てボットが何を求めてるか確認

結論

このExpressハニーポットミドルウェアは単なるおとりとは段違いだ。328エンドポイント、その場生成、動的ヘッダースプーフィング、トラフィック記録、本物のWordPress応答用PHPプロキシまで付けて、できるだけ現実的に設計した。

スキャナーは偽管理画面や信用できる.envファイルや銀行フィッシングページに時間を溶かして、その間本物アプリは守られる。二変種システムと賢い分類が特に気に入ってる。各応答が新鮮で、要求パスに合ったものになる。

ソースコードはGitHubにある。https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

초현실적인 Express 허니팟을 만들어 봤어요

즉석에서 만들어내는 응답의 가짜 엔드포인트 328개, 헤더 스푸핑, 봇 트래픽 로깅. 스캐너를 유인하려고 설계된 Express 허니팟 미들웨어 속으로 들어가 봐요.

TL;DR

두 가지 변형 중 선택할 수 있는 328개의 가짜 엔드포인트를 노출하는 Express 미들웨어를 만들었어요. default(차분하지만 믿을 만한 응답)와 complete(아주 자세한 응답)이에요. 각 응답은 신선한 타임스탬프와 요청 식별자와 함께 즉석에서 생성돼서, 매 응답이 고유해요. 디스크에 목 파일은 전혀 없어요. 미들웨어는 트래픽을 텍스트 파일에 기록하고, 봇이 무엇을 찾는지 분석하려고 처리되지 않은 라우트를 /newBotsRoute에 노출할 수도 있어요.

단순한 404 돌려주기가 아니에요. 스캐너가 시간을 낭비하도록 진짜 서버처럼 보이게 설계된 엔지니어링 작품이에요. 어떻게 동작하는지 보여 드릴게요.

문제

보안 봇과 스크레이퍼, 악의적 행위자들은 흔한 라우트를 찾아 웹 서버를 끊임없이 스캔해요.

  • .env, wp-config.php, admin/, phpinfo.php 등
  • /api/version, /api/config 같은 API 엔드포인트
  • 설정 파일: dump.sql, .git/config, docker-compose.yml
  • 은행 피싱 페이지: /lander/sberbank*, /index_sber.php

제 서버에서도 이 한계를 봤어요. 404나 403을 돌려주면 스캐너에게 그 라우트가 없다는 것을 바로 알려주니 다음으로 넘어가요. 이 허니팟에서는 반대를 택했어요. 스캐너에게 모든 것이 존재한다고 믿게 해서, 진짜 서버는 알려진 라우트 뒤에서 보호받는 동안 가짜 응답에 시간을 허비하게 해요.

아키텍처

1. 조회 테이블이 아닌 분류기

핵심은 src/services/mockupGenerator.ts의 classify() 함수로, 엔드포인트 경로를 받아서 응답 생성기를 돌려줘요. 규칙의 캐스케이드로 만들었어요.

  1. 정확히 일치: 가장 흔한 엔드포인트에는 전용 생성기가 있어요
  2. 패턴 일치: id_rsa, wp-admin, sitemap.xml 같은 패턴을 감지하는 경로 정규식
  3. 지능형 분류: /262LBNFp(6자 이상 무작위 문자) 같은 경로는 "C2 하트비트"로 분류돼요
  4. 포괄 규칙: API 경로와 .php, .html, .js, .json, .asmx 파일에는 범용 생성기가 있어요
  5. 최종 캐치올: genCatchall()이 메타데이터가 담긴 범용 JSON 응답을 만들어요

이 접근이 좋은 이유는 응답이 매 요청마다 신선하게 유지되기 때문이에요. 정적 파일도 없고 캐시도 없고, 각 응답이 진짜 서버에서 온 것처럼 자신만의 타임스탬프를 지녀요.

2. 이중 변형 시스템

각 생성기는 두 가지 변형을 지원해요.

type Variant = "default" | "complete";
  • default: 간결하지만 믿을 만한 응답. .env 예시:

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete: 아주 자세한 응답. .env 예시:

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

complete 변형에는 스타일된 CSS가 들어간 완전한 HTML 페이지도 포함돼요. 관리자 페이지, WordPress 로그인 화면, 러시아어 은행 피싱 페이지 등이요. 스캐너를 정말 붙잡아 두고 싶을 때 강력하다고 생각해요.

3. 즉석 생성

호출마다 고유한 타임스탬프와 요청 식별자를 만들기 위해 Date.now()와 Date().toISOString()을 써요. 보강 함수는 이렇습니다.

function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

JSON 응답의 경우 미들웨어가 파싱해서 이 필드들로 보강해요. JSON이 아닌 응답은 그대로 돌려줘요.

4. 헤더 스푸핑 미들웨어

요청된 파일 확장자에 따라 현실적인 HTTP 헤더를 설정하는 전용 미들웨어를 추가했어요.

res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// 파일 확장자 기반의 현실적인 X-Powered-By
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

이렇게 하면 응답이 훨씬 믿을 만해져요. X-Powered-By: PHP/8.1.12와 Server: nginx/1.24.0이 붙은 .php 엔드포인트는 정확히 진짜 WordPress 서버처럼 보여요. 가짜 서버임을 드러내는 것이 흔히 헤더이기 때문에 이 세부 사항을 고수해요.

5. PHP 스푸퍼

phpSpoofer는 여전히 제가 가장 좋아하는 컴포넌트예요. .php 파일 요청이 들어오고 호스트가 localhost이면, 미들웨어가 요청을 여러분의 로컬 PHP 서버로 프록시해서 진짜 PHP 페이지를 처리하고 돌려줘요. 호스트가 원격이면 SSRF 시도를 막으려고 404를 돌려줘요.

const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // 진짜 PHP 응답을 돌려줘요
  } else {
    res.status(404).send("404 Not Found");
  }
};

이렇게 하면 WordPress를 로컬로 돌릴 때 허니팟이 봇에게 진짜 WordPress 페이지를 서빙해요. 제 경험상 최대의 현실감이에요.

6. 트래픽 기록과 분석

두 책임을 나눴어요. TrafficService는 각 요청을 JSON-lines 파일에 기록해요. UnhandledRoutesService는 이 로그를 분석해서 봇이 요청하는데 허니팟에서 아직 커버하지 못하는 경로를 찾아내요.

app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

이 피드백 루프가 정말 좋아요. 허니팟을 오래 돌릴수록 봇이 무엇을 찾는지 더 배우고, 그 경로들을 알려진 엔드포인트에 추가할 수 있어요.

7. 알려진 엔드포인트와 제외

미들웨어는 knownPaths, knownPatterns, knownApiPaths, knownApiPatterns 목록을 받아요. 여기에 해당하는 라우트는 미들웨어를 통과해서 진짜 애플리케이션으로 가요. 이 필터가 필수라고 생각해요. 진짜 엔드포인트는 손대지 않고, 허니팟이 절대 가로채지 않아요.

const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

328개 엔드포인트 자세히 보기

분류되는 방식은 이렇습니다 (분류기에서 발췌).

카테고리 엔드포인트 예시
자격 증명 유출 .env, secrets.json, aws/credentials, etc/shadow
SSH 키 .ssh/id_rsa, .ssh/id_ed25519
데이터베이스 설정 config/database, wp-config.php, docker-compose.yml
관리자 패널 /admin, /wp-admin, /manage/account/login
API 응답 /api/version, /api/config, .do, .ashx
은행 피싱 /lander/sber*, /index_sber.php
C2 하트비트 6자 이상 무작위 경로 (/262LBNFp)
주식/거래소 /stock/mzhishu, /kline/1m/1, /m/allticker/1
게임/카지노 /proxy/games, /Ctrls/GetSysCoin, /room/getRoomBangFans
설정 파일 config.json, config.yml, sitemap.xml, ads.txt
랜딩 페이지 /about, /contact, /products, /blog

특수 코드와 엔드포인트

특별한 경우를 위한 전문화된 생성기를 추가했어요.

  • genLanding(): СберБанк, Тинькофф, Газпромбанк용 러시아어 은행 피싱 페이지
  • genC2Heartbeat(): agent_id, group, version이 담긴 현실적인 C2 하트비트 응답
  • genScannerPath(): 특정 스캐너용 함정 (nmap NICE 포트, Hazelcast, CGI)
  • genChineseApi(): msg: "成功"(성공)이 담긴 중국어 API 응답
  • genMerchant(): balance, fee_rate, tier가 담긴 상인 프로필

사용 방법

설치

npm install express-middleware-honeypot

자동 등록과 함께 기본 사용

const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

app.use()와 함께 (단일 포괄 미들웨어)

const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// 진짜 라우트를 먼저
app.get("/", (req, res) => res.send("Home"));

// 그 다음 포괄 허니팟
const instance = createHoneypot({ /* options */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

완전한 응답과 기본 응답

// 완전한 HTML 페이지가 담긴 자세한 응답
const instance = createHoneypot({ isCompleteResponses: true });

트래픽 기록

const instance = createHoneypot({ logTraffic: true });
// 봇이 무엇을 요청하는지 traffic.txt 파일에서 확인하세요

결론

이 Express 허니팟 미들웨어는 단순한 미끼를 훨씬 넘어서요. 328개 엔드포인트와 즉석 생성, 동적 헤더 스푸핑, 트래픽 기록, 심지어 진짜 WordPress 응답을 위한 PHP 프록시까지 갖춰서, 최대한 현실적으로 느껴지도록 설계했어요.

스캐너는 가짜 관리자 페이지와 믿을 만한 .env 파일, 은행 피싱 페이지에 시간을 허비하는 동안 진짜 애플리케이션은 보호받아요. 이중 변형 시스템과 지능형 분류가 특히 마음에 들어요. 각 응답이 신선하고 요청된 경로에 맞게 유지되거든요.

소스 코드는 GitHub에 있어요. https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

Ultra gerçekçi bir Express honeypot inşa ettim

Anında üretilmiş yanıtlarla 328 sahte endpoint, başlık sahteciliği, bot trafiği kaydı, tarayıcıları oltaya düşürmek için tasarlanmış bir Express honeypot middlewareinin kalbine dalış.

TL;DR

İki varyanttan birini seçen 328 sahte endpoint sunan bir Express middlewarei oluşturdum: default (sade ama inandırıcı yanıtlar) ve complete (çok ayrıntılı yanıtlar). Her yanıt taze zaman damgaları ve istek kimlikleriyle anında üretilir, bu da her yanıtı benzersiz kılar. Diskte hiç mock dosyası yok. Middleware ayrıca trafiği bir metin dosyasına kaydedebilir ve botların ne aradığını analiz etmek için yönetilmeyen rotaları /newBotsRoute üzerinde sunabilir.

Bu basit bir 404 döndürmeden çok daha fazlası. Bu mühendislik parçasını tarayıcıların zamanını üzerinde harcayacağı gerçek bir sunucuya benzemesi için tasarladım. Nasıl çalıştığını gösteriyorum.

Sorun

Güvenlik botları, kazıyıcılar ve kötü niyetli aktörler yaygın rotalar için web sunucularını sürekli tarar:

  • .env, wp-config.php, admin/, phpinfo.php vs.
  • /api/version, /api/config gibi API endpointleri
  • yapılandırma dosyaları: dump.sql, .git/config, docker-compose.yml
  • banka oltalama sayfaları: /lander/sberbank*, /index_sber.php

Bu sınırı kendi sunucularımda gördüm: 404 ya da 403 döndürmek tarayıcıya rotanın var olmadığını hemen söyler, o da sonrakine geçer. Bu honeypot ile tersini seçtim: tarayıcıyı her şeyin var olduğuna inandırmak, gerçek sunucun bilinen rotalarının arkasında korunurken onun zamanını sahte yanıtlar üzerinde kaybettirmek.

Mimari

1. Bir arama tablosu değil, bir sınıflandırıcı

Merkez parça, bir endpoint yolunu alıp yanıt üretici döndüren src/services/mockupGenerator.ts içindeki bir classify() fonksiyonu. Onu bir kural basamağı olarak inşa ettim:

  1. Tam eşleşmeler: en yaygın endpointlerin adanmış üreticileri var
  2. Desen eşleşmeleri: id_rsa, wp-admin, sitemap.xml gibi desenleri tespit etmek için yol üzerinde regex
  3. Akıllı sınıflandırma: /262LBNFp (6+ rastgele karakter) gibi yollar "C2 kalp atışı" olarak sınıflandırılır
  4. Her şeyi yakalayan kurallar: API yolları, .php, .html, .js, .json ve .asmx dosyalarının genel üreticileri var
  5. Final yakalama: genCatchall() meta verili genel bir JSON yanıtı üretir

Bu yaklaşımı seviyorum çünkü yanıtlar her istekte taze kalır: statik dosya yok, önbellek yok, her yanıt gerçek bir sunucudan gelmiş gibi kendi zaman damgasını taşır.

2. Çift varyant sistemi

Her üretici iki varyantı destekler:

type Variant = "default" | "complete";
  • default: özlü ama inandırıcı yanıtlar. .env için örnek:

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete: çok ayrıntılı yanıtlar. .env için örnek:

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

complete varyantı ayrıca stilli CSSli tam HTML sayfaları içerir: yönetim sayfaları, WordPress giriş ekranları, Rusça banka oltalama sayfaları vs. Bir tarayıcıyı gerçekten oyalamak istediğinde müthiş buluyorum.

3. Anında üretim

Her çağrıda benzersiz zaman damgaları ve istek kimlikleri üretmek için Date.now() ve Date().toISOString() kullanıyorum. İşte zenginleştirme fonksiyonu:

function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

JSON yanıtları için middleware ayrıştırıp bu alanlarla zenginleştirmeye çalışır. JSON olmayan yanıtlar olduğu gibi döndürülür.

4. Başlık sahteciliği middlewarei

İstenen dosyanın uzantısına göre gerçekçi HTTP başlıkları ayarlayan adanmış bir middleware ekledim:

res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// dosya uzantısına dayalı gerçekçi X-Powered-By
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

Bu yanıtları çok daha inandırıcı kılar: X-Powered-By: PHP/8.1.12 ve Server: nginx/1.24.0 başlıklı bir .php endpointi tam olarak gerçek bir WordPress sunucusuna benzer. Bu detaya önem veriyorum çünkü sahte bir sunucuyu ele veren genelde başlıktır.

5. PHP Sahtekârı

phpSpoofer favori bileşenim olmaya devam ediyor. Bir .php dosyası için istek gelir ve host localhost ise middleware isteği yerel PHP sunucuna vekiller, o da gerçek bir PHP sayfası işleyip döndürür. Host uzaksa SSRF girişimlerini engellemek için 404 döndürür.

const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // gerçek PHP yanıtını döndür
  } else {
    res.status(404).send("404 Not Found");
  }
};

Bu, WordPressi yerelde çalıştırdığında honeypotun botlara gerçek WordPress sayfaları sunmasını sağlar: deneyimime göre maksimum gerçekçilik.

6. Trafik kaydı ve analizi

İki sorumluluğu ayırdım: TrafficService her isteği JSON-lines dosyasına kaydeder. UnhandledRoutesService sonra bu logları analiz edip honeypotta kapsanması gereken, botların istediği yolları belirler.

app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

Bu geri bildirim döngüsüne bayılıyorum: honeypotu ne kadar uzun çalıştırırsan botların ne aradığını o kadar öğrenirsin ve bu yolları bilinen endpointlerine ekleyebilirsin.

7. Bilinen endpointler ve hariç tutma

Middleware knownPaths, knownPatterns, knownApiPaths ve knownApiPatterns listelerini kabul eder. Bunlarla eşleşen her rota middlewarei geçip gerçek uygulamana ulaşır. Bu filtreyi vazgeçilmez buluyorum: gerçek endpointlerin bozulmadan kalır, honeypot onları asla yakalamaz.

const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

328 endpoint ayrıntılı

Sınıflandırmaları şöyle (sınıflandırıcıdan alıntı):

Kategori Örnek endpointler
Kimlik bilgisi sızıntıları .env, secrets.json, aws/credentials, etc/shadow
SSH anahtarları .ssh/id_rsa, .ssh/id_ed25519
Veritabanı yapılandırmaları config/database, wp-config.php, docker-compose.yml
Yönetim panelleri /admin, /wp-admin, /manage/account/login
API yanıtları /api/version, /api/config, .do, .ashx
Banka oltalama /lander/sber*, /index_sber.php
C2 kalp atışları 6+ karakterli rastgele yollar (/262LBNFp)
Hisse/Borsa /stock/mzhishu, /kline/1m/1, /m/allticker/1
Oyun/Kumarhane /proxy/games, /Ctrls/GetSysCoin, /room/getRoomBangFans
Yapılandırma dosyaları config.json, config.yml, sitemap.xml, ads.txt
Açılış sayfaları /about, /contact, /products, /blog

Özel kodlar ve endpointler

Özel durumlar için uzmanlaşmış üreticiler ekledim:

  • genLanding(): СберБанк, Тинькофф, Газпромбанк için Rusça banka oltalama sayfaları
  • genC2Heartbeat(): agent_id, group, version ile gerçekçi C2 kalp atışı yanıtı
  • genScannerPath(): belirli tarayıcılar için tuzaklar (nmap NICE portları, Hazelcast, CGI)
  • genChineseApi(): msg: "成功" (başarı) ile Çince API yanıtları
  • genMerchant(): balance, fee_rate, tier ile satıcı profilleri

Nasıl kullanılır

Kurulum

npm install express-middleware-honeypot

Otomatik kayıtlı temel kullanım

const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

app.use() ile (tek her şeyi yakalayan middleware)

const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// Önce gerçek rotaların

app.get("/", (req, res) => res.send("Home"));

// Sonra her şeyi yakalayan honeypot
const instance = createHoneypot({ /* seçenekler */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

Tam yanıtlar ve varsayılan

// Tam HTML sayfalarıyla ayrıntılı yanıtlar
const instance = createHoneypot({ isCompleteResponses: true });

Trafik kaydı

const instance = createHoneypot({ logTraffic: true });
// botların ne istediğini görmek için traffic.txt dosyasına bak

Sonuç

Bu Express honeypot middlewarei basit bir yemden çok öteye gider. 328 endpoint, anında üretim, dinamik başlık sahteciliği, trafik kaydı ve gerçek WordPress yanıtları için bir PHP vekili bile ile onu olabildiğince gerçekçi kalacak şekilde tasarladım.

Tarayıcılar sahte yönetim sayfalarında, inandırıcı .env dosyalarında ve banka oltalama sayfalarında zaman kaybederken gerçek uygulaman korunur. Çift varyant sistemini ve akıllı sınıflandırmayı özellikle seviyorum: her yanıt taze kalır ve istenen yola uyarlanır.

Kaynak kodu GitHubda mevcut: https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

Ho costruito un honeypot Express ultra-realistico

328 endpoint finti con risposte generate al volo, spoofing di header, logging del traffico bot: immersione nel cuore di un middleware honeypot Express pensato per ingannare gli scanner.

TL;DR

Ho creato un middleware Express che espone 328 endpoint fittizi a scelta tra due varianti: default (risposte sobrie ma credibili) e complete (risposte molto dettagliate). Ogni risposta è generata al volo con timestamp e identificatori di richiesta freschi, il che rende ogni risposta unica. Non c'è alcun file mock sul disco. Il middleware può anche registrare il traffico in un file di testo ed esporre le route non gestite su /newBotsRoute per analizzare cosa cercano i bot.

È molto più di un semplice rinvio di 404. Ho progettato questo pezzo di ingegneria per assomigliare a un vero server, perché gli scanner sprechino il loro tempo lì sopra. Ti mostro come funziona.

Il problema

I bot di sicurezza, gli scraper e gli attori malevoli scansionano di continuo i server web in cerca di route comuni:

  • .env, wp-config.php, admin/, phpinfo.php, ecc.
  • endpoint di API come /api/version, /api/config
  • file di configurazione: dump.sql, .git/config, docker-compose.yml
  • pagine di phishing bancario: /lander/sberbank*, /index_sber.php

Ho visto questo limite sui miei server: restituire un 404 o un 403 indica subito allo scanner che la route non esiste, quindi passa alla successiva. Ho scelto l'inverso con questo honeypot: far credere allo scanner che tutto esista, per fargli perdere tempo su false risposte mentre il tuo vero server resta protetto dietro le tue route note.

L'architettura

1. Un classificatore, non una lookup table

Il pezzo forte è una funzione classify() in src/services/mockupGenerator.ts che prende un percorso di endpoint e restituisce un generatore di risposta. L'ho costruita come una cascata di regole:

  1. Corrispondenze esatte: gli endpoint più comuni hanno generatori dedicati
  2. Corrispondenze per pattern: regex sul percorso per rilevare pattern come id_rsa, wp-admin, sitemap.xml
  3. Classificazione intelligente: percorsi come /262LBNFp (6+ caratteri casuali) sono classificati come "battiti di cuore C2"
  4. Regole acchiappatutto: i percorsi di API, i file .php, .html, .js, .json e .asmx hanno generatori generici
  5. Catchall finale: genCatchall() produce una risposta JSON generica con metadati

Amo questo approccio perché le risposte restano fresche a ogni richiesta: nessun file statico, nessuna cache, ogni risposta porta il suo timestamp come se venisse da un vero server.

2. Il sistema a doppia variante

Ogni generatore supporta due varianti:

// due livelli di dettaglio per ogni risposta
type Variant = "default" | "complete";
  • default: risposte concise ma credibili. Esempio per .env:

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete: risposte molto dettagliate. Esempio per .env:

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

La variante complete include anche pagine HTML complete con CSS stilizzato: pagine di amministrazione, schermate di login WordPress, pagine di phishing bancario in russo, ecc. La trovo temibile quando vuoi davvero occupare uno scanner.

3. Generazione al volo

Uso Date.now() e Date().toISOString() per produrre timestamp e identificatori di richiesta unici a ogni chiamata. Ecco la funzione di arricchimento:

// aggiunge timestamp freschi a ogni risposta
function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

Per le risposte JSON, il middleware prova a parsare e arricchire con questi campi. Le risposte non-JSON sono rinviate così come sono.

4. Il middleware di spoofing di header

Ho aggiunto un middleware dedicato che definisce header HTTP realistici in base all'estensione del file richiesto:

// header credibili in base all'estensione
res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// X-Powered-By realistico basato sull'estensione del file
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

Rende le risposte molto più credibili: un endpoint .php con X-Powered-By: PHP/8.1.12 e Server: nginx/1.24.0 assomiglia esattamente a un vero server WordPress. Tengo a questo dettaglio perché spesso è l'header che tradisce un falso server.

5. Il PHP Spoofer

Il phpSpoofer resta il mio componente preferito. Se una richiesta arriva per un file .php e l'host è localhost, il middleware fa proxy della richiesta verso il tuo server PHP locale, che elabora e rinvia una vera pagina PHP. Se l'host è remoto, rinvia un 404 per bloccare ogni tentativo SSRF.

// proxy verso PHP locale, 404 se host remoto
const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // rinvia la vera risposta PHP
  } else {
    res.status(404).send("404 Not Found");
  }
};

Permette, quando fai girare WordPress in locale, di avere l'honeypot che serve vere pagine WordPress ai bot: il massimo del realismo secondo la mia esperienza.

6. Registrazione e analisi del traffico

Ho separato due responsabilità: il TrafficService registra ogni richiesta in un file JSON-lines. L'UnhandledRoutesService analizza poi questi log per identificare i percorsi richiesti dai bot che restano da coprire nell'honeypot.

// espone le route viste dai bot ma non ancora coperte
app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

Adoro questo ciclo di feedback: più lasci girare l'honeypot, più impari cosa cercano i bot, e puoi aggiungere questi percorsi ai tuoi endpoint noti.

7. Endpoint noti ed esclusione

Il middleware accetta liste di knownPaths, knownPatterns, knownApiPaths e knownApiPatterns. Qualsiasi route che corrisponde a queste attraversa il middleware verso la tua vera applicazione. Trovo questo filtro indispensabile: i tuoi veri endpoint restano intatti, l'honeypot non li intercetta mai.

// le route note passano oltre l'honeypot
const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

I 328 endpoint in dettaglio

Ecco come sono classificati (estratto dal classificatore):

Categoria Esempi di endpoint
Fughe di credenziali .env, secrets.json, aws/credentials, etc/shadow
Chiavi SSH .ssh/id_rsa, .ssh/id_ed25519
Config di database config/database, wp-config.php, docker-compose.yml
Pannelli di amministrazione /admin, /wp-admin, /manage/account/login
Risposte API /api/version, /api/config, .do, .ashx
Phishing bancario /lander/sber*, /index_sber.php
Battiti di cuore C2 Percorsi casuali di 6+ caratteri (/262LBNFp)
Azioni/Borsa /stock/mzhishu, /kline/1m/1, /m/allticker/1
Giochi/Casinò /proxy/games, /Ctrls/GetSysCoin, /room/getRoomBangFans
File di configurazione config.json, config.yml, sitemap.xml, ads.txt
Pagine di atterraggio /about, /contact, /products, /blog

Codici ed endpoint speciali

Ho aggiunto generatori specializzati per casi particolari:

  • genLanding(): pagine di phishing bancario in russo per СберБанк, Тинькофф, Газпромбанк
  • genC2Heartbeat(): risposta di battito di cuore C2 realistica con agent_id, group, version
  • genScannerPath(): trappole per scanner specifici (porte nmap NICE, Hazelcast, CGI)
  • genChineseApi(): risposte di API cinesi con msg: "成功" (successo)
  • genMerchant(): profili di commercianti con balance, fee_rate, tier

Come usarlo

Installazione

npm install express-middleware-honeypot

Uso base con auto-registrazione

// registra l'honeypot su express
const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

Con app.use() (middleware acchiappatutto unico)

// vere route prima, honeypot dopo
const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// Le tue vere route per prime
app.get("/", (req, res) => res.send("Home"));

// Poi l'honeypot acchiappatutto
const instance = createHoneypot({ /* opzioni */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

Risposte complete contro default

// Risposte dettagliate con pagine HTML complete
const instance = createHoneypot({ isCompleteResponses: true });

Registrazione del traffico

// logga ciò che i bot chiedono in traffic.txt
const instance = createHoneypot({ logTraffic: true });
// Consulta il file traffic.txt per vedere cosa chiedono i bot

Conclusione

Questo middleware honeypot Express va ben oltre una semplice esca. Con 328 endpoint, una generazione al volo, uno spoofing di header dinamico, una registrazione del traffico e persino un proxy PHP per vere risposte WordPress, l'ho pensato per restare il più realistico possibile.

Gli scanner perdono tempo su pagine di amministrazione fittizie, file .env credibili e pagine di phishing bancario mentre la tua vera applicazione resta protetta. Apprezzo in particolare il sistema a doppia variante e la classificazione intelligente: ogni risposta resta fresca e adatta al percorso richiesto.

Il codice sorgente è disponibile su GitHub: https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

Ich habe ein ultra-realistisches Express-Honeypot gebaut

328 Fake-Endpoints mit on-the-fly generierten Antworten, Header-Spoofing, Bot-Traffic-Logging, ein Tauchgang ins Herz einer Express-Honeypot-Middleware, gebaut um Scanner zu ködern.

TL;DR

Ich habe eine Express-Middleware erstellt, die 328 Fake-Endpoints zur Wahl aus zwei Varianten exponiert: default (nüchterne, aber glaubwürdige Antworten) und complete (sehr detaillierte Antworten). Jede Antwort wird on the fly mit frischen Timestamps und Request-IDs generiert, was jede Antwort einzigartig macht. Es gibt keine Mock-Dateien auf der Platte. Die Middleware kann auch Traffic loggen in eine Textdatei und unbehandelte Routen auf /newBotsRoute exponieren, um zu analysieren, was die Bots suchen.

Das ist weit mehr als ein simples 404-Zurückschicken. Ich habe dieses Stück Engineering so entworfen, dass es wie ein echter Server aussieht, damit Scanner ihre Zeit darauf verschwenden. Ich zeige dir, wie es funktioniert.

Das Problem

Sicherheitsbots, Scraper und böswillige Akteure scannen ständig Webserver nach gängigen Routen:

  • .env, wp-config.php, admin/, phpinfo.php usw.
  • API-Endpoints wie /api/version, /api/config
  • Konfigurationsdateien: dump.sql, .git/config, docker-compose.yml
  • Bank-Phishing-Seiten: /lander/sberbank*, /index_sber.php

Ich habe diese Grenze auf meinen eigenen Servern gesehen: Ein 404 oder 403 zurückzuschicken zeigt dem Scanner sofort, dass die Route nicht existiert, also geht er zur nächsten. Ich habe mit diesem Honeypot das Gegenteil gewählt: Den Scanner glauben lassen, dass alles existiert, um ihn Zeit auf falschen Antworten verlieren zu lassen, während dein echter Server hinter deinen bekannten Routen geschützt bleibt.

Die Architektur

1. Ein Klassifizierer, keine Lookup-Tabelle

Das Herzstück ist eine classify()-Funktion in src/services/mockupGenerator.ts, die einen Endpoint-Pfad nimmt und einen Antwortgenerator zurückgibt. Ich habe sie als Regelkaskade gebaut:

  1. Exakte Treffer: Die gängigsten Endpoints haben dedizierte Generatoren
  2. Muster-Treffer: Regex auf dem Pfad, um Muster wie id_rsa, wp-admin, sitemap.xml zu erkennen
  3. Intelligente Klassifizierung: Pfade wie /262LBNFp (6+ zufällige Zeichen) werden als "C2-Hearbeats" klassifiziert
  4. Catch-all-Regeln: API-Pfade, .php-, .html-, .js-, .json- und .asmx-Dateien haben generische Generatoren
  5. Finaler Catchall: genCatchall() produziert eine generische JSON-Antwort mit Metadaten

Ich mag diesen Ansatz, weil die Antworten bei jeder Anfrage frisch bleiben: Keine statischen Dateien, kein Caching, jede Antwort trägt ihren eigenen Timestamp, als käme sie von einem echten Server.

2. Das Doppel-Varianten-System

Jeder Generator unterstützt zwei Varianten:

type Variant = "default" | "complete";
  • default: Kurze, aber glaubwürdige Antworten. Beispiel für .env:

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete: Sehr detaillierte Antworten. Beispiel für .env:

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

Die complete-Variante enthält auch komplette HTML-Seiten mit gestyltem CSS: Admin-Seiten, WordPress-Login-Screens, russische Bank-Phishing-Seiten usw. Ich finde sie furchterregend, wenn du einen Scanner wirklich beschäftigen willst.

3. On-the-fly-Generierung

Ich nutze Date.now() und Date().toISOString(), um bei jedem Aufruf einzigartige Timestamps und Request-IDs zu produzieren. Hier die Anreicherungsfunktion:

function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

Für JSON-Antworten versucht die Middleware zu parsen und mit diesen Feldern anzureichern. Nicht-JSON-Antworten werden so zurückgeschickt, wie sie sind.

4. Die Header-Spoofing-Middleware

Ich habe eine dedizierte Middleware hinzugefügt, die realistische HTTP-Header setzt, je nach Endung der angefragten Datei:

res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// Realistisches X-Powered-By basierend auf der Dateiendung
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

Das macht die Antworten viel glaubwürdiger: Ein .php-Endpoint mit X-Powered-By: PHP/8.1.12 und Server: nginx/1.24.0 sieht exakt wie ein echter WordPress-Server aus. Mir liegt an diesem Detail, weil es oft der Header ist, der einen Fake-Server verrät.

5. Der PHP-Spoofer

Der phpSpoofer bleibt meine Lieblingskomponente. Wenn eine Anfrage für eine .php-Datei reinkommt und der Host localhost ist, proxyt die Middleware die Anfrage an deinen lokalen PHP-Server, der eine echte PHP-Seite verarbeitet und zurückschickt. Wenn der Host remote ist, gibt sie ein 404 zurück, um jeden SSRF-Versuch zu blockieren.

const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // echte PHP-Antwort zurückschicken
  } else {
    res.status(404).send("404 Not Found");
  }
};

Das erlaubt, wenn du WordPress lokal laufen lässt, dass das Honeypot den Bots echte WordPress-Seiten serviert: Nach meiner Erfahrung das Maximum an Realismus.

6. Traffic-Logging und -Analyse

Ich habe zwei Verantwortlichkeiten getrennt: Der TrafficService loggt jede Anfrage in eine JSON-Lines-Datei. Der UnhandledRoutesService analysiert dann diese Logs, um die von Bots angefragten Pfade zu identifizieren, die im Honeypot noch abzudecken sind.

app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

Ich liebe diese Feedbackschleife: Je länger du das Honeypot laufen lässt, desto mehr lernst du, was die Bots suchen, und du kannst diese Pfade zu deinen bekannten Endpoints hinzufügen.

7. Bekannte Endpoints und Ausschluss

Die Middleware akzeptiert Listen von knownPaths, knownPatterns, knownApiPaths und knownApiPatterns. Jede Route, die darauf passt, läuft durch die Middleware zu deiner echten Applikation. Ich finde diesen Filter unverzichtbar: Deine echten Endpoints bleiben intakt, das Honeypot fängt sie niemals ab.

const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

Die 328 Endpoints im Detail

So werden sie klassifiziert (Auszug aus dem Klassifizierer):

Kategorie Endpoint-Beispiele
Credential-Leaks .env, secrets.json, aws/credentials, etc/shadow
SSH-Schlüssel .ssh/id_rsa, .ssh/id_ed25519
Datenbank-Configs config/database, wp-config.php, docker-compose.yml
Admin-Panels /admin, /wp-admin, /manage/account/login
API-Antworten /api/version, /api/config, .do, .ashx
Bank-Phishing /lander/sber*, /index_sber.php
C2-Heartbeats Zufällige 6+ Zeichen-Pfade (/262LBNFp)
Aktien/Börse /stock/mzhishu, /kline/1m/1, /m/allticker/1
Spiele/Casino /proxy/games, /Ctrls/GetSysCoin, /room/getRoomBangFans
Konfigurationsdateien config.json, config.yml, sitemap.xml, ads.txt
Landingpages /about, /contact, /products, /blog

Spezielle Codes und Endpoints

Ich habe spezialisierte Generatoren für besondere Fälle hinzugefügt:

  • genLanding(): Russische Bank-Phishing-Seiten für СберБанк, Тинькофф, Газпромбанк
  • genC2Heartbeat(): Realistische C2-Heartbeat-Antwort mit agent_id, group, version
  • genScannerPath(): Fallen für spezifische Scanner (nmap NICE-Ports, Hazelcast, CGI)
  • genChineseApi(): Chinesische API-Antworten mit msg: "成功" (Erfolg)
  • genMerchant(): Händlerprofile mit balance, fee_rate, tier

Wie man es nutzt

Installation

npm install express-middleware-honeypot

Basisnutzung mit Auto-Registrierung

const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

Mit app.use() (einzige Catch-all-Middleware)

const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// Deine echten Routen zuerst
app.get("/", (req, res) => res.send("Home"));

// Dann das Catch-all-Honeypot
const instance = createHoneypot({ /* options */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

Vollständige vs. Standardantworten

// Detaillierte Antworten mit kompletten HTML-Seiten
const instance = createHoneypot({ isCompleteResponses: true });

Traffic-Logging

const instance = createHoneypot({ logTraffic: true });
// Schau in die Datei traffic.txt, um zu sehen, was die Bots anfragen

Fazit

Diese Express-Honeypot-Middleware geht weit über einen simplen Köder hinaus. Mit 328 Endpoints, On-the-fly-Generierung, dynamischem Header-Spoofing, Traffic-Logging und sogar einem PHP-Proxy für echte WordPress-Antworten habe ich sie so realistisch wie möglich entworfen.

Scanner verlieren Zeit auf gefälschten Admin-Seiten, glaubwürdigen .env-Dateien und Bank-Phishing-Seiten, während deine echte Applikation geschützt bleibt. Ich schätze besonders das Doppel-Varianten-System und die intelligente Klassifizierung: Jede Antwort bleibt frisch und an den angefragten Pfad angepasst.

Der Quellcode ist auf GitHub verfügbar: https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

Я построил ультрареалистичный honeypot на Express

328 фейковых эндпоинтов с ответами, генерируемыми на лету, спуфинг заголовков, логирование бот-трафика -- погружение в сердце honeypot-middleware для Express, задуманного дурить сканеры.

TL;DR

Я создал middleware для Express, выставляющий 328 фейковых эндпоинтов на выбор из двух вариантов : default (сдержанные, но правдоподобные ответы) и complete (очень детальные ответы). Каждый ответ генерируется на лету со свежими timestamp и идентификаторами запроса, что делает каждый ответ уникальным. Никаких мок-файлов на диске. Middleware умеет также журналировать трафик в текстовый файл и выставлять необработанные роуты на /newBotsRoute, чтобы анализировать, что ищут боты.

Это куда больше, чем простой возврат 404. Я проектировал эту инженерную штуку так, чтобы походить на настоящий сервер, чтобы сканеры тратили на неё время. Показываю, как это работает.

Проблема

Боты безопасности, скраперы и злоумышленники постоянно сканируют веб-серверы в поисках распространённых роутов :

  • .env, wp-config.php, admin/, phpinfo.php и т.д.
  • API-эндпоинты вроде /api/version, /api/config
  • файлы конфигурации : dump.sql, .git/config, docker-compose.yml
  • страницы банковского фишинга : /lander/sberbank*, /index_sber.php

Я видел этот предел на собственных серверах : возврат 404 или 403 сразу говорит сканеру, что роута нет, и он идёт к следующей. С этим honeypot я выбрал обратное : заставить сканер поверить, что существует всё, чтобы он терял время на фейковых ответах, пока твой настоящий сервер остаётся защищён за известными роутами.

Архитектура

1. Классификатор, а не lookup-таблица

Центральная деталь это функция classify() в src/services/mockupGenerator.ts, принимающая путь эндпоинта и возвращающая генератор ответа. Я построил её как каскад правил :

  1. Точные совпадения : у самых распространённых эндпоинтов выделенные генераторы
  2. Совпадения по паттерну : regex по пути для детекта паттернов вроде id_rsa, wp-admin, sitemap.xml
  3. Умная классификация : пути вроде /262LBNFp (6+ случайных символов) классифицируются как "сердцебиения C2"
  4. Правила-заглушки : пути API, файлы .php, .html, .js, .json и .asmx имеют общие генераторы
  5. Финальный catchall : genCatchall() даёт общий JSON-ответ с метаданными

Мне нравится этот подход, потому что ответы остаются свежими при каждом запросе : никаких статичных файлов, никакого кэширования, каждый ответ несёт собственный timestamp, будто пришёл с настоящего сервера.

2. Система двойного варианта

Каждый генератор поддерживает два варианта :

type Variant = "default" | "complete";
  • default : краткие, но правдоподобные ответы. Пример для .env :

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete : очень детальные ответы. Пример для .env :

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

Вариант complete включает также полные HTML-страницы со стилизованным CSS : админки, экраны логина WordPress, страницы банковского фишинга на русском и т.д. Считаю его грозным, когда хочешь реально занять сканер.

3. Генерация на лету

Я использую Date.now() и Date().toISOString(), чтобы давать уникальные timestamp и идентификаторы запроса при каждом вызове. Вот функция обогащения :

function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

Для JSON-ответов middleware пытается парсить и обогащать этими полями. Не-JSON ответы возвращаются как есть.

4. Middleware спуфинга заголовков

Я добавил выделенный middleware, ставящий реалистичные HTTP-заголовки в зависимости от расширения запрошенного файла :

res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// Реалистичный X-Powered-By по расширению файла
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

Это делает ответы куда правдоподобнее : эндпоинт .php с X-Powered-By: PHP/8.1.12 и Server: nginx/1.24.0 выглядит ровно как настоящий сервер WordPress. Держусь за эту деталь, потому что часто именно заголовок выдаёт фейковый сервер.

5. PHP-спуфер

phpSpoofer остаётся моим любимым компонентом. Если запрос приходит за файлом .php и хост это localhost, middleware проксирует запрос на твой локальный PHP-сервер, который обрабатывает и возвращает настоящую PHP-страницу. Если хост удалённый, он возвращает 404, чтобы блокировать попытки SSRF.

const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // возвращает настоящий PHP-ответ
  } else {
    res.status(404).send("404 Not Found");
  }
};

Это позволяет, когда крутишь WordPress локально, honeypot отдаёт ботам настоящие страницы WordPress : максимум реализма по моему опыту.

6. Журналирование и анализ трафика

Я разделил две ответственности : TrafficService журналирует каждый запрос в файл JSON-lines. UnhandledRoutesService затем анализирует эти логи, чтобы выявлять пути, запрашиваемые ботами, которые honeypot пока не покрывает.

app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

Обожаю эту петлю обратной связи : чем дольше крутится honeypot, тем больше узнаёшь, что ищут боты, и можешь добавлять эти пути в известные эндпоинты.

7. Известные эндпоинты и исключение

Middleware принимает списки knownPaths, knownPatterns, knownApiPaths и knownApiPatterns. Любой роут, совпавший с ними, проходит сквозь middleware к твоему настоящему приложению. Считаю этот фильтр обязательным : настоящие эндпоинты остаются нетронутыми, honeypot их никогда не перехватывает.

const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

328 эндпоинтов в деталях

Вот как они классифицированы (выдержка из классификатора) :

Catégorie Exemples d'endpoints
Fuites de credentials .env, secrets.json, aws/credentials, etc/shadow
Clés SSH .ssh/id_rsa, .ssh/id_ed25519
Configs de base de données config/database, wp-config.php, docker-compose.yml
Panneaux d'administration /admin, /wp-admin, /manage/account/login
Réponses API /api/version, /api/config, .do, .ashx
Phishing bancaire /lander/sber*, /index_sber.php
Battements de cœur C2 Chemins aléatoires de 6+ caractères (/262LBNFp)
Actions/Bourse /stock/mzhishu, /kline/1m/1, /m/allticker/1
Jeux/Casino /proxy/games, /Ctrls/GetSysCoin, /room/getRoomBangFans
Fichiers de configuration config.json, config.yml, sitemap.xml, ads.txt
Pages d'atterrissage /about, /contact, /products, /blog

Специальные коды и эндпоинты

Я добавил специализированные генераторы для частных случаев :

  • genLanding() : страницы банковского фишинга на русском для СберБанка, Тинькофф, Газпромбанка
  • genC2Heartbeat() : реалистичный ответ сердцебиения C2 с agent_id, group, version
  • genScannerPath() : ловушки для конкретных сканеров (порты nmap NICE, Hazelcast, CGI)
  • genChineseApi() : ответы китайских API с msg: "成功" (успех)
  • genMerchant() : профили торговцев с balance, fee_rate, tier

Как использовать

Установка

npm install express-middleware-honeypot

Базовое использование с авторегистрацией

const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

Через app.use() (единый catch-all middleware)

const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// Сначала настоящие роуты
app.get("/", (req, res) => res.send("Home"));

// Затем catch-all honeypot
const instance = createHoneypot({ /* options */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

Полные против дефолтных ответов

// Детальные ответы с полными HTML-страницами
const instance = createHoneypot({ isCompleteResponses: true });

Журналирование трафика

const instance = createHoneypot({ logTraffic: true });
// Смотри файл traffic.txt, чтобы видеть, что просят боты

Заключение

Этот honeypot-middleware для Express идёт сильно дальше простой приманки. С 328 эндпоинтами, генерацией на лету, динамическим спуфингом заголовков, журналированием трафика и даже PHP-прокси для настоящих ответов WordPress, я проектировал его максимально реалистичным.

Сканеры теряют время на фейковых админках, правдоподобных файлах .env и страницах банковского фишинга, пока настоящее приложение остаётся защищённым. Особенно ценю систему двойного варианта и умную классификацию : каждый ответ остаётся свежим и подходящим запрошенному пути.

Исходник доступен на GitHub : https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

Construí un honeypot Express ultrarrealista

328 endpoints falsos con respuestas generadas al vuelo, spoofing de cabeceras, registro del tráfico bot: inmersión en un middleware honeypot Express diseñado para engañar a los escáneres.

TL;DR

Creé un middleware Express que expone 328 endpoints ficticios a elegir entre dos variantes: default (respuestas sobrias pero creíbles) y complete (respuestas muy detalladas). Cada respuesta se genera al vuelo con timestamps e identificadores de petición frescos, lo que hace cada respuesta única. No hay ningún archivo mock en el disco. El middleware también puede registrar el tráfico en un archivo de texto y exponer las rutas no gestionadas en /newBotsRoute para analizar lo que buscan los bots.

Es mucho más que un simple retorno de 404. Diseñé esta pieza de ingeniería para parecerse a un servidor real, para que los escáneres pierdan su tiempo en él. Te muestro cómo funciona.

El problema

Los bots de seguridad, los scrapers y los actores maliciosos escanean constantemente los servidores web buscando rutas comunes:

  • .env, wp-config.php, admin/, phpinfo.php, etc.
  • endpoints de API como /api/version, /api/config
  • archivos de configuración: dump.sql, .git/config, docker-compose.yml
  • páginas de phishing bancario: /lander/sberbank*, /index_sber.php

Vi ese límite en mis propios servidores: devolver un 404 o un 403 indica enseguida al escáner que la ruta no existe, así que pasa a la siguiente. Elegí lo contrario con este honeypot: hacer creer al escáner que todo existe, para hacerle perder tiempo en respuestas falsas mientras tu servidor real queda protegido tras tus rutas conocidas.

La arquitectura

1. Un clasificador, no una lookup table

La pieza maestra es una función classify() en src/services/mockupGenerator.ts que toma una ruta de endpoint y devuelve un generador de respuesta. La construí como una cascada de reglas:

  1. Coincidencias exactas: los endpoints más comunes tienen generadores dedicados
  2. Coincidencias por patrón: regex sobre la ruta para detectar patrones como id_rsa, wp-admin, sitemap.xml
  3. Clasificación inteligente: rutas como /262LBNFp (6+ caracteres aleatorios) se clasifican como "latidos C2"
  4. Reglas comodín: las rutas de API, los archivos .php, .html, .js, .json y .asmx tienen generadores genéricos
  5. Catchall final: genCatchall() produce una respuesta JSON genérica con metadatos

Me gusta este enfoque porque las respuestas quedan frescas en cada petición: ningún archivo estático, ningún cacheo, cada respuesta lleva su propio timestamp como si viniera de un servidor real.

2. El sistema de doble variante

Cada generador soporta dos variantes:

type Variant = "default" | "complete";
  • default: respuestas concisas pero creíbles. Ejemplo para .env:

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete: respuestas muy detalladas. Ejemplo para .env:

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

La variante complete incluye además páginas HTML completas con CSS estilizado: páginas de administración, pantallas de login de WordPress, páginas de phishing bancario en ruso, etc. La encuentro temible cuando de verdad quieres ocupar un escáner.

3. Generación al vuelo

Uso Date.now() y Date().toISOString() para producir timestamps e identificadores de petición únicos en cada llamada. Aquí la función de enriquecimiento:

function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

Para las respuestas JSON, el middleware intenta parsear y enriquecer con esos campos. Las respuestas no-JSON se devuelven tal cual.

4. El middleware de spoofing de cabeceras

Añadí un middleware dedicado que define cabeceras HTTP realistas según la extensión del archivo pedido:

res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// X-Powered-By realista basado en la extensión del archivo
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

Eso hace las respuestas mucho más creíbles: un endpoint .php con X-Powered-By: PHP/8.1.12 y Server: nginx/1.24.0 se parece exactamente a un servidor WordPress real. Me importa ese detalle porque a menudo es la cabecera la que delata un servidor falso.

5. El PHP Spoofer

El phpSpoofer sigue siendo mi componente favorito. Si llega una petición para un archivo .php y el host es localhost, el middleware proxifica la petición hacia tu servidor PHP local, que procesa y devuelve una página PHP real. Si el host es remoto, devuelve un 404 para bloquear cualquier intento SSRF.

const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // devuelve la respuesta PHP real
  } else {
    res.status(404).send("404 Not Found");
  }
};

Eso permite, cuando corres WordPress en local, que el honeypot sirva verdaderas páginas WordPress a los bots: el máximo realismo según mi experiencia.

6. Registro y análisis del tráfico

Separé dos responsabilidades: el TrafficService registra cada petición en un archivo JSON-lines. El UnhandledRoutesService analiza después esos logs para identificar las rutas que piden los bots y que quedan por cubrir en el honeypot.

app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

Me encanta ese bucle de retroalimentación: cuanto más dejas correr el honeypot, más aprendes lo que buscan los bots, y puedes añadir esas rutas a tus endpoints conocidos.

7. Endpoints conocidos y exclusión

El middleware acepta listas de knownPaths, knownPatterns, knownApiPaths y knownApiPatterns. Toda ruta que coincida con ellas atraviesa el middleware hacia tu verdadera aplicación. Encuentro ese filtro indispensable: tus endpoints reales quedan intactos, el honeypot nunca los intercepta.

const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

Los 328 endpoints en detalle

Así se clasifican (extracto del clasificador):

Categoría Ejemplos de endpoints
Fugas de credentials .env, secrets.json, aws/credentials, etc/shadow
Claves SSH .ssh/id_rsa, .ssh/id_ed25519
Configs de base de datos config/database, wp-config.php, docker-compose.yml
Paneles de administración /admin, /wp-admin, /manage/account/login
Respuestas API /api/version, /api/config, .do, .ashx
Phishing bancario /lander/sber*, /index_sber.php
Latidos C2 Rutas aleatorias de 6+ caracteres (/262LBNFp)
Acciones/Bolsa /stock/mzhishu, /kline/1m/1, /m/allticker/1
Juegos/Casino /proxy/games, /Ctrls/GetSysCoin, /room/getRoomBangFans
Archivos de configuración config.json, config.yml, sitemap.xml, ads.txt
Páginas de aterrizaje /about, /contact, /products, /blog

Códigos y endpoints especiales

Añadí generadores especializados para casos particulares:

  • genLanding(): páginas de phishing bancario en ruso para СберБанк, Тинькофф, Газпромбанк
  • genC2Heartbeat(): respuesta de latido C2 realista con agent_id, group, version
  • genScannerPath(): trampas para escáneres específicos (puertos nmap NICE, Hazelcast, CGI)
  • genChineseApi(): respuestas de API chinas con msg: "成功" (éxito)
  • genMerchant(): perfiles de comerciantes con balance, fee_rate, tier

Cómo usarlo

Instalación

npm install express-middleware-honeypot

Uso básico con autorregistro

const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

Con app.use() (middleware comodín único)

const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// Tus verdaderas rutas primero
app.get("/", (req, res) => res.send("Home"));

// Después el honeypot comodín
const instance = createHoneypot({ /* options */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

Respuestas completas vs. por defecto

// Respuestas detalladas con páginas HTML completas
const instance = createHoneypot({ isCompleteResponses: true });

Registro del tráfico

const instance = createHoneypot({ logTraffic: true });
// Consulta el archivo traffic.txt para ver qué piden los bots

Conclusión

Este middleware honeypot Express va mucho más allá de un simple señuelo. Con 328 endpoints, una generación al vuelo, un spoofing de cabeceras dinámico, un registro del tráfico e incluso un proxy PHP para verdaderas respuestas WordPress, lo diseñé para quedar lo más realista posible.

Los escáneres pierden tiempo en páginas de administración ficticias, archivos .env creíbles y páginas de phishing bancario mientras tu aplicación real queda protegida. Aprecio en particular el sistema de doble variante y la clasificación inteligente: cada respuesta queda fresca y adaptada a la ruta pedida.

El código fuente está disponible en GitHub: https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

Construí um honeypot Express ultrarrealista

328 endpoints falsos com respostas geradas em tempo real, spoofing de cabeçalhos, logging de tráfego bot, mergulho no coração de um middleware honeypot Express feito para enganar scanners.

TL;DR

Criei um middleware Express que expõe 328 endpoints falsos à escolha entre duas variantes : default (respostas sóbrias mas críveis) e complete (respostas bem detalhadas). Cada resposta é gerada em tempo real com timestamps e identificadores de requisição novos, o que torna cada resposta única. Não há nenhum arquivo mock no disco. O middleware também pode registrar o tráfego em um arquivo texto e expor as rotas não tratadas em /newBotsRoute para analisar o que os bots procuram.

É bem mais do que um simples retorno de 404. Projetei essa peça de engenharia para parecer um servidor de verdade, para os scanners perderem tempo nela. Vou mostrar como funciona.

O problema

Bots de segurança, scrapers e agentes maliciosos escaneiam servidores web sem parar em busca de rotas comuns :

  • .env, wp-config.php, admin/, phpinfo.php, etc.
  • endpoints de API como /api/version, /api/config
  • arquivos de configuração : dump.sql, .git/config, docker-compose.yml
  • páginas de phishing bancário : /lander/sberbank*, /index_sber.php

Vi esse limite nos meus próprios servidores : devolver um 404 ou 403 indica na hora ao scanner que a rota não existe, então ele passa para a próxima. Escolhi o inverso com este honeypot : fazer o scanner acreditar que tudo existe, para fazê-lo perder tempo em respostas falsas enquanto seu servidor de verdade fica protegido atrás das suas rotas conhecidas.

A arquitetura

1. Um classificador, não uma tabela de lookup

A peça principal é uma função classify() em src/services/mockupGenerator.ts que recebe um caminho de endpoint e devolve um gerador de resposta. Construí como uma cascata de regras :

  1. Correspondências exatas : os endpoints mais comuns têm geradores dedicados
  2. Correspondências por padrão : regex no caminho para detectar padrões como id_rsa, wp-admin, sitemap.xml
  3. Classificação inteligente : caminhos como /262LBNFp (6+ caracteres aleatórios) são classificados como "batimentos de coração C2"
  4. Regras genéricas : caminhos de API, arquivos .php, .html, .js, .json e .asmx têm geradores genéricos
  5. Catchall final : genCatchall() produz uma resposta JSON genérica com metadados

Gosto dessa abordagem porque as respostas ficam novas a cada requisição : nenhum arquivo estático, nenhum cache, cada resposta carrega seu próprio timestamp como se viesse de um servidor de verdade.

2. O sistema de variante dupla

Cada gerador suporta duas variantes :

// Variantes de resposta : concisa ou detalhada
type Variant = "default" | "complete";
  • default : respostas concisas mas críveis. Exemplo para .env :

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete : respostas bem detalhadas. Exemplo para .env :

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

A variante complete inclui também páginas HTML completas com CSS estilizado : páginas de administração, telas de login WordPress, páginas de phishing bancário em russo, etc. Acho imbatível quando você quer mesmo ocupar um scanner.

3. Geração em tempo real

Uso Date.now() e Date().toISOString() para produzir timestamps e identificadores de requisição únicos a cada chamada. Eis a função de enriquecimento :

// Enriquece a resposta com timestamp e versão novos
function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

Para respostas JSON, o middleware tenta interpretar e enriquecer com esses campos. Respostas não JSON são devolvidas como estão.

4. O middleware de spoofing de cabeçalhos

Adicionei um middleware dedicado que define cabeçalhos HTTP realistas conforme a extensão do arquivo pedido :

// Cabeçalhos realistas + X-Powered-By conforme a extensão
res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// X-Powered-By realista baseado na extensão do arquivo
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

Isso deixa as respostas bem mais críveis : um endpoint .php com X-Powered-By: PHP/8.1.12 e Server: nginx/1.24.0 parece exatamente um servidor WordPress de verdade. Faço questão desse detalhe porque muitas vezes é o cabeçalho que entrega um servidor falso.

5. O PHP Spoofer

O phpSpoofer continua sendo meu componente favorito. Se chega uma requisição para um arquivo .php e o host é localhost, o middleware faz proxy da requisição para seu servidor PHP local, que processa e devolve uma página PHP de verdade. Se o host é remoto, devolve um 404 para bloquear qualquer tentativa de SSRF.

// Proxy para PHP local em localhost, 404 em host remoto (anti-SSRF)
const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // devolve a resposta PHP real
  } else {
    res.status(404).send("404 Not Found");
  }
};

Isso permite, quando você roda WordPress localmente, ter o honeypot servindo páginas WordPress de verdade aos bots : o máximo de realismo na minha experiência.

6. Logging e análise de tráfego

Separei duas responsabilidades : o TrafficService registra cada requisição em um arquivo JSON-lines. O UnhandledRoutesService analisa depois esses logs para identificar os caminhos pedidos pelos bots que ainda faltam cobrir no honeypot.

// Expõe as rotas pedidas pelos bots ainda não cobertas pelo honeypot
app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

Adoro esse ciclo de feedback : quanto mais você deixa o honeypot rodar, mais aprende o que os bots procuram, e pode adicionar esses caminhos aos seus endpoints conhecidos.

7. Endpoints conhecidos e exclusão

O middleware aceita listas de knownPaths, knownPatterns, knownApiPaths e knownApiPatterns. Toda rota que bate com elas atravessa o middleware para sua aplicação de verdade. Acho esse filtro indispensável : seus endpoints reais ficam intactos, o honeypot nunca os intercepta.

// Rota conhecida atravessa o honeypot para a aplicação real
const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

Os 328 endpoints em detalhe

Veja como são classificados (trecho do classificador) :

Categoria Exemplos de endpoints
Vazamentos de credentials .env, secrets.json, aws/credentials, etc/shadow
Chaves SSH .ssh/id_rsa, .ssh/id_ed25519
Configs de banco de dados config/database, wp-config.php, docker-compose.yml
Painéis de administração /admin, /wp-admin, /manage/account/login
Respostas API /api/version, /api/config, .do, .ashx
Phishing bancário /lander/sber*, /index_sber.php
Batimentos de coração C2 Caminhos aleatórios de 6+ caracteres (/262LBNFp)
Ações/Bolsa /stock/mzhishu, /kline/1m/1, /m/allticker/1
Jogos/Cassino /proxy/games, /Ctrls/GetSysCoin, /room/getRoomBangFans
Arquivos de configuração config.json, config.yml, sitemap.xml, ads.txt
Landing pages /about, /contact, /products, /blog

Códigos e endpoints especiais

Adicionei geradores especializados para casos específicos :

  • genLanding() : páginas de phishing bancário em russo para СберБанк, Тинькофф, Газпромбанк
  • genC2Heartbeat() : resposta de batimento de coração C2 realista com agent_id, group, version
  • genScannerPath() : armadilhas para scanners específicos (portas nmap NICE, Hazelcast, CGI)
  • genChineseApi() : respostas de API chinesas com msg: "成功" (sucesso)
  • genMerchant() : perfis de comerciantes com balance, fee_rate, tier

Como usar

Instalação

# Instala o middleware honeypot
npm install express-middleware-honeypot

Uso básico com autorregistro

// Cria o honeypot com rotas conhecidas e registra no app Express
const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

Com app.use() (middleware genérico único)

// Honeypot genérico depois das rotas reais
const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// Suas rotas reais primeiro
app.get("/", (req, res) => res.send("Home"));

// Depois o honeypot genérico
const instance = createHoneypot({ /* options */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

Respostas completas vs. padrão

// Respostas detalhadas com páginas HTML completas
const instance = createHoneypot({ isCompleteResponses: true });

Logging de tráfego

// Ativa o logging : ver traffic.txt para o que os bots pedem
const instance = createHoneypot({ logTraffic: true });
// Consulte o arquivo traffic.txt para ver o que os bots pedem

Conclusão

Esse middleware honeypot Express vai bem além de uma simples isca. Com 328 endpoints, geração em tempo real, spoofing dinâmico de cabeçalhos, logging de tráfego e até um proxy PHP para respostas WordPress de verdade, projetei para ficar o mais realista possível.

Os scanners perdem tempo em páginas de administração falsas, arquivos .env críveis e páginas de phishing bancário enquanto sua aplicação de verdade fica protegida. Gosto especialmente do sistema de variante dupla e da classificação inteligente : cada resposta fica nova e adaptada ao caminho pedido.

O código-fonte está no GitHub : https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

Saya membangun honeypot Express yang sangat realistis

328 endpoint palsu dengan respons yang dibangkitkan langsung, spoofing header, logging trafik bot -- menyelami middleware honeypot Express yang dirancang untuk mengecoh pemindai.

TL;DR

Saya membuat middleware Express yang mengekspos 328 endpoint tiruan dengan pilihan dua varian : default (respons singkat tapi kredibel) dan complete (respons sangat detail). Setiap respons dibangkitkan langsung dengan timestamp dan ID permintaan yang segar, sehingga setiap respons unik. Tidak ada berkas mock di disk. Middleware juga bisa mencatat trafik ke berkas teks dan mengekspos rute yang tidak tertangani di /newBotsRoute untuk menganalisis apa yang dicari bot.

Ini jauh lebih dari sekadar mengembalikan 404. Saya merancang karya rekayasa ini agar menyerupai server sungguhan, supaya pemindai membuang waktu di atasnya. Saya tunjukkan cara kerjanya.

Masalahnya

Bot keamanan, scraper, dan pelaku jahat terus-menerus memindai server web mencari rute umum :

  • .env, wp-config.php, admin/, phpinfo.php, dll.
  • endpoint API seperti /api/version, /api/config
  • berkas konfigurasi : dump.sql, .git/config, docker-compose.yml
  • halaman phishing bank : /lander/sberbank*, /index_sber.php

Saya melihat batas ini di server saya sendiri : mengembalikan 404 atau 403 langsung memberi tahu pemindai bahwa rute tidak ada, jadi ia lanjut ke berikutnya. Saya memilih kebalikannya dengan honeypot ini : membuat pemindai percaya semuanya ada, supaya ia kehilangan waktu pada respons palsu sementara server aslimu tetap terlindungi di balik rute yang kamu kenal.

Arsitekturnya

1. Pengklasifikasi, bukan tabel lookup

Bagian utamanya adalah fungsi classify() di src/services/mockupGenerator.ts yang menerima path endpoint dan mengembalikan pembangkit respons. Saya membangunnya sebagai kaskade aturan :

  1. Kecocokan persis : endpoint paling umum punya pembangkit khusus
  2. Kecocokan pola : regex pada path untuk mendeteksi pola seperti id_rsa, wp-admin, sitemap.xml
  3. Klasifikasi cerdas : path seperti /262LBNFp (6+ karakter acak) diklasifikasikan sebagai "detak jantung C2"
  4. Aturan sapu bersih : path API, berkas .php, .html, .js, .json dan .asmx punya pembangkit generik
  5. Catchall terakhir : genCatchall() menghasilkan respons JSON generik dengan metadata

Saya suka pendekatan ini karena respons tetap segar di setiap permintaan : tidak ada berkas statis, tidak ada caching, setiap respons membawa timestamp sendiri seolah berasal dari server sungguhan.

2. Sistem varian ganda

Setiap pembangkit mendukung dua varian :

type Variant = "default" | "complete";
  • default : respons ringkas tapi kredibel. Contoh untuk .env :

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete : respons sangat detail. Contoh untuk .env :

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

Varian complete juga menyertakan halaman HTML lengkap dengan CSS bergaya : halaman administrasi, layar login WordPress, halaman phishing bank berbahasa Rusia, dll. Menurut saya ia mematikan kalau kamu benar-benar ingin menyibukkan pemindai.

3. Pembangkitan langsung

Saya memakai Date.now() dan Date().toISOString() untuk menghasilkan timestamp dan ID permintaan unik di setiap panggilan. Berikut fungsi pengayaannya :

function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

Untuk respons JSON, middleware mencoba mem parsing dan memperkaya dengan kolom ini. Respons non-JSON dikembalikan apa adanya.

4. Middleware spoofing header

Saya menambahkan middleware khusus yang menetapkan header HTTP realistis tergantung ekstensi berkas yang diminta :

res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// X-Powered-By realistis berdasarkan ekstensi berkas
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

Ini membuat respons jauh lebih kredibel : endpoint .php dengan X-Powered-By: PHP/8.1.12 dan Server: nginx/1.24.0 tampak persis seperti server WordPress sungguhan. Saya memegang detail ini karena sering kali headerlah yang mengkhianati server palsu.

5. PHP Spoofer

phpSpoofer tetap komponen favorit saya. Kalau permintaan datang untuk berkas .php dan hostnya localhost, middleware memproxy permintaan ke server PHP lokalmu, yang memproses dan mengembalikan halaman PHP sungguhan. Kalau hostnya jarak jauh, ia mengembalikan 404 untuk memblokir upaya SSRF apa pun.

const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // mengembalikan respons PHP yang nyata
  } else {
    res.status(404).send("404 Not Found");
  }
};

Ini memungkinkan, ketika kamu menjalankan WordPress secara lokal, honeypot menyajikan halaman WordPress sungguhan kepada bot : realisme maksimal menurut pengalaman saya.

6. Pencatatan dan analisis trafik

Saya memisahkan dua tanggung jawab : TrafficService mencatat setiap permintaan ke berkas JSON-lines. UnhandledRoutesService lalu menganalisis log ini untuk mengidentifikasi path yang diminta bot yang masih perlu dicakup di honeypot.

app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

Saya suka lingkaran umpan balik ini : makin lama kamu membiarkan honeypot berjalan, makin banyak kamu belajar apa yang dicari bot, dan kamu bisa menambahkan path itu ke endpoint yang kamu kenal.

7. Endpoint dikenal dan pengecualian

Middleware menerima daftar knownPaths, knownPatterns, knownApiPaths dan knownApiPatterns. Rute apa pun yang cocok melewati middleware menuju aplikasi aslimu. Menurut saya filter ini tak tergantikan : endpoint aslimu tetap utuh, honeypot tidak pernah mencegatnya.

const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

328 endpoint secara detail

Begini klasifikasinya (kutipan dari pengklasifikasi) :

Kategori Contoh endpoint
Kebocoran kredensial .env, secrets.json, aws/credentials, etc/shadow
Kunci SSH .ssh/id_rsa, .ssh/id_ed25519
Config basis data config/database, wp-config.php, docker-compose.yml
Panel administrasi /admin, /wp-admin, /manage/account/login
Respons API /api/version, /api/config, .do, .ashx
Phishing bank /lander/sber*, /index_sber.php
Detak jantung C2 Path acak 6+ karakter (/262LBNFp)
Aksi/Bursa /stock/mzhishu, /kline/1m/1, /m/allticker/1
Game/Kasino /proxy/games, /Ctrls/GetSysCoin, /room/getRoomBangFans
Berkas konfigurasi config.json, config.yml, sitemap.xml, ads.txt
Halaman arahan /about, /contact, /products, /blog

Kode dan endpoint khusus

Saya menambahkan pembangkit khusus untuk kasus tertentu :

  • genLanding() : halaman phishing bank berbahasa Rusia untuk СберБанк, Тинькофф, Газпромбанк
  • genC2Heartbeat() : respons detak jantung C2 realistis dengan agent_id, group, version
  • genScannerPath() : jebakan untuk pemindai spesifik (port nmap NICE, Hazelcast, CGI)
  • genChineseApi() : respons API Tiongkok dengan msg: "成功" (sukses)
  • genMerchant() : profil pedagang dengan balance, fee_rate, tier

Cara menggunakannya

Instalasi

npm install express-middleware-honeypot

Penggunaan dasar dengan pendaftaran otomatis

const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

Dengan app.use() (middleware sapu bersih tunggal)

const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// Rute aslimu dulu
app.get("/", (req, res) => res.send("Home"));

// Lalu honeypot sapu bersih
const instance = createHoneypot({ /* options */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

Respons lengkap vs bawaan

// Respons detail dengan halaman HTML lengkap
const instance = createHoneypot({ isCompleteResponses: true });

Pencatatan trafik

const instance = createHoneypot({ logTraffic: true });
// Lihat berkas traffic.txt untuk melihat apa yang diminta bot

Kesimpulan

Middleware honeypot Express ini jauh melampaui umpan sederhana. Dengan 328 endpoint, pembangkitan langsung, spoofing header dinamis, pencatatan trafik bahkan proxy PHP untuk respons WordPress sungguhan, saya merancangnya agar tetap serealistis mungkin.

Pemindai kehilangan waktu pada halaman administrasi tiruan, berkas .env yang kredibel dan halaman phishing bank sementara aplikasi aslimu tetap terlindungi. Saya terutama menghargai sistem varian ganda dan klasifikasi cerdasnya : setiap respons tetap segar dan sesuai dengan path yang diminta.

Kode sumber tersedia di GitHub : https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

मैंने अति यथार्थवादी Express honeypot बनाया

तुरंत उत्पन्न उत्तरों वाले 328 नकली endpoint, हेडर spoofing, बॉट ट्रैफिक logging, स्कैनर को फँसाने के लिए डिजाइन किए Express honeypot middleware के हृदय में गोता।

TL;DR

मैंने एक Express middleware बनाया जो दो रूपों में चुनकर 328 नकली endpoint उजागर करता है : default (संयमित लेकिन विश्वसनीय उत्तर) और complete (बहुत विस्तृत उत्तर)। हर उत्तर ताजे timestamp और अनुरोध पहचान के साथ तुरंत उत्पन्न होता है, जो हर उत्तर को अद्वितीय बनाता है। डिस्क पर कोई mock फाइल नहीं है। middleware टेक्स्ट फाइल में ट्रैफिक log भी कर सकता है और बॉट क्या खोजते हैं इसका विश्लेषण करने के लिए /newBotsRoute पर अप्रबंधित route उजागर कर सकता है।

यह साधारण 404 लौटाने से कहीं ज्यादा है। मैंने इंजीनियरिंग का यह टुकड़ा असली सर्वर जैसा लगने के लिए डिजाइन किया, ताकि स्कैनर उस पर अपना समय बर्बाद करें। मैं दिखाता हूँ यह कैसे काम करता है।

समस्या

सुरक्षा बॉट, scraper और दुर्भावनापूर्ण कर्ता सामान्य route खोजने के लिए वेब सर्वर लगातार स्कैन करते हैं :

  • .env, wp-config.php, admin/, phpinfo.php, आदि।
  • /api/version, /api/config जैसे API endpoint
  • कॉन्फ़िगरेशन फाइलें : dump.sql, .git/config, docker-compose.yml
  • बैंकिंग phishing पेज : /lander/sberbank*, /index_sber.php

मैंने अपने सर्वरों पर यह सीमा देखी : 404 या 403 लौटाना स्कैनर को तुरंत बताता है कि route मौजूद नहीं है, इसलिए वह अगले पर जाता है। मैंने इस honeypot के साथ उल्टा चुना : स्कैनर को मनवाना कि सब मौजूद है, ताकि वह नकली उत्तरों पर समय खोए जबकि तुम्हारा असली सर्वर तुम्हारे ज्ञात route के पीछे सुरक्षित रहता है।

आर्किटेक्चर

1. वर्गीकरणकर्ता, lookup table नहीं

मुख्य टुकड़ा src/services/mockupGenerator.ts में classify() फंक्शन है जो endpoint पथ लेता है और उत्तर जनरेटर लौटाता है। मैंने इसे नियमों के झरने की तरह बनाया :

  1. सटीक मिलान : सबसे आम endpoint के समर्पित जनरेटर हैं
  2. पैटर्न मिलान : id_rsa, wp-admin, sitemap.xml जैसे पैटर्न पहचानने के लिए पथ पर regex
  3. बुद्धिमान वर्गीकरण : /262LBNFp जैसे पथ (6+ बेतरतीब अक्षर) "C2 हृदय धड़कन" के रूप में वर्गीकृत होते हैं
  4. सब समेट नियम : API पथ, .php, .html, .js, .json और .asmx फाइलों के सामान्य जनरेटर हैं
  5. अंतिम catchall : genCatchall() मेटाडेटा के साथ सामान्य JSON उत्तर बनाता है

मुझे यह दृष्टिकोण पसंद है क्योंकि उत्तर हर अनुरोध पर ताजे रहते हैं : कोई स्थिर फाइल नहीं, कोई caching नहीं, हर उत्तर अपना timestamp रखता है जैसे वह असली सर्वर से आया हो।

2. दोहरी रूप प्रणाली

हर जनरेटर दो रूप समर्थन करता है :

type Variant = "default" | "complete";
  • default : संक्षिप्त लेकिन विश्वसनीय उत्तर। .env के लिए उदाहरण :

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete : बहुत विस्तृत उत्तर। .env के लिए उदाहरण :

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

complete रूप में स्टाइल CSS वाले पूरे HTML पेज भी शामिल हैं : प्रशासन पेज, WordPress login स्क्रीन, रूसी में बैंकिंग phishing पेज, आदि। जब तुम सच में स्कैनर को व्यस्त रखना चाहते हो तो मुझे यह दुर्जेय लगता है।

3. तुरंत पीढ़ी

हर कॉल पर अद्वितीय timestamp और अनुरोध पहचान बनाने के लिए मैं Date.now() और Date().toISOString() इस्तेमाल करता हूँ। यहाँ समृद्धि फंक्शन है :

function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

JSON उत्तरों के लिए, middleware इन फ़ील्ड के साथ parse और समृद्ध करने की कोशिश करता है। गैर JSON उत्तर जैसे हैं वैसे लौटाए जाते हैं।

4. हेडर spoofing middleware

मैंने समर्पित middleware जोड़ा जो माँगी गई फाइल के एक्सटेंशन के अनुसार यथार्थवादी HTTP हेडर सेट करता है :

res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// फाइल एक्सटेंशन आधारित यथार्थवादी X-Powered-By
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

यह उत्तरों को कहीं ज्यादा विश्वसनीय बनाता है : X-Powered-By: PHP/8.1.12 और Server: nginx/1.24.0 वाला .php endpoint बिल्कुल असली WordPress सर्वर जैसा लगता है। मैं इस विवरण पर ध्यान देता हूँ क्योंकि अक्सर हेडर ही नकली सर्वर को धोखा देता है।

5. PHP Spoofer

phpSpoofer मेरा पसंदीदा घटक है। अगर किसी .php फाइल के लिए अनुरोध आता है और host localhost है, तो middleware अनुरोध को तुम्हारे स्थानीय PHP सर्वर की ओर proxy करता है, जो असली PHP पेज प्रोसेस और लौटाता है। अगर host दूरस्थ है, तो वह किसी भी SSRF प्रयास को रोकने के लिए 404 लौटाता है।

const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // असली PHP उत्तर लौटाता है
  } else {
    res.status(404).send("404 Not Found");
  }
};

यह अनुमति देता है, जब तुम WordPress स्थानीय चलाते हो, कि honeypot बॉट को असली WordPress पेज परोसे : मेरे अनुभव के अनुसार अधिकतम यथार्थवाद।

6. ट्रैफिक logging और विश्लेषण

मैंने दो जिम्मेदारियाँ अलग कीं : TrafficService हर अनुरोध JSON-lines फाइल में log करता है। UnhandledRoutesService फिर इन log का विश्लेषण करता है ताकि बॉट द्वारा माँगे गए पथ पहचाने जो honeypot में कवर करने बाकी हैं।

app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

मुझे यह प्रतिपुष्टि लूप पसंद है : जितना तुम honeypot चलाते हो, उतना सीखते हो कि बॉट क्या खोजते हैं, और तुम इन पथों को अपने ज्ञात endpoint में जोड़ सकते हो।

7. ज्ञात endpoint और बहिष्करण

middleware knownPaths, knownPatterns, knownApiPaths और knownApiPatterns सूचियाँ स्वीकार करता है। इनसे मेल खाने वाली हर route तुम्हारी असली application की ओर middleware पार करती है। मुझे यह फ़िल्टर अपरिहार्य लगता है : तुम्हारे असली endpoint अक्षुण्ण रहते हैं, honeypot उन्हें कभी रोकता नहीं।

const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

328 endpoint विस्तार में

वे कैसे वर्गीकृत हैं (वर्गीकरणकर्ता से अंश) :

श्रेणी endpoint उदाहरण
क्रेडेंशियल लीक .env, secrets.json, aws/credentials, etc/shadow
SSH कुंजियाँ .ssh/id_rsa, .ssh/id_ed25519
डेटाबेस कॉन्फ़िग config/database, wp-config.php, docker-compose.yml
प्रशासन पैनल /admin, /wp-admin, /manage/account/login
API उत्तर /api/version, /api/config, .do, .ashx
बैंकिंग phishing /lander/sber*, /index_sber.php
C2 हृदय धड़कन 6+ अक्षरों के बेतरतीब पथ (/262LBNFp)
क्रिया/बाजार /stock/mzhishu, /kline/1m/1, /m/allticker/1
गेम/कैसीनो /proxy/games, /Ctrls/GetSysCoin, /room/getRoomBangFans
कॉन्फ़िगरेशन फाइलें config.json, config.yml, sitemap.xml, ads.txt
लैंडिंग पेज /about, /contact, /products, /blog

विशेष कोड और endpoint

मैंने विशेष मामलों के लिए विशिष्ट जनरेटर जोड़े :

  • genLanding() : СберБанк, Тинькофф, Газпромбанк के लिए रूसी में बैंकिंग phishing पेज
  • genC2Heartbeat() : agent_id, group, version के साथ यथार्थवादी C2 हृदय धड़कन उत्तर
  • genScannerPath() : विशिष्ट स्कैनर के लिए जाल (nmap NICE पोर्ट, Hazelcast, CGI)
  • genChineseApi() : msg: "成功" (सफलता) के साथ चीनी API उत्तर
  • genMerchant() : balance, fee_rate, tier के साथ व्यापारी प्रोफाइल

इसे कैसे इस्तेमाल करें

स्थापना

npm install express-middleware-honeypot

स्व पंजीकरण के साथ बुनियादी उपयोग

const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

app.use() के साथ (एकल catchall middleware)

const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// तुम्हारी असली route पहले
app.get("/", (req, res) => res.send("Home"));

// फिर catchall honeypot
const instance = createHoneypot({ /* विकल्प */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

पूर्ण बनाम डिफ़ॉल्ट उत्तर

// पूरे HTML पेज के साथ विस्तृत उत्तर
const instance = createHoneypot({ isCompleteResponses: true });

ट्रैफिक logging

const instance = createHoneypot({ logTraffic: true });
// बॉट क्या माँगते हैं यह देखने के लिए traffic.txt फाइल देखो

निष्कर्ष

यह Express honeypot middleware साधारण चारे से कहीं आगे जाता है। 328 endpoint, तुरंत पीढ़ी, गतिशील हेडर spoofing, ट्रैफिक logging और असली WordPress उत्तरों के लिए PHP proxy तक के साथ, मैंने इसे यथासंभव यथार्थवादी रहने के लिए डिजाइन किया।

स्कैनर नकली प्रशासन पेज, विश्वसनीय .env फाइलों और बैंकिंग phishing पेजों पर समय खोते हैं जबकि तुम्हारी असली application सुरक्षित रहती है। मुझे विशेष रूप से दोहरी रूप प्रणाली और बुद्धिमान वर्गीकरण पसंद है : हर उत्तर ताजा रहता है और माँगे गए पथ के अनुकूल होता है।

स्रोत कोड GitHub पर उपलब्ध है : https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

بنيت honeypot Express واقعيا للغاية

328 endpoint زائفا مع ردود مولدة لحظيا، وتزوير ترويسات، وتسجيل ترافيك البوتات، غوص في قلب middleware honeypot لـExpress مصمم لخداع الماسحات.

TL;DR

أنشأت middleware لـExpress يعرض 328 endpoint زائفا للاختيار بين متغيرين : default (ردود رصينة لكن موثوقة) وcomplete (ردود مفصلة جدا). كل رد مولَّد لحظيا مع طوابع زمنية ومعرفات طلب جديدة، مما يجعل كل رد فريدا. لا يوجد أي ملف mock على القرص. يستطيع الـmiddleware أيضا تسجيل الترافيك في ملف نصي وعرض الطرق غير المُدارَة على /newBotsRoute لتحليل ما تبحث عنه البوتات.

إنه أكثر بكثير من مجرد إعادة 404. صممت هذه القطعة الهندسية لتشبه خادما حقيقيا، لكي تضيّع الماسحات وقتها عليها. أريك كيف تعمل.

المشكلة

بوتات الأمان والكاشطات والفاعلون الخبثاء يمسحون باستمرار خوادم الويب بحثا عن طرق شائعة :

  • .env وwp-config.php وadmin/ وphpinfo.php، إلخ.
  • نقاط API مثل /api/version و/api/config
  • ملفات إعداد : dump.sql و.git/config وdocker-compose.yml
  • صفحات تصيد مصرفي : /lander/sberbank* و/index_sber.php

رأيت هذا الحد على خوادمي الخاصة : إعادة 404 أو 403 تشير فورا للماسح أن الطريق غير موجود، فينتقل للتالي. اخترت العكس مع هذا الـhoneypot : جعل الماسح يظن أن كل شيء موجود، ليضيع وقته على ردود زائفة بينما يبقى خادمك الحقيقي محميا خلف طرقك المعروفة.

البنية

1. مصنف، لا جدول بحث

القطعة الأساسية هي دالة classify() في src/services/mockupGenerator.ts تأخذ مسار endpoint وتعيد مولد رد. بنيتها كسلسلة قواعد :

  1. مطابقات دقيقة : أكثر الـendpoints شيوعا لها مولدات مخصصة
  2. مطابقات بالنمط : regex على المسار لكشف أنماط مثل id_rsa وwp-admin وsitemap.xml
  3. تصنيف ذكي : مسارات مثل /262LBNFp (6+ حروف عشوائية) تُصنَّف "نبضات قلب C2"
  4. قواعد شاملة : مسارات API وملفات .php و.html و.js و.json و.asmx لها مولدات عامة
  5. التقاط نهائي : genCatchall() ينتج رد JSON عاما مع بيانات وصفية

أحب هذه المقاربة لأن الردود تبقى جديدة مع كل طلب : لا ملف ثابت، ولا تخزين مؤقت، كل رد يحمل طابعه الزمني الخاص كأنه من خادم حقيقي.

2. نظام المتغير المزدوج

يدعم كل مولد متغيرين :

type Variant = "default" | "complete";
  • default : ردود موجزة لكن موثوقة. مثال لـ.env :

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete : ردود مفصلة جدا. مثال لـ.env :

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

يتضمن المتغير complete أيضا صفحات HTML كاملة مع CSS منسق : صفحات إدارة وشاشات دخول WordPress وصفحات تصيد مصرفي بالروسية، إلخ. أجده مرعبا عندما تريد فعلا إشغال ماسح.

3. توليد لحظي

أستخدم Date.now() وDate().toISOString() لإنتاج طوابع زمنية ومعرفات طلب فريدة مع كل استدعاء. إليكم دالة الإثراء :

function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

لردود JSON، يحاول الـmiddleware التحليل والإثراء بهذه الحقول. الردود غير JSON تُعاد كما هي.

4. middleware تزوير الترويسات

أضفت middleware مخصصا يضبط ترويسات HTTP واقعية حسب امتداد الملف المطلوب :

res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// X-Powered-By واقعي حسب امتداد الملف
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

وهذا يجعل الردود أكثر موثوقية : endpoint بصيغة .php مع X-Powered-By: PHP/8.1.12 وServer: nginx/1.24.0 يشبه تماما خادم WordPress حقيقي. أتمسك بهذا التفصيل لأن الترويسة هي غالبا ما تخون خادما زائفا.

5. مُزوِّر PHP

يبقى phpSpoofer مكوني المفضل. إذا وصل طلب لملف .php وكان المضيف localhost، يوكّل الـmiddleware الطلب نحو خادم PHP المحلي الخاص بك، الذي يعالج ويعيد صفحة PHP حقيقية. وإذا كان المضيف بعيدا، يعيد 404 لحظر أي محاولة SSRF.

const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // يعيد رد PHP الحقيقي
  } else {
    res.status(404).send("404 Not Found");
  }
};

وهذا يسمح، عندما تشغّل WordPress محليا، أن يخدم الـhoneypot البوتات بصفحات WordPress حقيقية : أقصى واقعية حسب تجربتي.

6. تسجيل الترافيك وتحليله

فصلت مسؤوليتين : TrafficService يسجل كل طلب في ملف JSON-lines. ثم يحلل UnhandledRoutesService هذه السجلات لتحديد المسارات التي تطلبها البوتات والتي تبقى لتُغطَّى في الـhoneypot.

app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

أعشق حلقة التغذية الراجعة هذه : كلما تركت الـhoneypot يعمل، تعلمت أكثر عما تبحث عنه البوتات، وتستطيع إضافة هذه المسارات إلى endpoints المعروفة لديك.

7. Endpoints معروفة واستثناء

يقبل الـmiddleware قوائم knownPaths وknownPatterns وknownApiPaths وknownApiPatterns. كل طريق يطابقها يعبر الـmiddleware نحو تطبيقك الحقيقي. أجد هذا المرشح لا غنى عنه : تبقى endpoints الحقيقية سليمة، ولا يعترضها الـhoneypot أبدا.

const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

الـ328 endpoint بالتفصيل

إليكم كيف تُصنَّف (مقتطف من المصنف) :

الفئة أمثلة endpoints
تسريبات بيانات اعتماد .env وsecrets.json وaws/credentials وetc/shadow
مفاتيح SSH .ssh/id_rsa و.ssh/id_ed25519
إعدادات قواعد بيانات config/database وwp-config.php وdocker-compose.yml
لوحات إدارة /admin و/wp-admin و/manage/account/login
ردود API /api/version و/api/config و.do و.ashx
تصيد مصرفي /lander/sber* و/index_sber.php
نبضات قلب C2 مسارات عشوائية من 6+ حروف (/262LBNFp)
أفعال/بورصة /stock/mzhishu و/kline/1m/1 و/m/allticker/1
ألعاب/كازينو /proxy/games و/Ctrls/GetSysCoin و/room/getRoomBangFans
ملفات إعداد config.json وconfig.yml وsitemap.xml وads.txt
صفحات هبوط /about و/contact و/products و/blog

رموز وendpoints خاصة

أضفت مولدات متخصصة لحالات خاصة :

  • genLanding() : صفحات تصيد مصرفي بالروسية لـСберБанк وТинькофф وГазпромбанк
  • genC2Heartbeat() : رد نبضة قلب C2 واقعي مع agent_id وgroup وversion
  • genScannerPath() : فخاخ لماسحات بعينها (منافذ nmap NICE وHazelcast وCGI)
  • genChineseApi() : ردود API صينية مع msg: "成功" (نجاح)
  • genMerchant() : ملفات تجار مع balance وfee_rate وtier

كيفية الاستخدام

التثبيت

npm install express-middleware-honeypot

استخدام أساسي مع تسجيل تلقائي

const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

مع app.use() (middleware شامل وحيد)

const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// طرقك الحقيقية أولا
app.get("/", (req, res) => res.send("Home"));

// ثم الـhoneypot الشامل
const instance = createHoneypot({ /* options */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

ردود كاملة ضد افتراضية

// ردود مفصلة مع صفحات HTML كاملة
const instance = createHoneypot({ isCompleteResponses: true });

تسجيل الترافيك

const instance = createHoneypot({ logTraffic: true });
// راجع ملف traffic.txt لترى ما تطلبه البوتات

الخلاصة

يتجاوز middleware الـhoneypot في Express هذا مجرد شرك بسيط. مع 328 endpoint وتوليد لحظي وتزوير ترويسات ديناميكي وتسجيل ترافيك وحتى وكيل PHP لردود WordPress حقيقية، صممته ليبقى واقعيا قدر الإمكان.

تضيّع الماسحات وقتها على صفحات إدارة زائفة وملفات .env موثوقة وصفحات تصيد مصرفي بينما يبقى تطبيقك الحقيقي محميا. أقدّر خصوصا نظام المتغير المزدوج والتصنيف الذكي : كل رد يبقى جديدا وملائما للمسار المطلوب.

الكود المصدري متاح على GitHub : https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

Tôi đã xây một honeypot Express siêu thực

328 endpoint giả với phản hồi sinh tức thì, giả mạo header, ghi log lưu lượng bot -- lặn sâu vào một middleware honeypot Express thiết kế để đánh lừa scanner.

TL;DR

Tôi đã tạo một middleware Express phơi bày 328 endpoint giả tùy chọn giữa hai biến thể : default (phản hồi gọn nhưng đáng tin) và complete (phản hồi rất chi tiết). Mỗi phản hồi được sinh tức thì với timestamp và định danh yêu cầu mới, khiến mỗi phản hồi là duy nhất. Không có tệp mock nào trên đĩa. Middleware còn có thể ghi log lưu lượng vào tệp văn bản và phơi bày các route chưa xử lý trên /newBotsRoute để phân tích bot đang tìm gì.

Đây không chỉ là trả về 404 đơn thuần. Tôi thiết kế mẩu kỹ thuật này để giống máy chủ thật, để scanner phí thời gian vào đó. Tôi sẽ cho bạn thấy nó hoạt động thế nào.

Vấn đề

Bot bảo mật, scraper và tác nhân độc hại liên tục quét máy chủ web để tìm các route phổ biến :

  • .env, wp-config.php, admin/, phpinfo.php, v.v.
  • endpoint API như /api/version, /api/config
  • tệp cấu hình : dump.sql, .git/config, docker-compose.yml
  • trang phishing ngân hàng : /lander/sberbank*, /index_sber.php

Tôi đã thấy giới hạn này trên chính máy chủ của mình : trả về 404 hay 403 báo ngay cho scanner rằng route không tồn tại, nên nó chuyển sang route tiếp theo. Tôi chọn ngược lại với honeypot này : khiến scanner tin rằng mọi thứ tồn tại, để nó mất thời gian vào phản hồi giả trong khi máy chủ thật của bạn vẫn được bảo vệ sau các route đã biết của bạn.

Kiến trúc

1. Một bộ phân loại, không phải bảng tra

Mảnh trung tâm là hàm classify() trong src/services/mockupGenerator.ts nhận đường dẫn endpoint và trả về bộ sinh phản hồi. Tôi xây nó như thác quy tắc :

  1. Khớp chính xác : các endpoint phổ biến nhất có bộ sinh riêng
  2. Khớp theo mẫu : regex trên đường dẫn để phát hiện mẫu như id_rsa, wp-admin, sitemap.xml
  3. Phân loại thông minh : đường dẫn như /262LBNFp (6+ ký tự ngẫu nhiên) được phân loại là "nhịp tim C2"
  4. Quy tắc vét : đường dẫn API, tệp .php, .html, .js, .json và .asmx có bộ sinh chung
  5. Catchall cuối : genCatchall() sinh phản hồi JSON chung kèm metadata

Tôi thích cách tiếp cận này vì phản hồi vẫn tươi mỗi yêu cầu : không tệp tĩnh, không cache, mỗi phản hồi mang timestamp riêng như thể tới từ máy chủ thật.

2. Hệ thống hai biến thể

Mỗi bộ sinh hỗ trợ hai biến thể :

type Variant = "default" | "complete";
  • default : phản hồi súc tích nhưng đáng tin. Ví dụ cho .env :

    DB_HOST=localhost
    DB_USER=root
    DB_PASS=s3cur3P@ss
    APP_ENV=development
    APP_DEBUG=true
    
  • complete : phản hồi rất chi tiết. Ví dụ cho .env :

    # Database
    DB_CONNECTION=mysql
    DB_HOST=db-master-01.internal.example.com
    DB_PORT=3306
    DB_DATABASE=production_app_v2
    DB_USER=admin_service
    DB_PASS=mM9k#2$xL!qR7pZ
    
    # Redis
    REDIS_HOST=redis-cluster-01.internal.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=R3d!s_S3cur3_K3y_2025
    
    # App
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://admin.internal.example.com
    APP_KEY=base64:qJ3fR8mL2pX5vB7nC4kA9wE1yH6sD0tG
    ...
    

Biến thể complete còn gồm các trang HTML đầy đủ với CSS tạo kiểu : trang quản trị, màn hình đăng nhập WordPress, trang phishing ngân hàng tiếng Nga, v.v. Tôi thấy nó đáng gờm khi bạn thực sự muốn giữ chân scanner.

3. Sinh tức thì

Tôi dùng Date.now() và Date().toISOString() để tạo timestamp và định danh yêu cầu duy nhất mỗi lần gọi. Đây là hàm làm giàu :

function enrichResponse(response: Record<string, unknown>): Record<string, unknown> {
  return {
    ...response,
    timestamp: getTimestamp(),
    version: "1.0",
    lastUpdated: getTimestamp(),
  };
}

Với phản hồi JSON, middleware cố parse và làm giàu bằng các trường này. Phản hồi không phải JSON được trả nguyên.

4. Middleware giả mạo header

Tôi thêm một middleware riêng đặt các header HTTP thực tế tùy theo phần mở rộng tệp được yêu cầu :

res.setHeader("Server", "nginx/1.24.0");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("X-XSS-Protection", "1; mode=block");
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Robots-Tag", "noindex, nofollow");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");

// X-Powered-By thực tế dựa trên phần mở rộng tệp
if (path.endsWith(".php")) res.setHeader("X-Powered-By", "PHP/8.1.12");
else if (path.endsWith(".jsp")) res.setHeader("X-Powered-By", "JSP/3.0");
else if (path.endsWith(".aspx") || path.endsWith(".ashx") || path.endsWith(".asmx"))
  res.setHeader("X-Powered-By", "ASP.NET");
else if (path.endsWith(".do") || path.endsWith(".action"))
  res.setHeader("X-Powered-By", "Servlet/3.0");

Việc này khiến phản hồi đáng tin hơn nhiều : một endpoint .php với X-Powered-By: PHP/8.1.12 và Server: nginx/1.24.0 trông đúng như máy chủ WordPress thật. Tôi coi trọng chi tiết này vì thường chính header tố cáo một máy chủ giả.

5. PHP Spoofer

phpSpoofer vẫn là thành phần tôi thích nhất. Nếu yêu cầu tới cho tệp .php và host là localhost, middleware proxy yêu cầu tới máy chủ PHP cục bộ của bạn, nơi xử lý và trả về trang PHP thật. Nếu host ở xa, nó trả 404 để chặn mọi mưu toan SSRF.

const phpSpoofer = async (req, res, next) => {
  if (!req.path?.match(/\.php$/)) return next();
  const host = req.headers?.host;
  if (host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]")) {
    const baseUrl = req.originalUrl.replace(/\.php.*$/, "");
    const response = await fetch(`http://${host}${baseUrl}`);
    // trả về phản hồi PHP thật
  } else {
    res.status(404).send("404 Not Found");
  }
};

Nhờ vậy, khi bạn chạy WordPress cục bộ, honeypot phục vụ cho bot các trang WordPress thật : mức thực tế tối đa theo kinh nghiệm của tôi.

6. Ghi log và phân tích lưu lượng

Tôi tách hai trách nhiệm : TrafficService ghi mỗi yêu cầu vào tệp JSON-lines. UnhandledRoutesService sau đó phân tích các log này để nhận diện đường dẫn bot yêu cầu mà honeypot chưa phủ.

app.get("/newBotsRoute", async (_req, res) => {
  const routes = await unhandledRoutesService.getUnhandledRoutes(
    additionalEndpoints, knownPathOptions
  );
  res.setHeader("Content-Type", "text/plain");
  res.send(routes.join("\n"));
});

Tôi rất thích vòng phản hồi này : càng để honeypot chạy lâu, bạn càng học được bot đang tìm gì, và có thể thêm các đường dẫn đó vào endpoint đã biết của bạn.

7. Endpoint đã biết và loại trừ

Middleware chấp nhận danh sách knownPaths, knownPatterns, knownApiPaths và knownApiPatterns. Mọi route khớp với chúng đi xuyên middleware tới ứng dụng thật của bạn. Tôi thấy bộ lọc này không thể thiếu : các endpoint thật của bạn còn nguyên, honeypot không bao giờ chặn chúng.

const matches = (p) => p === endpoint || p.startsWith(endpoint + "/");

328 endpoint chi tiết

Đây là cách chúng được phân loại (trích từ bộ phân loại) :

Danh mục Ví dụ endpoint
Rò rỉ credential .env, secrets.json, aws/credentials, etc/shadow
Khóa SSH .ssh/id_rsa, .ssh/id_ed25519
Cấu hình cơ sở dữ liệu config/database, wp-config.php, docker-compose.yml
Bảng quản trị /admin, /wp-admin, /manage/account/login
Phản hồi API /api/version, /api/config, .do, .ashx
Phishing ngân hàng /lander/sber*, /index_sber.php
Nhịp tim C2 Đường dẫn ngẫu nhiên 6+ ký tự (/262LBNFp)
Hành động/Chứng khoán /stock/mzhishu, /kline/1m/1, /m/allticker/1
Game/Casino /proxy/games, /Ctrls/GetSysCoin, /room/getRoomBangFans
Tệp cấu hình config.json, config.yml, sitemap.xml, ads.txt
Trang đích /about, /contact, /products, /blog

Mã và endpoint đặc biệt

Tôi thêm các bộ sinh chuyên biệt cho trường hợp riêng :

  • genLanding() : trang phishing ngân hàng tiếng Nga cho СберБанк, Тинькофф, Газпромбанк
  • genC2Heartbeat() : phản hồi nhịp tim C2 thực tế với agent_id, group, version
  • genScannerPath() : bẫy cho scanner cụ thể (cổng nmap NICE, Hazelcast, CGI)
  • genChineseApi() : phản hồi API Trung Quốc với msg: "成功" (thành công)
  • genMerchant() : hồ sơ thương nhân với balance, fee_rate, tier

Cách dùng

Cài đặt

npm install express-middleware-honeypot

Dùng cơ bản với tự đăng ký

const { createHoneypot } = require("express-middleware-honeypot");

const app = express();
const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);
app.listen(3000);

Với app.use() (middleware vét duy nhất)

const { createHoneypot } = require("express-middleware-honeypot");
const app = express();

// Các route thật của bạn trước
app.get("/", (req, res) => res.send("Home"));

// Rồi tới honeypot vét
const instance = createHoneypot({ /* options */ });
app.use(instance.headersMiddleware);
app.use(instance.middleware);

Phản hồi đầy đủ so với mặc định

// Phản hồi chi tiết với trang HTML đầy đủ
const instance = createHoneypot({ isCompleteResponses: true });

Ghi log lưu lượng

const instance = createHoneypot({ logTraffic: true });
// Xem tệp traffic.txt để thấy bot yêu cầu gì

Kết luận

Middleware honeypot Express này đi xa hơn nhiều so với mồi nhử đơn thuần. Với 328 endpoint, sinh tức thì, giả mạo header động, ghi log lưu lượng và thậm chí proxy PHP cho phản hồi WordPress thật, tôi thiết kế nó để thực tế nhất có thể.

Scanner mất thời gian vào trang quản trị giả, tệp .env đáng tin và trang phishing ngân hàng trong khi ứng dụng thật của bạn vẫn được bảo vệ. Tôi đặc biệt thích hệ thống hai biến thể và phân loại thông minh : mỗi phản hồi vẫn tươi mới và hợp với đường dẫn được yêu cầu.

Mã nguồn có trên GitHub : https://github.com/fox3000foxy/express-honeypot-middleware

✨ AI Generated Article

ฉันสร้าง honeypot Express ที่สมจริงอย่างยิ่ง

328 ปลายทางปลอมพร้อมการตอบสนองที่สร้างขึ้นทันที การปลอมแปลงส่วนหัว การบันทึกทราฟฟิกบอท -- เจาะลึกโค้ดของมิดเดิลแวร์ honeypot Express ที่ออกแบบมาเพื่อหลอกลวงสแกนเนอร์

แนวคิด

บอทและสแกนเนอร์อัตโนมัติสแกนเว็บแอปพลิเคชันอย่างต่อเนื่องเพื่อหาช่องโหว่ พวกมันมองหาไฟล์ .env แผงผู้ดูแลระบบ การสำรองฐานข้อมูล ข้อมูลประจำตัว SSH -- อะไรก็ตามที่สามารถนำไปใช้ประโยชน์ได้

แทนที่จะส่งคืน 404 ธรรมดา ฉันต้องการสร้างสิ่งที่สนุกกว่า: honeypot Express ที่ตอบสนองด้วยเนื้อหาที่น่าเชื่อถือ ทำให้ผู้โจมตีเชื่อว่าพวกเขาพบเป้าหมายที่มีช่องโหว่

ความสามารถ

มิดเดิลแวร์นี้เปิดเผย 328 ปลายทาง แบ่งเป็นสองรูปแบบ (ค่าเริ่มต้นและสมบูรณ์) แต่ละคำขอจะได้รับการตอบสนองที่ไม่ซ้ำกันซึ่งสร้างขึ้นทันทีพร้อมประทับเวลาและ ID คำขอใหม่ เลียนแบบเซิร์ฟเวอร์จริง

เริ่มต้นใช้งาน

npm install express-middleware-honeypot

การใช้งานพื้นฐานกับการลงทะเบียนอัตโนมัติ:

const express = require("express");
const { createHoneypot } = require("express-middleware-honeypot");

const app = express();

const instance = createHoneypot({
    knownPaths: ["/", "/login", "/support"],
    knownPatterns: [/^\/blogs\/[^/]+$/],
    knownApiPaths: ["/api/cart", "/api/cart/list"],
    knownApiPatterns: [/^\/api\/cart\/[^/]+$/],
    logTraffic: true,
    is404Handler: true,
    isCompleteResponses: false,
});

instance.register(app);

app.listen(3000, () => {
    console.log("เซิร์ฟเวอร์กำลังทำงานบนพอร์ต 3000");
});

วิธีการทำงาน

การสร้างทันที

ไม่มีไฟล์จำลองบนดิสก์ บริการ mockupGenerator.ts สร้างการตอบสนองแต่ละรายการ ณ เวลาที่คำขอด้วย:

  • ประทับเวลาและ ID คำขอที่ไม่ซ้ำกัน
  • เนื้อหาที่ปรับให้เหมาะกับปลายทาง (ข้อมูลประจำตัว การกำหนดค่า หน้าเข้าสู่ระบบ การตอบสนอง API)
  • ส่วนหัว HTTP ที่สมจริงพร้อมการปลอมแปลง X-Powered-By แบบไดนามิก

การปลอมแปลงส่วนหัว

headersMiddleware เลือกส่วนหัว X-Powered-By แบบไดนามิกตามนามสกุลของเส้นทาง:

  • .php → X-Powered-By: PHP/8.1.12
  • .jsp → X-Powered-By: JSP/3.0
  • .aspx/.ashx/.asmx → X-Powered-By: ASP.NET
  • .do/.action → X-Powered-By: Servlet/3.0
  • เส้นทางอื่น → ไม่มีส่วนหัว X-Powered-By

328 ปลายทาง

ประเภท ตัวอย่างปลายทาง
การรั่วไหลของข้อมูลประจำตัว .env, secrets.json, aws/credentials, etc/shadow
คีย์ SSH .ssh/id_rsa, .ssh/id_ed25519
การกำหนดค่าฐานข้อมูล config/database, wp-config.php, docker-compose.yml
แผงผู้ดูแลระบบ /admin, /wp-admin, /manage/account/login
การตอบสนอง API /api/version, /api/config, .do, .ashx
ฟิชชิ่งธนาคาร /lander/sber*, /index_sber.php
การส่งสัญญาณ C2 เส้นทางสุ่ม 6+ ตัวอักษร (/262LBNFp, /Kd67Fq1x)
หุ้น/คริปโต /stock/mzhishu, /kline/1m/1, /m/allticker/1
การพนัน/เกม /proxy/games, /Ctrls/GetSysCoin, /room/getRoomBangFans
ไฟล์กำหนดค่า config.json, config.yml, sitemap.xml, ads.txt
หน้าแลนดิ้ง /about, /contact, /products, /blog

การปลอมแปลง PHP

instance.phpSpoofer ดักจับคำขอ *.php และส่งต่อพร็อกซีไปยังเซิร์ฟเวอร์พัฒนาท้องถิ่นของคุณ โดยส่งคืนผลลัพธ์ PHP จริงแทนการจำลองแบบคงที่

การบันทึกทราฟฟิก

ทราฟฟิกสามารถบันทึกในรูปแบบ JSON-lines ไปยัง traffic.txt เส้นทางที่ไม่รู้จักซึ่งไม่ได้รับการจัดการสามารถดึงข้อมูลผ่าน GET /newBotsRoute

API ของ HoneypotInstance

interface HoneypotInstance {
  mocks: Record<string, Middleware>;
  middleware: Middleware;
  headersMiddleware: Middleware;
  phpSpoofer: Middleware;
  notFoundHandler: Middleware;
  register(app: RouteApp): void;
  getUnhandledRoutes(): Promise<string[]>;
  getNotCoveredEndpoints(): string[];
}

ทำไมถึงมีประสิทธิภาพ

สแกนเนอร์อัตโนมัติคาดหวังให้เว็บไซต์ที่มีช่องโหว่มีไฟล์บางอย่าง โดยการตอบสนองด้วยเนื้อหาจริงแทน 404 honeypot สามารถ:

  1. ทำให้ผู้โจมตีเสียเวลา ในการวิเคราะห์ผลลัพธ์ปลอม
  2. บันทึกลายนิ้วมือของพวกเขา เพื่อวิเคราะห์ในภายหลัง
  3. เบี่ยงเบนความสนใจ จากช่องโหว่ที่แท้จริง
  4. เผยให้เห็นรูปแบบการโจมตีใหม่ ผ่านเส้นทางที่ไม่ได้รับการจัดการ

บทสรุป

ซอร์สโค้ดทั้งหมดพร้อมใช้งานบน GitHub ที่ github.com/anomalyco/express-honeypot-middleware ลองนำไปใช้ เปิด issue หรือมีส่วนร่วมได้เลย

Related Articles