package honeypot
import (
"html"
"net/http"
"strings"
)
// LoginPageVars 是假登录页的渲染参数。
type LoginPageVars struct {
// Action 是表单提交地址(当前诱饵路径本身)。
Action string
// Title / Heading 是页面标题与表单标题。
Title string
Heading string
// Notice 是提交失败后的提示语。
Notice string
// Redir 为原始请求的查询串(WordPress 风格的 ?redirect_to=),原样回填。
Redir string
}
// RenderLoginPage 输出仿 WordPress 后台风格的假登录页(200,text/html)。
//
// 页面刻意包含:
// - 隐藏 canary 标记(HTML 注释 + meta),便于日后溯源;
// - 经典 WP 字段名 log / pwd / redirect_to —— 让暴力破解脚本原样提交,
// 我们就能拿到最干净的用户名与密码对;
// - 一个 nginx/Apache 风格的 404 页脚,暗示「这个后台真的存在」,
// 但正文是纯静态 HTML,不泄露任何版本、路径或真实凭据。
func RenderLoginPage(vars LoginPageVars) []byte {
action := html.EscapeString(vars.Action)
title := vars.Title
if title == "" {
title = "Log In ‹ WordPress"
}
heading := vars.Heading
if heading == "" {
heading = "WordPress"
}
notice := ""
if vars.Notice != "" {
notice = `
` + html.EscapeString(vars.Notice) + `
`
}
redir := html.EscapeString(vars.Redir)
var b strings.Builder
b.Grow(4096)
b.WriteString(`` + "\n")
b.WriteString(`` + "\n")
b.WriteString(`` + "\n")
b.WriteString(`` + "\n")
b.WriteString(`` + "\n")
b.WriteString(`` + "\n")
b.WriteString(`` + "\n")
b.WriteString(`` + html.EscapeString(title) + `` + "\n")
// 静态内联样式,避免蜜罐依赖站点静态资源(也避免污染真实缓存)
b.WriteString(`` + "\n")
b.WriteString(`` + "\n")
b.WriteString(`` + "\n")
b.WriteString(`` + "\n")
b.WriteString(`
` + "\n")
b.WriteString(notice + "\n")
b.WriteString(`
` + "\n")
b.WriteString(`
Lost your password? | Register
` + "\n")
b.WriteString(`
` + "\n")
b.WriteString(`` + "\n")
b.WriteString(`` + "\n")
b.WriteString(`` + "\n")
return []byte(b.String())
}
const honeypotCSS = `html{background:#f0f0f1}body{margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;font-size:14px;color:#3c434a}
#login{border:1px solid #dcdcde;background:#fff;width:320px;margin:5em auto;padding:26px 24px 46px;box-shadow:0 1px 3px rgba(0,0,0,.04)}
#login h1{text-align:center;margin:0 0 1em;font-size:24px;font-weight:400}
#login h1 a{color:#444;text-decoration:none}
#login form{margin-top:16px}
#login label{display:block;font-size:14px}
#login input[type=text],#login input[type=password]{width:100%;padding:5px 8px;border:1px solid #ddd;border-radius:3px;box-sizing:border-box;margin-top:4px}
#login .button{width:100%;padding:7px 12px;background:#2271b1;border:1px solid #2271b1;border-radius:3px;color:#fff;cursor:pointer;font-size:13px}
#login .submit{margin:16px 0 0}
#login_error{border-left:4px solid #d63638;background:#fcf0f1;padding:10px 12px;margin:12px 0}
#nav,.forgetmenot{font-size:13px;color:#50575e}
#footer{text-align:center;color:#646970;font-size:12px;margin:24px auto;max-width:320px}
`
// NotFoundFooter 是 404 假页脚,用于给扫描器「后台存在但资源不存在」的错觉。
const NotFoundFooter = "The requested resource was not found on this server."
// LoginFailureNotice 是提交凭据后的统一提示(不区分「用户不存在」与「密码错误」,
// 避免因响应差异让爆破脚本调整策略;同时这也是虚假后台「存在」的证据)。
const LoginFailureNotice = "ERROR: Invalid username or password."
// WriteLoginPage 将假登录页写入响应。
func WriteLoginPage(w http.ResponseWriter, vars LoginPageVars) {
body := RenderLoginPage(vars)
h := w.Header()
h.Set("Content-Type", "text/html; charset=utf-8")
h.Set("Cache-Control", "no-store, no-cache, must-revalidate")
// 诱饵页面绝不进入任何共享/边缘缓存,也不被中间层改写。
h.Set("X-Robots-Tag", "noindex, nofollow")
w.WriteHeader(http.StatusOK)
_, _ = w.Write(body)
}