邀请码刷量漏洞攻防
"邀请一个好友,双方各得 20 元红包"——看似美好的增长方案,遇到职业羊毛党,可能让平台一天损失几百万。
一、刷量全景图
攻击者准备阶段:
手机号资源(接码平台 / SIM 卡池)
代理 IP(动态住宅 IP / 4G IP)
设备指纹模拟器(Android 模拟器 / 改机工具)
自动化脚本
攻击路径:
注册账号A → 生成邀请码 → 注册账号B(填A的邀请码)→ A和B都拿奖励
↑ 重复 N 次,即可薅 N × 奖励金额
二、漏洞场景一:自邀自奖
2.1 漏洞描述
用户可以用自己的邀请码"邀请"自己注册的新账号,形成自循环刷奖励。
2.2 漏洞代码
// VULNERABLE: 推荐关系没有闭环校验
app.post('/api/register', async (req, res) => {
const { phone, password, inviteCode } = req.body;
const user = await db.User.create({ phone, password: hash(password) });
if (inviteCode) {
const inviter = await db.User.findOne({ where: { inviteCode } });
if (inviter) {
// ❌ 没有检查 inviter 是不是当前用户自己
// ❌ 没有检查 invitee 是否绑定过其他邀请关系
await db.Referral.create({ inviterId: inviter.id, inviteeId: user.id });
// 双方都发奖励
await db.Wallet.increment('balance', { by: 20, where: { id: inviter.id } });
await db.Wallet.increment('balance', { by: 20, where: { id: user.id } });
}
}
res.json({ success: true });
});
2.3 攻击脚本
# 自邀自奖脚本
import requests, random, time
API = 'http://target.example.com'
HEADERS = {'Content-Type': 'application/json'}
def get_new_phone():
# 接码平台获取手机号
return requests.get('https://sms-provider.com/new').json()['phone']
def do_register(phone, password, invite_code):
return requests.post(f'{API}/api/register', json={
'phone': phone, 'password': password, 'inviteCode': invite_code,
})
# 主号登录获取自己的邀请码
main_phone = '13800138000'
main_code = 'MY_CODE_123'
# 循环刷 100 个小号
for i in range(100):
new_phone = get_new_phone()
resp = do_register(new_phone, 'P@ssw0rd!', main_code)
if resp.json().get('success'):
print(f'[+] 成功注册第 {i+1} 个小号,双方各得 20 元')
time.sleep(0.5)
2.4 修复
// FIXED: 多重校验 + 风控
app.post('/api/register', async (req, res) => {
const { phone, password, inviteCode } = req.body;
const clientIp = req.ip;
const deviceFingerprint = req.headers['x-device-fp'];
// 1. 创建用户
const user = await db.User.create({ phone, password: hash(password) });
if (inviteCode) {
const inviter = await db.User.findOne({ where: { inviteCode } });
// ✅ 校验链
const checks = [
inviter, // 邀请人存在
inviter.id !== user.id, // 不能邀请自己
!inviter.directReferrals.includes(user.id), // 还没被邀请过
user.createdAt - inviter.createdAt > 60000, // 邀请人注册超过1分钟
];
// ✅ 风控校验
const risk = await riskEngine.evaluate({
clientIp,
deviceFingerprint,
phone,
inviterId: inviter?.id,
});
if (checks.every(Boolean) && risk.score < 70) {
await db.Referral.create({ inviterId: inviter.id, inviteeId: user.id });
await db.Wallet.increment('balance', { by: 20, where: { id: inviter.id } });
await db.Wallet.increment('balance', { by: 20, where: { id: user.id } });
} else {
console.warn('[RISK] Referral blocked', { checks, risk });
}
}
res.json({ success: true });
});
三、漏洞场景二:IP/设备指纹批量注册
3.1 问题
同一个 IP 或同一台设备上注册大量账号,即使邀请码逻辑正确,也会被职业羊毛党利用代理 IP + 改机工具绕过。
3.2 防御方案
// 分层风控引擎
async function evaluateRisk({ clientIp, deviceFingerprint, phone, inviterId }) {
const scores = [];
// 1. IP 维度
const ipCount = await redis.incr(`reg:ip:${clientIp}`);
if (ipCount === 1) await redis.expire(`reg:ip:${clientIp}`, 3600);
scores.push(ipCount > 3 ? 30 : 0); // 同 IP 注册超过 3 次,风险 +30
const ipProxy = await checkProxy(clientIp); // 检测是不是代理
scores.push(ipProxy ? 25 : 0);
// 2. 设备维度
const devCount = await redis.incr(`reg:dev:${deviceFingerprint}`);
if (devCount === 1) await redis.expire(`reg:dev:${deviceFingerprint}`, 86400);
scores.push(devCount > 5 ? 35 : 0);
// 3. 手机号维度
const phonePattern = phone.substring(0, 7);
const batchPhones = await db.User.count({ where: { phone: { $like: phonePattern + '%' }, createdAt: { $gt: new Date(Date.now() - 3600000) } } });
scores.push(batchPhones > 10 ? 20 : 0); // 同一号段 1 小时内注册超过 10 个
// 4. 邀请关系维度
if (inviterId) {
const inviterSpeed = await redis.zcount(
`referral:timeline:${inviterId}`,
Date.now() - 3600000, Date.now()
);
scores.push(inviterSpeed > 5 ? 25 : 0); // 1 小时内邀请超过 5 人
}
return { score: scores.reduce((a, b) => a + b, 0), details: scores };
}
四、漏洞场景三:提现绕过
4.1 问题
即使成功拦截了刷注册,攻击者仍可能通过邀请奖励提现洗钱。防御链必须延伸到提现环节。
4.2 防御
app.post('/api/withdraw', authMiddleware, async (req, res) => {
const { amount, bankCard } = req.body;
// ✅ 必须完成实名
if (!req.user.verified) {
return res.status(400).json({ error: '请先完成实名认证' });
}
// ✅ 提现银行卡必须与实名信息一致
const cardInfo = await db.BankCard.findOne({
where: { userId: req.user.id, cardNumber: bankCard },
});
if (!cardInfo || !cardInfo.verified) {
return res.status(400).json({ error: '请先绑定银行卡' });
}
// ✅ 奖励金额提现必须满足"存活期"
const referralRewards = await db.WalletLog.sum('amount', {
where: {
userId: req.user.id,
type: 'REFERRAL',
createdAt: { $lt: new Date(Date.now() - 7 * 86400000) }, // 7 天冻结期
},
});
if (amount > referralRewards) {
return res.status(400).json({ error: '邀请奖励需 7 天后才可提现' });
}
// ✅ 单日/单月限额
const dailyTotal = await db.Withdraw.sum('amount', {
where: { userId: req.user.id, createdAt: { $gt: new Date(Date.now() - 86400000) } },
});
if (dailyTotal + amount > 2000) {
return res.status(400).json({ error: '单日提现限额 2000 元' });
}
// ✅ 频控
const withdrawLock = await redis.set(
`withdraw:lock:${req.user.id}`, '1', 'NX', 'EX', 60
);
if (!withdrawLock) {
return res.status(429).json({ error: '操作太频繁' });
}
// ✅ 大额提现走人工审核
if (amount >= 10000) {
await createManualReview(req.user.id, amount);
return res.json({ success: true, message: '提现已提交审核' });
}
await processWithdraw(req.user.id, amount);
res.json({ success: true });
});
五、邀请码系统通用防御架构
┌─────────────────────────────┐
│ 注册/邀请请求入口 │
└────────────┬────────────────┘
│
┌──────────▼──────────┐
│ ① 身份真实性校验 │
│ (实名 / 手机实名) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ ② 关系链合理性校验 │
│ (不能自邀 / 循环链) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ ③ 多维度风控打分 │
│ (IP/设备/行为) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ ④ 奖励到账延迟 + 冻结│
│ (T+7 才可提现) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ ⑤ 反洗钱规则引擎 │
│ (大额/频次/异常链) │
└─────────────────────┘
反循环邀请链检测
// 防止 A→B→C→A 的循环邀请
async function detectLoop(inviterId, inviteeId) {
const visited = new Set();
let current = inviterId;
let count = 0;
const MAX_DEPTH = 10;
while (current && count < MAX_DEPTH) {
if (visited.has(current)) return true; // 发现循环
visited.add(current);
const referral = await db.Referral.findOne({
where: { inviteeId: current },
order: [['createdAt', 'DESC']],
});
current = referral?.inviterId;
count++;
}
return false;
}
六、总结
邀请码刷量的本质是:攻击者的边际成本极低(接码 1 元/个 + 代理 IP),而防御者要覆盖所有维度。
核心防御策略:
- 闭环校验:不能自邀、不能循环、不能批量
- 实时风控:IP + 设备 + 行为 + 关系链多维度打分
- 延迟结算:奖励延迟到账,给风控留排查时间
- 提现设卡:实名 + 冻结期 + 限额 + 人工审核
- 持续迭代:羊毛党手法会更新,风控规则必须跟着迭代