feat: add honeypots and (mostly) invisible PoW captchas to highly spam prone forms#3488
feat: add honeypots and (mostly) invisible PoW captchas to highly spam prone forms#3488
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request implements a comprehensive anti-spam system for PubPub by adding honeypots and proof-of-work (PoW) captchas to highly spam-prone forms. The system uses the Altcha library for invisible PoW challenges and honeypot fields to catch automated bots.
Changes:
- Added Altcha-based proof-of-work captcha system with server-side HMAC verification
- Implemented honeypot fields across user-facing forms (signup, login, community/pub creation, discussions)
- Enhanced spam tag tracking with honeypot triggers, manual markings, and automated status updates
- Added comprehensive Slack and email notifications for spam events (new tags, bans, lifts)
- Created UI components for spam management (SpamStatusMenu, user profile spam indicators)
Reviewed changes
Copilot reviewed 64 out of 68 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| server/utils/captcha.ts | New utility for Altcha PoW captcha verification with HMAC |
| server/utils/honeypot.ts | Honeypot detection and spam tag creation logic |
| server/spamTag/userQueries.ts | Enhanced user spam tag management with notifications and session deletion |
| server/spamTag/userDashboard.ts | New spam user dashboard queries with affiliation tracking |
| server/utils/slack.ts | Expanded Slack notifications for spam events (bans, lifts, new tags) |
| server/utils/email/spam.ts | Email notifications for spam bans and lifts to users and dev team |
| server/community/api.ts | Community creation with captcha/honeypot verification |
| server/pub/api.ts | Pub creation endpoints with spam protection |
| server/user/api.ts | User creation/update with captcha and honeypot checks |
| server/discussion/api.ts | Discussion creation with spam protection |
| server/threadComment/api.ts | Thread comment creation with spam protection |
| server/login/api.ts | Login endpoint with captcha verification |
| server/signup/api.ts | Signup with captcha and honeypot protection |
| server/captcha/api.ts | Challenge generation endpoint for Altcha |
| client/components/Altcha/Altcha.tsx | React component for Altcha widget integration |
| client/components/Honeypot/Honeypot.tsx | Hidden honeypot field component with dev mode visibility |
| client/components/SpamStatusMenu/SpamStatusMenu.tsx | UI for managing spam status of users |
| client/containers/User/UserHeader.tsx | Display spam status badges for super admins |
| types/spam.ts | New type definitions for honeypot triggers and user spam tag fields |
| utils/api/schemas/community.ts | Schema updates for captcha/honeypot fields |
| utils/api/contracts/pub.ts | New createFromForm endpoint contract |
| utils/api/contracts/auth.ts | New loginFromForm endpoint contract |
| package.json | Added altcha and altcha-lib dependencies |
| .test/setup-env.js | Added BYPASS_CAPTCHA=true for tests |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| router.get('/api/captcha/challenge', async (_req, res) => { | ||
| const hmacKey = getAltchaHmacKey(); | ||
| const challenge = await createChallenge({ | ||
| hmacKey, | ||
| maxNumber: MAX_NUMBER, | ||
| }); | ||
| return res.json(challenge); | ||
| }); |
There was a problem hiding this comment.
The /api/captcha/challenge endpoint has no rate limiting. This could allow an attacker to generate unlimited challenge requests, potentially using up server resources or gathering information about the HMAC key through timing attacks. Consider adding rate limiting (e.g., 10 requests per IP per minute) to prevent abuse.
client/containers/Pub/PubDocument/PubDiscussions/Discussion/DiscussionInput.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
i did straight up modify the create user/signup apis rather than creating new ones for them, as these are really meant to only be used through the ui anyway, so if this breaks someones setup: good!
Issue(s) Resolved
Try to stop the stampede of spam.
What does it do
🤐 Captchas
Adds invisible captchas using Altcha
These are very simple Proof of Work (PoW) captchas. They don't check whether the user is a real person (bc that usually requires a(n) expensive service, or a very convoluted cloudflare setup), but just asks the user agent to perform some work (that takes like a second or two at most). This can be sufficient disincentive for most stupid bots to submit a trillion spam comments, as it will take much much longer to do so, as well as requiring a full js load to submit the form (which, tbf, most forms already required, bc we tend not to use native forms at all but just fake react forms).
This lead to creating a bunch of new api routes which require a verification token to perform the action. I did not touch the original apis for this as I didn't want to break them for anyone relying on them.
Captchas are only visible if they fail.
In dev mode, you can test the captcha failing logic by checking the thingamagic
Fail state:
Extra details
Affected forms
The following forms have a Captcha added
Open to suggestions for other places!
Possible issues
This may lead to increased load on the server, as a lot of common actions (creating discussions, creating pubs, logging in) now require the server to check your work. We should monitor whether this increases the load on the server significantly, if so we likely need to come up with some other solution.
Why not turnstile?
We discussed this in Slack, but good to put here for the future:
An idea was to dynamically create 120+1 turnstile widgets for each custom domain, store the secret keys on startup by fetching them from Cloudflare, and then render a different widget for each custom site.
This was thought to be a bit convoluted and easy to break, so we dediced not to go for it and see how far this "easy" solution takes us.
🍯 Honeypots
This PR also adds Honeypots to a variety of forms (a subset of the ones Captchas were added to)
These take the form (hah!) of hidden inputs that look sensible (eg descriptions, titles, extra information) but only a bot would ever submit them. These hidden inputs are visible in dev mode for testing (they will immediately ban you so try not to do that as a superadmin, bc you won't be able to undo it)
When such an input is filled out, it's added as a
_honeypotfield to the payload. When such a field is detected, the user is immediately banned, sending the dev team a message in Slack and on email, and sending the user an email stating they are banned.Information is added to the spamtag saying which honeypot they fell for, making it a bit easier to figure out whether the user was really being evil or not.
There is no grace period or anything: honeypot === banned
Honeypot places
Same as captchas, minus the login page. Not a really good reason, other than that "logging in" isnt really creating anything.
Possible issues
It remains to be seen whether any bot will be able to pass the captcha and fall for the honeypot.
Also mayyybe real users will also fall for the honeypot. They shouldn't though!
🙆 Misc
Nicer User spam dash
Allow sorting the users by amount of discussions made, spam score, and filtering by community
Also shows the last three discussions made by users, making it kinda easy to tell if they're legit or not
Ability to ban tag users from a discussion
Not the same as #3504 , as this is for superadmins only, but allows superadmins to mark users as spam/ban users from the discussion section and their profile.
Profile
Discussion section
Also makes it easier to see that a user has been marked as spam already
Maybe a bit too much info
Test Plan
Just trust meScreenshots (if applicable)
Optional
Notes/Context/Gotchas
Supporting Docs