Anti-spam for Contact Form 7
We were informed by IT that some contact forms powered by Contact Form 7 were being used to send some spam emails to the site admins. I dug into the logs and found the following (from my email to IT):
18.104.22.168 - - [22/Aug/2017:13:27:04
0400] "GET /contact-us/ HTTP/1.1" 200 124474 "" "-" 23284
22.214.171.124 - - [22/Aug/2017:13:27:07
0400] "GET /files/wpcf7_captcha/3072338607.png HTTP/1.1" 200 1204 "" "-" 23284
126.96.36.199 - - [22/Aug/2017:13:27:20 -0400] "POST /contact-us/ HTTP/1.1" 200 124594 "http://www.google.com" "Mozilla/5.0 (IE 11.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C; rv:11.0) like Gecko" 23380
Notice the google.com referer in the third line. Looking at this, my guess is that a bot is doing the following:
- In a browser, search Google for pages containing contact forms powered by this specific plugin.
- Based on results, fetch the contact page. This is presumably not being done with a web browser, because all of the rest of the page assets (CSS, JS, etc) are not being loaded, and there's no user-agent recorded in the access log.
- Parse the contact page HTML to get the URL of the CAPTCHA image, and then fetch that image.
- Use some technique to solve the CATCHA
- Build a POST request known to work with this plugin, including the CAPTCHA answer, and launch it from the browser. This explains why the POST request has a User-Agent as well as a Referer.
I think we can thwart this with a sort of reverse honeypot: a hidden field that has a secret token, which is rendered as part of the form but is not part of CF7. It must be part of the POST request in order for the submission to go through.
Ray, have you built this kind of thing before? If so, and you have any code, would you mind sharing? Otherwise I can try to whip something up.
#1 Updated by Raymond Hoh almost 3 years ago
- Category name set to Spam/Spam Prevention
There's a CF7 honeypot plugin I found on the wordpress.org plugin repository:
Looks simple enough.
Could also hook Akismet into Contact Form 7 as per CF7's docs:
Also, this blog article goes into all the options available for counteracting spam with CF7:
I guess all those options would still require some configuring on the user's side.
Maybe there is a way to filter the CF7 form contents so the anti-spam options for the CF7 form are rendered all the time. Since we already use Akismet, we should try injecting Akismet into all our CF7 forms first. I'll take a look and see how difficult that may be.
#2 Updated by Boone Gorges almost 3 years ago
Yeah, injecting Akismet for all CF7 instances seems like it'd be the best. It appears that CF7 doesn't have a ton of filter points to make this easy. Worst case scenario, we could create our own version of wpcf7_akismet(), which would skip the wpcf7_akismet_submitted_params() check (since we'd be forcing all compatible params to use Akismet).
If you get time to look at this in the next couple days, that'd be great. Otherwise I'll see what I can rig up before the 1.11.11 release.
#3 Updated by Raymond Hoh almost 3 years ago
- Status changed from Assigned to Staged for Production Release
I've added Akismet protection to all CF7 forms in https://github.com/cuny-academic-commons/cac/commit/60dc08b54ce0d7d39318b685dcedd728ebf62e71.
Since each CF7 form can be different, based on the CF7 Akismet docs, I've only injected the
akismet:author_email attribute since there is always going to be an
[email] tag. We can't really determine what is going to be the author name or URL field, so I left those out.
I tested locally and Akismet protection worked.
#4 Updated by Boone Gorges almost 3 years ago
Cool, thanks, Ray!
Since each CF7 form can be different, based on the CF7 Akismet docs, I've only injected the akismet:author_email attribute since there is always going to be an [email] tag. We can't really determine what is going to be the author name or URL field, so I left those out.
Does this mean that all items will be sent to Akismet, because CF7 only needs to have a single Akismet-specified field to send the entire submission through Akismet? Or does it mean that only the email field is sent to Akismet?
#5 Updated by Raymond Hoh almost 3 years ago
Does this mean that all items will be sent to Akismet, because CF7 only needs to have a single Akismet-specified field to send the entire submission through Akismet?
Yes, this looks to be the case even though we only use the one
akismet:author_email attribute for CF7.
The following was the query sent to Akismet as a querystring, I just formatted it a bit better for viewing here: