On March 26, 2019 we launched new MODX Forums. Please join us at the new MODX Community Forums.
Subscribe: RSS
  • I had a bot attack on all of my forms recently. The knee-jerk reaction from everyone was to add captcha to all the forms. But I hate them.
    So I created a cool-down hook to slow the bots down. Most people do not submit form twice so quickly.

    It is working for me, but I was wondering if anyone else could use it, and or did I miss a feature.
    The prehook creates a session with a timestamp. The hook compares it to the time and a delay.

    FormItWaitForItPreHook
    <?php
    $waitSessionName = $modx->getOption('waitSessionName', $formit->config, 'waitSession');
    if (!isset($_SESSION[$waitSessionName])) {
    	$_SESSION[$waitSessionName] = time();
    }
    // debug
    // $hook->setValue('session', $_SESSION[$waitSessionName] . ' - ' . time());
    return true;
    


    FormItWaitForIt
    <?php
    $waitSessionName = $modx->getOption('waitSessionName', $formit->config, 'waitSession');
    $delay = $modx->getOption('delay', $formit->config, 15);
    $delayPenalty = $modx->getOption('delayPenalty', $formit->config, 30);
    
    $delayTimer = ($_SESSION[$waitSessionName] - time()) + $delayPenalty;
    $s = $delayTimer%60;
    $dTm = floor(($delayTimer % 3600) / 60);
    $dTh = floor(($delayTimer % 86400) / 3600);
    $m = $dTm>0?$dTm.' minute'.($dTm>1?'s':''):'';
    $h = $dTh>0?$dTh.' hour'.($dTh>1?'s':''):'';
    
    $timeRemaining = "$h $m $s seconds" . ' ' . $_SESSION[$waitSessionName] . ' ' . $delayTimer;
    
    $waitErrorMsg = $modx->getOption('waitErrorMsg', $formit->config, 'Too soon, please wait [[!+timeRemaining]]');
    
    if ($_SESSION[$waitSessionName]+$delay <= time()){
      $_SESSION[$waitSessionName] = time();
      return true;
    } else {
      $_SESSION[$waitSessionName] = time() + $delayTimer;
      $modx->log(modX::LOG_LEVEL_INFO, $_SESSION[$waitSessionName]);
      $hook->addError('waitError',$waitErrorMsg);
      $modx->setPlaceholder('timeRemaining', $timeRemaining);
      return false;
    }
    



    Test Form
    [[!FormIt?
       &preHooks=`FormItWaitForItPreHook`
       &hooks=`FormItWaitForIt,redirect`
       &emailTpl=`MyEmailChunk`
       &emailTo=`me@gmail.com`
       &redirectTo=`1`
    ]]
     
    <h1>Session Form</h1>
    <span class="errors">[[!+fi.error.waitError]]</span>
    <form action="[[~[[*id]]]]" method="post" class="form">
        <div class="form-row">
            <label for="session">Session</label>
            <input type="text" name="session" size="50" autocomplete="off" class="form-field" value="[[!+fi.session]]"/>
        </div>
        <button type="submit" class="submit-button">Submit</button>
    </form>
    
      DropboxUploader -- Upload files to a Dropbox account.
      DIG -- Dynamic Image Generator
      gus -- Google URL Shortener
      makeQR -- Uses google chart api to make QR codes.
      MODxTweeter -- Update your twitter status on publish.
    • Nice idea. Thanks for sharing that.

      You might also look at the SPForm code to see some other options like requiring the use of the mouse or keyboard (something bots almost never do). Restricting bad user agents in .htaccess can also help a lot.

      Adding an extra, unused field, like 'user_name' or 'enter_subject' that is not visible and rejecting things when it's filled out can also help. You can do it with the FormIt spam hook, or on your own with a postHook.


        Did I help you? Buy me a beer
        Get my Book: MODX:The Official Guide
        MODX info for everyone: http://bobsguides.com/modx.html
        My MODX Extras
        Bob's Guides is now hosted at A2 MODX Hosting
      • Quote from: BobRay at Feb 17, 2019, 11:32 PM

        Adding an extra, unused field, like 'user_name' or 'enter_subject' that is not visible and rejecting things when it's filled out can also help. You can do it with the FormIt spam hook, or on your own with a postHook.

        I had it on one of the forms, but not all. I had a CSS hidden fax number field. Not sure if they hit that one at not. I need to download the data and have a look. It stopped the spam bots on that form before. I did get much on the others until now. This was different, this was a vulnerability test, lots of weird code submitted.
          DropboxUploader -- Upload files to a Dropbox account.
          DIG -- Dynamic Image Generator
          gus -- Google URL Shortener
          makeQR -- Uses google chart api to make QR codes.
          MODxTweeter -- Update your twitter status on publish.
        • Nice idea! Cheers for sharing.
            I'm lead developer at Digital Penguin Creative Studio in Hong Kong. https://www.digitalpenguin.hk
            Check out the MODX tutorial series on my blog at https://www.hkwebdeveloper.com
          • I had the same idea some time ago, thank you for sharing! That will be helpful for many people.
              Anton Tarasov
              MODX Developer

              Email: contact@antontarasov.com
              Web: antontarasov.com
            • Quote from: himurovi4 at Feb 18, 2019, 08:33 AM
              I had the same idea some time ago, thank you for sharing! That will be helpful for many people.

              If you have ideas please add them. I tried to think of different use cases. I would like to output the time left to wait as a raw time then format it with javascript for a live countdown.
                DropboxUploader -- Upload files to a Dropbox account.
                DIG -- Dynamic Image Generator
                gus -- Google URL Shortener
                makeQR -- Uses google chart api to make QR codes.
                MODxTweeter -- Update your twitter status on publish.
              • New Code... something is not lining up correctly. I missing some logic.

                FormItWaitForIt
                <?php
                $waitSessionName = $modx->getOption('waitSessionName', $formit->config, 'waitSession');
                $delay = $modx->getOption('delay', $formit->config, 15);
                $delayPenalty = $modx->getOption('delayPenalty', $formit->config, 30);
                
                $delayTimer = ($_SESSION[$waitSessionName] - time()) + $delayPenalty ;
                $s = $delayTimer%60;
                $dTm = floor(($delayTimer % 3600) / 60);
                $dTh = floor(($delayTimer % 86400) / 3600);
                $m = $dTm>0?$dTm.' minute'.($dTm>1?'s':''):'';
                $h = $dTh>0?$dTh.' hour'.($dTh>1?'s':''):'';
                
                $timeRemaining = "$h $m $s seconds - " . $delayTimer;
                
                $waitErrorMsg = $modx->getOption('waitErrorMsg', $formit->config, 'Too soon, please wait <strong id="countdown"></strong> ([[!+timeRemaining]])');
                
                if ($_SESSION[$waitSessionName] + $delay <= time()){
                  $_SESSION[$waitSessionName] = time();
                  return true;
                } else {
                  $_SESSION[$waitSessionName] = time() + $delayTimer;
                  $modx->log(modX::LOG_LEVEL_INFO, $_SESSION[$waitSessionName]);
                  $hook->addError('waitError',$waitErrorMsg);
                  $modx->setPlaceholder('timeRemaining', $timeRemaining);
                  $modx->regClientStartupScript('assets/components/waitforit/js/waitCountdown.js');
                  $newDelay = $delayTimer + $delay;
                  $modx->regClientStartupHTMLBlock('<script type="text/javascript">countSeconds('.$newDelay.');</script>');
                  return false;
                }
                



                assets/components/waitforit/js/waitCountdown.js

                function countSeconds(seconds) {
                     initialSecs = seconds;
                	 currentSecs = initialSecs;
                }
                
                setTimeout(decrement,1000); 
                
                function decrement() {
                   var displayedSecs = currentSecs % 60;
                   var displayedMin = Math.floor(currentSecs / 60) % 60;
                   var displayedHrs = Math.floor(currentSecs / 60 /60);
                
                    if(displayedMin <= 9) displayedMin = "0" + displayedMin;
                    if(displayedSecs <= 9) displayedSecs = "0" + displayedSecs;
                    currentSecs--;
                    document.getElementById("countdown").innerHTML = displayedHrs + ":" + displayedMin + ":" + displayedSecs;
                    if(currentSecs !== -1) setTimeout(decrement,1000);
                }
                
                
                  DropboxUploader -- Upload files to a Dropbox account.
                  DIG -- Dynamic Image Generator
                  gus -- Google URL Shortener
                  makeQR -- Uses google chart api to make QR codes.
                  MODxTweeter -- Update your twitter status on publish.