Bug #8570
closedAnti-spam for Contact Form 7
0%
Description
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):
158.222.111.170 - - [22/Aug/2017:13:27:04
0400] "GET /contact-us/ HTTP/1.1" 200 124474 "" "-" 23284
158.222.111.170 - - [22/Aug/2017:13:27:070400] "GET /files/wpcf7_captcha/3072338607.png HTTP/1.1" 200 1204 "" "-" 23284
158.222.111.170 - - [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" 23380Notice 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.
Related issues
Updated by Raymond Hoh over 7 years ago
- Category name set to Spam/Spam Prevention
There's a CF7 honeypot plugin I found on the wordpress.org plugin repository:
https://wordpress.org/plugins/contact-form-7-honeypot/
Looks simple enough.
Could also hook Akismet into Contact Form 7 as per CF7's docs:
https://contactform7.com/spam-filtering-with-akismet/
Also, this blog article goes into all the options available for counteracting spam with CF7:
https://barn2.co.uk/stop-contact-form-7-spam/
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.
Updated by Boone Gorges over 7 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.
Updated by Raymond Hoh over 7 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.
Updated by Boone Gorges over 7 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?
Updated by Raymond Hoh over 7 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:
comment_author: comment_author_email: test@test.com comment_author_url: comment_content: Test Testing Akismet What? blog: http://localhost/ blog_lang: en_US blog_charset: UTF-8 user_ip: ::1 user_agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 referrer: http://localhost/hello-world/ comment_type: contact-form SERVER_SOFTWARE: Apache REQUEST_URI: /wp-json/contact-form-7/v1/contact-forms/1256/feedback REDIRECT_STATUS: 200 HTTP_HOST: localhost HTTP_CONNECTION: keep-alive CONTENT_LENGTH: 1010 HTTP_ACCEPT: application/json, text/javascript, */*; q=0.01 HTTP_ORIGIN: http://localhost HTTP_X_REQUESTED_WITH: XMLHttpRequest HTTP_USER_AGENT: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 CONTENT_TYPE: multipart/form-data; boundary=----WebKitFormBoundaryLEkr88OVybKHK0uL HTTP_REFERER: http://localhost/hello-world/ HTTP_ACCEPT_ENCODING: gzip, deflate, br HTTP_ACCEPT_LANGUAGE: en-US,en;q=0.8 SERVER_SIGNATURE: SERVER_NAME: localhost SERVER_ADDR: ::1 SERVER_PORT: 80 REMOTE_ADDR: ::1 DOCUMENT_ROOT: /www REQUEST_SCHEME: http CONTEXT_PREFIX: CONTEXT_DOCUMENT_ROOT: /www SERVER_ADMIN: admin@localhost SCRIPT_FILENAME: /index.php REMOTE_PORT: 64594 REDIRECT_URL: /wp-json/contact-form-7/v1/contact-forms/1256/feedback GATEWAY_INTERFACE: CGI/1.1 SERVER_PROTOCOL: HTTP/1.1 REQUEST_METHOD: POST QUERY_STRING: SCRIPT_NAME: /index.php PHP_SELF: /index.php REQUEST_TIME_FLOAT: 1503601753.147 REQUEST_TIME: 1503601753
Updated by Boone Gorges over 7 years ago
- Status changed from Staged for Production Release to Resolved
Deployed.
Updated by Raymond Hoh over 6 years ago
- Related to Bug #9489: Email spam possibly related to CAC form added