有人利用我的开源项目钓鱼了 14,000 人。
Someone used my open source project to phish people

原始链接: https://andrej.sh/posts/phishing-through-my-open-source-project

开源工具 Kaneo 的创建者 Andrej Acevski 最近发现,有诈骗者利用他的云平台发送了 14,520 封网络钓鱼邮件。攻击者利用该平台的开放注册和邀请系统,以他已验证的域名为名义,发送了伪装成银行信息的欺诈邮件。 此次事件并非技术漏洞,而是对软件既有功能的滥用。Acevski 意识到,他无意中将自己域名的声誉和邮件发送权限借给了不法分子。尽管他开发的自托管软件旨在实现个人控制,但他认识到,其托管的云版本已成为需要更严格安全监管的关键基础设施。 为了防止此类滥用,Acevski 实施了验证码(CAPTCHA)、临时邮箱拦截器、速率限制以及针对新账号的邀请权限限制等防护措施。他总结认为,开发者必须区分开源软件和多租户 SaaS 模式下的威胁模型。管理云服务不仅仅是托管代码,更肩负着保护平台完整性和维护发送域名声誉的专业责任。

Hacker News最新 | 过往 | 评论 | 提问 | 展示 | 招聘 | 提交登录有人利用我的开源项目钓鱼了14,000人 (andrej.sh)15 分,由 andrejsshell 发布于 3 小时前 | 隐藏 | 过往 | 收藏 | 讨论 帮助 准则 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

May 2026 · Andrej Acevski · 4 min read

Thursday morning, Resend emailed me. My sending quota for cloud.kaneo.app was exhausted. I had not sent anything in days.

I run an open source project management tool called Kaneo. Simple, all you need and nothing you don’t. The cloud version exists so people can try it without standing up Postgres. It’s the same software my self-hosted users run, and until last weekend it was almost exclusively used by developers evaluating it for their teams.

Last weekend someone else found it.

What I found

I sshed in and ran a query. The new workspaces looked like this:

🔒Paul Brown from BANKING OPERATION invited you to join 3.4090_BTC receipt ...

There were 949 of them. All created in a three-hour window on May 28th. Each came from a different throwaway email provider: yomail.info, dropmail.me, spymail.one. Each account had created one workspace whose name was a complete phishing email subject line, then sent roughly a hundred invitations from that workspace to a list of strangers. 14,520 invitations total, mostly while I was asleep.

The invitation email went out from my verified Resend domain. Subject line: the workspace name. Body: a polite “<phishing payload> invited you to join <phishing payload> on Kaneo.” Click here to accept. The “click here” pointed at my actual site, which made the email look real, and the workspace name carried the scam, a link to craftum.io with a tracking suffix.

Whoever did this was patient. They had clearly tested. The workspace names weren’t random. They followed a template, with rotating variations on bank names, crypto amounts, and presenter names. They had a recipient list ready, presumably bought. They timed it for 4am UTC on a Thursday. They got about an hour and a half of clean sending before Resend’s rate detection kicked in and stopped them.

By the time I woke up it was over. The Resend quota alert that woke me was a coincidence. The attack had finished hours earlier.

The part that bothered me

What stuck with me wasn’t the scale, although 14,000 people getting a phishing email from a domain I own is bad. It was how mundane it was.

There was no exploit. No vulnerability disclosure. No CVE for me to write. The attacker filled out my signup form 942 times, made 942 workspaces, sent 942 batches of about a hundred invitations each, and stopped. They used my tool exactly as designed. The design was just bad enough that the tool was good for phishing.

Every gate I’d later add (captcha, disposable email block, rate limit, workspace-name filter) was something a thoughtful person would have asked for if they’d looked at my signup flow with a phishing operation in mind. I hadn’t. I’d looked at it with a person-trying-Kaneo in mind, and built for that person.

The attacker hadn’t broken into anything. They’d just noticed something I hadn’t: I had a verified email-sending domain attached to open, unverified signup, and that’s a useful primitive if you don’t care what you send.

The cleanup

I revoked the Resend keys first, then dumped the bot accounts, their workspaces, and all 14,520 invitations to CSV in case Resend wanted evidence. The destructive work was one Postgres transaction. 942 users banned, 947 workspaces deleted, 14,533 invitations cascaded out via foreign keys. I ran it with ROLLBACK at the end first to check the counts, then with COMMIT. Maybe an hour, total.

The hardening that followed (captcha, rate limits, a disposable-email blocker, a workspace-name filter, no invites from guest accounts) took a day. The easy work isn’t what stuck.

What I keep thinking about

Open source projects with a hosted cloud version sit in a weird spot. The whole point of the project is that strangers can read the code, fork it, run it themselves. That’s the bargain. The cloud version is a convenience on top, a way for people who don’t want to manage infrastructure to still try the thing.

But the cloud version’s threat model is nothing like the self-hosted one. Self-hosted, the operator and the user are the same person, or at least know each other. Adversarial workspace names don’t matter because no one would create one. Rate limits don’t matter because there’s no incentive to spam yourself.

On a multi-tenant SaaS, the operator is me, the users are everyone, and the operator vouches for everything the user does to every external system the SaaS touches. Resend doesn’t know the difference between an invite I sent and an invite a stranger sent through my project. My DKIM signature is on both.

I’d been thinking of cloud.kaneo.app as “the same software, hosted for you.” It was always more than that. It was “the same software, plus my Resend reputation, my IP reputation, my domain’s relationship with every mail provider on the internet.” For 14 months no one cared. Last Thursday someone did.

What I’m changing

Captcha on signup. Disposable email domains blocked. Rate limits on the invite endpoint. A workspace-name filter. Guest accounts can’t send invitations anymore. None of it ships to self-hosters, because their threat model is different, and I don’t want to slow down actual users with paranoia they don’t need.

The deeper change is in how I think about the cloud tier. It’s not a casual sandbox for the project. It’s a piece of infrastructure I’m running on behalf of strangers, with all the responsibilities that entails. The 14,000 phishing recipients are not my users, but my domain landed in their inboxes, and that’s on me.

I’m keeping the cloud version. I’m narrowing it. And I’m done assuming the next person to find it will want what I built it for.

联系我们 contact @ memedata.com