1

Is there a way to intercept form submission that was triggered by JavaScript?

I'm attempting to reduce form spam submissions from bots by performing some client-side validation (with the help of reCAPTCHA v2 if it matters). When a user submits the form, I'm able to confirm that the input data has been validated (the reCAPTCHA has been filled out) by checking a flag in a method designated via the form's onSubmit attribute...

<form ... id="my_form" onSubmit="submitHandler()">

function submitHandler(e) {
    if ( !valid ) {
        e.preventDefault();
    }
}

...or in a method defined via a jQuery .submit() event handler:

$('#my_form').submit(function(e) {
    if ( !valid ) {
        e.preventDefault();
    }
});

The issue is that neither of these methods appear to get called when the form is submitted via JavaScript. When I open the console and test out submitting via JavaScript...

document.getElementById('my_form').submit()

...the form submits and neither method gets called. The form submit event doesn't appear to be fired, and thus I am unable to check if the data has been validated.

Presumably, this JavaScript-based form submission is the method spambots would be using to submit the form - which tracks with the fact that form spam has not been reduced via these efforts. They're able to bypass my checks.

To be clear, a third-party form service does the heavy lifting on actual field validation server-side. I'm simply trying to reduce the amount of spam that gets sent to their server in the first place.

Is it possible to intercept this JavaScript-based form submission? Am I approaching this the wrong way?

EDIT: To clarify, the onsubmit method does get called when the form is submitted via the form's submit button. But it doesn't get called when the form is submitted via JavaScript (.submit()).

Brett
  • 21
  • 3
  • maybe they don't even use your form to spam the server. A simple http post does the submission without using any html form elements. A simple way (not bulletproof) to reduce the spam a little bit would be filed validation on the server and maybe some CAPTCHAs – iacobalin Mar 12 '19 at 17:33
  • Where are you setting `valid`? – mercator Mar 12 '19 at 17:33
  • https://blog.adriaan.io/submit-event-listener-does-not-work.html this might be a work around – Aadhi Vive Mar 12 '19 at 17:34
  • 2
    Hypothetically, a client can utterly manipulate your javascript anyways so you have to [verify the reCAPTCHA request on server side](https://developers.google.com/recaptcha/docs/verify) to prevent spam – Tom M Mar 12 '19 at 17:37
  • I think for form validation via onsubmit, you have to specify it like onsubmit="return submitHandler()" and in submitHandler if there is a validation error you return false. – Doug F Mar 12 '19 at 17:37
  • @mercator The valid flag was meant to be an example. What I'm actually doing is checking the reCAPTCHA object to see if the token has been populated. Testing via the console indicates that this token is populated only once the reCAPTCHA has been completed successfully. if (grecaptcha.getResponse() == "") { e.preventDefault(); } – Brett Mar 12 '19 at 17:42
  • 3
    Possible duplicate of [Form 'onsubmit' not getting called](https://stackoverflow.com/questions/19847203/form-onsubmit-not-getting-called) – mercator Mar 12 '19 at 17:52
  • I recognize I might not be using reCAPTCHA properly in this case, but I guess the specific problem I'm trying to solve with this question is whether or not it's possible to run a method between the form submission being triggered via .submit() and the actual form submission. In the same way I can intercept form submission when the form is submitted via a button. Does that make sense? – Brett Mar 12 '19 at 18:01
  • @mercator The answer to that question indicates that the submit() method does not fire the submit event, which would explain why the event handlers aren't getting triggered. If that's the case, then I suppose the answer to my question would be no - it's not possible to intercept form submission via JavaScript. – Brett Mar 12 '19 at 18:09
  • Indeed yes (https://developer.mozilla.org/en-US/docs/Web/Events/submit) so my earlier question about `valid` was irrelevant. There's a work-around answer too (overriding the prototype), but there's no guarantee that that will improve anything. The spambots might be sending POST requests directly, without going through your site. – mercator Mar 12 '19 at 18:16
  • @mercator Thanks for the link. That clears it up - form.submit() doesn't raise the submit event, which is why I'm unable to intercept the form submission. I'll need to approach this from a different angle. If you want to post an answer with that link, I'll accept it. Thanks for the help! – Brett Mar 12 '19 at 18:29
  • I've added links to the accepted answer to the question this is a duplicate of instead. :P – mercator Mar 13 '19 at 16:57

1 Answers1

2

Only checking reCAPTCHA in the front end does nothing and is by no means a security precaution. The client can swap out/completely ignore your javascript. Please check the validity of the request backend-side.

However, intercepting requests is possible by using a Service Worker if you want to e.G. reduce the load on your server, given that

  1. The bots UserAgent utilizes this feature.
  2. The bot actually renders your front end in order to send the request
  3. Your site uses https

A Service Worker acts as a proxy between client, server and cache. Thus, it can inspect and modify requests sent by the client. In this case, we want to check for the presence of a certain attribute in the request and ignore the request if it is not found.

<form ... action="/my-form-route" id="my_form" onSubmit="submitHandler()">

main.js

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js');
}

service-worker.js

self.addEventListener('fetch', (event) => {
    // use json(), text() or formData() depending on what the request should look like
    const formData = await event.request.body.formData();

    if (event.request.url.includes('my-form-route') && formData.valid) {
         return; // if google recaptcha is found in request object, ignore the request
    }
    event.respondWith(
        new Promise (resolve, reject) {
           reject('nope')
        }
    )
})

This can be easily bypassed though:

curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"username":"Jeff","email": "jeff@totallylegit.com", "content": "WOW NICE ARTICLE\n free leg enlargement @ totallylegit.com btw lol"}' \
  http://example.com/my-form-route
Tom M
  • 2,815
  • 2
  • 20
  • 47