Skip to content
Grav 2.0 is officially stable. Read the announcement →

Community guidelines

Please keep discussions civil and on-topic. Repeated violations may lead to a temporary ban.

Forms & Blueprints

Form validation.pattern for !URLs

form

Solved by Hugh Barnes View solution

Started by Thomas 8 years ago · 5 replies · 1729 views
8 years ago

Hi there,

I get a lot of spam mails through the public available forms. I use the honypot field but looks like spam-bots are too clever to get trapped.
Also I want to avoid using google captcha as long as there are other autmated solutons.

After some search for best-practice I came across two ideas for detecting form spam:

  1. The first one uses a hidden field with a timestamp and and compares the submitted timestamp with the current time. if the delta is below a certain time it´s likley that there was no human involved. I guess this is something which has to be implemented in the plugin itself.

  2. Spam-bots want to share URLs. All of the spam-mails i have in the inbox contain an URL in the message-field. But it doesn´t make sense to write about URLs in my forms, so I thought about adding a validation.pattern that checks for URL-strings. Basically checking for "http://" would be enough.
    For this approach I need some help. How would a validation pattern look like, that is falsey when a URL is included?

And does it work to have

validation.required: false

and also a functional validation.pattern?

Thanks and all the best
mirac

8 years ago

so far, this dosn´t work:

pattern: '/^((?!http[s]?:\/\/).)*$/m'

matches true to everything.

8 years ago

I've used this one before to check on urls:

"/\b(?:(?:https?|ftp):\/\/|www.)[-a-z0-9+&@#\/%?=~|!:,.;]*[-a-z0-9+&@#\/%=~|]/i"

Edit out the ftp part and it should work perfectly fine for you :) (I think ;) )

5 years ago

Did you find a resolution to this problem @miraculli ?

I face something similar with text field validation

5 years ago

@daubneyi, I needed a solution myself for my own ContactForm plugin because someone was having a blast last night filling my mailbox which a few thousand of spam messages...

The solution mentioned above, seems to be failing when surrounding the pattern with a /. And without the /s, the pattern will fail when 'message' contains newlines.

So here is a rough abstract of the solution I was working on. It requires a custom plugin, but isn't that difficult to create. It allows you to add as many patterns and messages as you like.

  • Create a plugin using $ bin/plugin devtools new-plugin
    Let's assume you named it 'nospam'.
  • In '/user/plugins/nospam/nospam.yaml' add the following:
    YAML
    enabled: true
    fields:
    name: []
    email: []
    phone: []
    message:
      - pattern: '/<br\/?>/i'
        message: 'HTML is not allowed'
      - pattern: '/https?:\/\//i'
        message: 'URL is not allowed'
    onSpam: notify   # die|notify (default)
    
  • In file '/user/plugins/nospam/nospam.php':

    • Alter function 'onPluginsInitialized' into:

      PHP
      public function onPluginsInitialized(): void
      {
      // Don't proceed if we are in the admin plugin
      if ($this->isAdmin()) {
          return;
      }
      
      // Enable the main events we are interested in
      $this->enable([
          // Put your main events here
          'onFormPrepareValidation' => ['onFormPrepareValidation', 0],
      ]);
      }
      
    • Create a new function to validate the fields

      PHP
      public function onFormPrepareValidation(Event $event)
      {
      /** @var Form */
      $form = $event['form'];
      $values = $form->value();
      $fields = array_keys($form->getFields());
      $onSpam = $this->config->get("plugins.nospam.onSpam", 'notify');
      
      $messages = [];
      
      foreach($fields as $field) {
          $patterns = $this->config->get("plugins.nospam.fields.$field", []);
      
          foreach($patterns as $pattern) {
              if (preg_match($pattern['pattern'], $values[$field]) === 1) {
                  if ($onSpam === 'die') {
                      die();
                  }
      
                  if (!isset($messages[$field])) {
                      $messages[$field] = [];
                  }
                  $messages[$field][] = "Field '$field': {$pattern['message']}";
              }
          }
      }
      
      if (count($messages) > 0) {
          $exception = new ValidationException('');
          $exception->setMessages($messages);
      
          throw $exception; 
      }
      }
      
    • Result:
      Untitled|510x256

    • Notes:

    • Multiple notifications per field.

    • Multiple validation checks per field.

    • onSpam: notify: show validation message in page.

    • onSpam: die: stop processing when spam detected.

    • nospam.yaml: Only add fields that need testing for spam.

    • Simpler regex: no negative lookahead required.

    • Todo:

    • Add blueprint for easy form in Admin.

👍 3
last edited 06/05/21 by pamtbaau
4 years ago Solution

Here is what I have used to screen for links. It's been tested on a few sites for a few years and I can't remember how all of it works!

'^((?!https?:\/\/)(?:\R|.))*$'

I hope it's useful rather than confusing and unwelcome 😕

👍 1

Suggested topics

Topic Participants Replies Views Activity
Forms & Blueprints · by Ton Haarmans, 5 years ago
13 1139 4 months ago
Forms & Blueprints · by Hugo Oliveira, 5 months ago
0 63 5 months ago
Forms & Blueprints · by Flachy Joe, 6 months ago
9 137 6 months ago
Forms & Blueprints · by Augustus, 7 months ago
7 112 7 months ago
Forms & Blueprints · by Julien, 7 months ago
10 131 7 months ago