ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PHP实现防红短网址系统:短码生成、防红混淆与后台管理

PHP实现防红短网址系统:短码生成、防红混淆与后台管理 简介短网址生成与防红源码是一套适合Web开发者及编程学习者参考的完整项目核心解决长链接转短链、以及降低推广链接被平台屏蔽或限制访问的风险。包内共73个文件以PHP后端逻辑、CSS样式与JS交互脚本为主另含SQL数据库脚本及字体、图片等静态资源压缩包仅1.1MB结构清晰且便于部署调试。项目自带后台管理系统涵盖用户管理、网址管理、访问统计、防红策略配置与安全防护模块并演示了加密解密、代理转发、动态生成短码等关键机制能够直观看到短网址从生成到跳转的完整流程。防红部分还包含混淆算法和代理服务器设计可在不暴露原始链接的情况下完成访问转发对理解Web安全防护与链接追踪方案很有帮助。目前已有1249人浏览学习适合用来快速搭建短链服务也可作为二次开发基础在此基础上扩展API接口或优化防红策略。1. 短网址与防红源码的拆解思路某天你发给客户一个链接结果被聊天软件提示不安全而你换一个短链就能正常打开。这套短网址生成网站源码要解决的就是这个问题先用短码把长链接包起来再用防红模块让落地页在各种外链场景下尽量正常访问。源码是原生PHP没有用框架核心文件包括index.php、api.php、ip.php、hzbrm.php、show.php和houtai后台另外附了fanghong.sql建库脚本。和那些打包好的商业源码不同这套代码结构简单适合阅读和二次开发。想搞懂短链生成原理、后台权限设计或者正在做外链管理系统的PHP开发者都能从里面找到可复用的写法。2. 短网址核心实现短码生成、重定向与IP落地2.1 自增ID映射短码而不是MD5截断短网址服务的第一件事是把长URL变成一串短字符。很多人第一反应是用MD5或SHA截取前6位我建议直接放弃这条路。哈希截断会撞车概率虽然不高但一旦两个长链生成同一个短码跳转就乱了哈希没有递增关系后台想按生成时间排序、按ID恢复顺序时会很痛苦哈希值无法反解日后想统计某个长链对应哪几条短码也做不了。这套源码里的api.php接收长URL后走的是自增ID加自定义字符集映射。数据库里id每增加1短码就从字符集里取一个顺序值。我第一次打开zise.php时以为它是防红混淆后来才发现它就是短码编码器把十进制ID转成56进制以内表示。字符表里刻意去掉了0、O、1、I这些容易看错的字符生成的短码在聊天框里被人工抄写时不容易出错。function encode_id(int $id): string { // 去掉易混淆字符保证短码可读性 $charset 23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ; $base strlen($charset); // 56进制 $code ; while ($id 0) { $code $charset[$id % $base] . $code; $id intdiv($id, $base); } return $code ?: $charset[0]; // id为0时返回首位 } function decode_code(string $code): int { $charset 23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ; $base strlen($charset); $id 0; $map []; for ($i 0; $i $base; $i) { $map[$charset[$i]] $i; } $len strlen($code); for ($i 0; $i $len; $i) { $c $code[$i]; if (!isset($map[$c])) { return 0; // 字符集外字符直接判非法 } $id $id * $base $map[$c]; } return $id; }这段代码的逻辑很直白encode_id把ID逐位除以56余数映射成字符最终得到短码decode_code再按同一张字符表执行逆向运算还原出数据库主键。参数$charset是可见字符表$base是进制基数。因为去掉了6个易混字符所以是56而不是62。短码长度随ID增长很慢前几千条基本是1到2位对用户输入非常友好。光有短码还不够三种主流生成方案放一起对比就能看出这套源码为什么选自增映射生成方案优点缺点适用场景MD5/SHA截断实现简单长短固定碰撞不可控无法反解ID临时短链、不关心排序自增ID映射无碰撞、可反解、可排序ID容易被人顺序枚举后台管理型短链服务随机字符串安全不易枚举需要查重生成慢对外公开且需要防爬这套源码显然是第二种。因为houtai后台要按ID管理链接、统计点击量、设置过期时间自增映射让所有操作都落在同一个主键上管理和统计都方便。2.2 index.php的重定向与过期判断拿到短码之后真正处理入口是index.php本质就是拿code去换真实URL再做一个302跳转。注意这里强烈建议用302不要用301。301会被浏览器缓存后台改了长链接用户访问的还是旧地址排错时特别被动。下面是我按这套源码的结构重写的核心跳转逻辑对应原包里的index.php?php require db.php; // PDO连接 $code $_GET[c] ?? ; // 只放行4到10位字母数字挡住注入和异常参数 if (!preg_match(/^[a-zA-Z0-9]{4,10}$/, $code)) { include show.php; exit; } $stmt $pdo-prepare(SELECT id, url, expire_time FROM urls WHERE code ? LIMIT 1); $stmt-execute([$code]); $row $stmt-fetch(); if (!$row) { include show.php; // 找不到则进入落地页而不是直接404 exit; } if ($row[expire_time] $row[expire_time] time()) { include show.php; // 过期链接统一走提示页 exit; } // 点击量可以先同步加1量大后再拆异步队列 $pdo-prepare(UPDATE urls SET hits hits 1 WHERE id ?) -execute([$row[id]]); header(Location: . $row[url], true, 302);这段代码里正则/^[a-zA-Z0-9]{4,10}$/限定了短码的字符集和长度既能过滤异常请求也让后续SQL查询更安全。expire_time是Unix时间戳0表示永久有效非0时和当前时间对比。找不到记录时include show.php而不是直接exit是为了让访客看到提示页而不是浏览器默认错误页这一点对后面接防红策略非常关键。302跳转保留了修改长链后立即生效的能力。2.3 ip.php 获取来访IP的坑跳转前后系统都要记录访问来源源码里的ip.php专门做这件事。它优先读取REMOTE_ADDR同时兼容HTTP_X_FORWARDED_FOR但这里有坑。function client_ip(): string { // 直接来源优先不要盲目信任X-Forwarded-For $ip $_SERVER[REMOTE_ADDR] ?? 0.0.0.0; if (!empty($_SERVER[HTTP_X_FORWARDED_FOR])) { // Nginx反代场景下取第一个IP但客户端可伪造 $forwarded explode(,, $_SERVER[HTTP_X_FORWARDED_FOR]); $ip trim($forwarded[0]); } return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : 0.0.0.0; }风险点在于HTTP_X_FORWARDED_FOR这个头可以随便伪造。如果后端只认它短链的点击统计会被刷爆。常见做法是在Nginx层用real_ip模块覆盖X-Forwarded-ForPHP端只取REMOTE_ADDR。我实际部署时REMOTE_ADDR是最终依据X-Forwarded-For只作参考否则后台数据基本是脏的。到这里短链从生成、解析到记录IP的主链路已经清晰api.php负责生成index.php负责跳转show.php承担落地页ip.php提供基础数据。3. 防红模块的工程实现URL混淆、UA校验与动态转发3.1 分清防红和加密的区别防红在技术圈有各种叫法本质是一件事让链接在外链场景下尽量不被平台机械策略识别并拦截。它和加密不同加密是为保密防红是为降低链接被识别的概率。常见策略大体有三类URL混淆、UA校验、动态生成。URL混淆是把长链接参数藏起来不让平台爬虫直接从URL里看到目标域名和关键参数UA校验是区分正常浏览器和平台检测爬虫对可疑UA返回提示页而不是真实地址动态生成是不固定落地地址每次访问都生成带时效的临时参数。这套源码里的hzbrm.php就是承担混淆和还原的模块。后台开启防红策略后生成的短链接不再直接跳转原始URL而是先请求hzbrm.php再由它解密出真实地址并完成转发。如果有多台服务器常见的做法还会在跳转动作外面套一层Nginx反向代理把真实源站IP藏住但这需要额外维护一套转发层小项目可以先不引入。3.2 hzbrm.php 的混淆与还原防红模块的混淆算法不需要设计成高强度加密关键是让原始URL在传输过程中不具备明显业务特征。源码中hzbrm.php的核心逻辑类似这样function encode_url(string $url, string $key): string { // 异或混淆URL与密钥逐字节异或 $out ; $len strlen($url); $klen strlen($key); for ($i 0; $i $len; $i) { $out . chr(ord($url[$i]) ^ ord($key[$i % $klen])); } // base64转码并替换和/避免URL被截断 return rtrim(strtr(base64_encode($out), /, -_), ); } function decode_url(string $cipher, string $key): string { $cipher strtr($cipher, -_, /); $bin base64_decode($cipher); // 还原异或前的二进制 $out ; $len strlen($bin); $klen strlen($key); for ($i 0; $i $len; $i) { $out . chr(ord($bin[$i]) ^ ord($key[$i % $klen])); } return $out; }这里的$key取自后台配置项每次生成短链时我还会叠加一个随机盐salt避免同一个长URL每次都生成相同密文。encode_url返回的字符串不含/这些URL特殊符号拼进短链参数不会截断。decode_url把参数还原再交给index.php做跳转。需要明确一点这只是混淆不是强加密目的是让链接不再一眼被识别而不是对抗专业逆向。真正可靠的还是服务端配置和及时的密钥轮换。我通常在后台加一个密钥轮换配置每7天换一次key旧链接在宽限期内自动失效。3.3 防红策略配置与跳转方式选择后台的防红配置项一般包括是否启用防红、混淆密钥、UA白名单、跳转方式、链接有效时长。跳转方式有三种取舍差异很大跳转方式可被爬虫识别用户体感适用场景302高最快信任环境、企业内部JS中延迟几十毫秒投放页面、防红场景Meta Refresh中延迟约1秒兼容性优先302跳转最快但平台爬虫能直接从响应头看到目标地址JS跳转需要执行页面脚本普通用户无感但部分App内会禁用JSMeta Refresh兼容性最好代价是延迟1秒。这套源码默认用302如果想做防红我一般改成JS跳转并在页面里做UA判断。UA白名单是防红模块里容易忽略又很实用的点。代码里通常会有类似preg_match(/MicroMessenger|QQ/i, $ua)的判断对微信、QQ内置浏览器放行对可疑UA返回提示页。这不是歧视特定客户端而是平台检测爬虫的UA特征明显全部放行反而容易被特征识别。我会在后台维护一个UA正则列表交给运营人员随时增删。3.4 动态生成短链的接口设计api.php除了生成短码还要处理防红相关的动态参数。我沿用这套源码的接口风格把它设计成POST形式POST /api.php urlhttps://example.com/promo?id100tokenabc expired86400返回结构{ code: 0, msg: ok, url: http://s.xxx.com/Ab3xY }服务端生成短码后同时把原始URL用混淆算法编码进参数等用户访问短链时再解密。这样即使链接被爬取拿到也是经过混淆的加密参数不会暴露真实地址。动态生成还可以在参数里加上时间戳超过过期时间就拒绝跳转并进入show.php提示页避免固定模式被批量识别。4. 后台管理与安全houtai、fanghong.sql与注入防护4.1 houtai目录的后台功能结构这套源码的houtai目录是后台管理入口。按常见划分后台至少要有四块用户管理、网址管理、统计查看、设置配置。小项目不需要引入复杂RBAC简单session加分级权限就够用。登录后的每个页面开头都要做身份校验session_start(); if (empty($_SESSION[admin_id])) { header(Location: login.php); exit; } // 只有超管能访问设置页 $admin $pdo-prepare(SELECT role FROM admin_users WHERE id ?); $admin-execute([$_SESSION[admin_id]]); if ($admin-fetchColumn() ! super) { exit(无权限); }这里login.php是后台登录页admin_users表里保存角色字段。每个后台入口都做一次session检查能防止未登录用户直接请求管理脚本。这个代码虽然简单却挡住了大部分误访问。后台功能模块对应关系如下模块作用典型操作用户管理控制谁能登录后台添加、禁用、重置密码网址管理管理已生成的短链修改目标URL、删除、导出统计查看查看点击量、独立IP按天筛选、按短码聚合设置配置配置防红密钥、UA白名单保存配置、测试跳转4.2 fanghong.sql 核心表结构安装包里自带fanghong.sql这是全系统的地基。核心表只需两张urls和visit_logs。urls存短链映射visit_logs存访问日志。建表语句可以精简成下面这样CREATE TABLE urls ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, code VARCHAR(10) NOT NULL, url TEXT NOT NULL, expire_time INT UNSIGNED NOT NULL DEFAULT 0, hits INT UNSIGNED NOT NULL DEFAULT 0, create_time INT UNSIGNED NOT NULL, PRIMARY KEY (id), UNIQUE KEY uk_code (code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE visit_logs ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, code VARCHAR(10) NOT NULL, ip VARCHAR(45) NOT NULL, ua VARCHAR(255) NOT NULL, referer VARCHAR(255) DEFAULT , visit_time INT UNSIGNED NOT NULL, PRIMARY KEY (id), KEY idx_code (code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;code字段设置唯一索引因为短码查询是最高频的操作visit_logs的idx_code索引用于按短码聚合统计。ip字段用VARCHAR(45)而不是INT因为要兼容IPv6。url用TEXT因为长链可能带大量追踪参数。expire_time用Unix时间戳比较方便。如果以后访问量过千万visit_logs可以按月分表前期这个设计足够。4.3 SQL注入、XSS与CSRF过滤后台最容易出问题的是搜索框和参数拼接。拿到这套源码后第一件事是检查所有SQL语句。使用PDO预处理能拦住绝大多数SQL注入$stmt $pdo-prepare(SELECT * FROM urls WHERE code ? AND expire_time ?); $stmt-execute([$code, time()]);预处理把SQL模板和数据分离数据库先编译模板再用参数填充用户输入里的引号、分号只会被当成字符串值不会改变SQL结构。输出到页面时再做XSS转义echo htmlspecialchars($row[url], ENT_QUOTES, UTF-8);ENT_QUOTES会把单引号和双引号都转成实体即使用户在链接备注里插入script后台页面也不会执行。还有一个容易被漏掉的是CSRF后台删除链接、修改配置的接口需要校验token否则诱导管理员访问一个构造好的URL就能删掉全部短链。加一个简单的token字段到表单并验证能挡住绝大多数跨站请求。5. 二次开发技巧把防红短链改造成链接可用性检测工具后台统计里的hits只能告诉你链接有没有被点击不能告诉你链接在外链环境里是否被拦截。我基于这套源码做过一个改造让短链系统定时探测urls表里的长链接用手机UA请求看返回内容里有没有平台拦截特征再把结果写进check_log表。这样运营每天打开后台就能知道哪些链接需要换域名或改参数。核心脚本是cron_check.php?php require db.php; $stmt $pdo-query(SELECT id, url FROM urls WHERE expire_time 0 OR expire_time . time()); $ch curl_init(); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER true, CURLOPT_FOLLOWLOCATION true, CURLOPT_TIMEOUT 10, CURLOPT_USERAGENT Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15, CURLOPT_SSL_VERIFYPEER false, ]); foreach ($stmt as $row) { curl_setopt($ch, CURLOPT_URL, $row[url]); $body curl_exec($ch); $httpCode curl_getinfo($ch, CURLINFO_RESPONSE_CODE); // 返回为空或出现拦截关键词时标记为异常 $isBlocked ($httpCode 0) || (mb_strpos($body, 该网页无法访问) ! false); $pdo-prepare(INSERT INTO check_log (url_id, http_code, blocked, check_time) VALUES (?,?,?,?)) -execute([$row[id], $httpCode, $isBlocked ? 1 : 0, time()]); } curl_close($ch);这里的CURLOPT_USERAGENT模拟了iPhone微信内置浏览器的UACURLOPT_FOLLOWLOCATION会让CURL跟随短链跳转。部署到crontab时要注意频率建议每天一次每次最多检查200条并且把日志写到独立文件0 3 * * * /usr/bin/php /data/www/shorty/cron_check.php /data/logs/check.log 21如果服务器有多个出口IP可以在curl里增加CURLOPT_INTERFACE参数绑定不同IP让每个链接用不同出口探测结果更接近真实用户。改造完成后原来源源不断被动的短链系统就多了一个主动发现链接被拦截的能力比单纯依赖后台点击统计直观得多。本文还有配套的精品资源点击获取
返回列表