AJAX-Webformular mit Invisible Recaptcha und serverseitiger Validierung

AJAX-Webformular mit Invisible Recaptcha und serverseitiger Validierung

Ein Kontaktformular gehört als Service auf jede gute Website. Im Prinzip sind Kontaktformulare recht simple HTML-Konstrukte, aber dennoch kommt hier eine Menge Programmierarbeit hinzu, wenn sie richtig funktionieren sollen. Das beginnt mit einer eleganten Kommunikation zwischen Client und Server und endet mit einer vernünftigen Validierung der User-Eingaben auf der Serverseite.

Wenden wir uns zunächst dem simplen Teil zu. So könnte der HTML-Code unseres Formulars aussehen:

<form role="form" id="kontaktformular">
  <div class="form-group">
    <input type="text" class="form-control" id="InputName" name="name" placeholder="Name *">
  </div>
  <div class="form-group">
    <input type="email" class="form-control" id="InputEmail" name="email" placeholder="eMail-Adresse">
  </div>
  <div class="form-group">
    <input type="text" class="form-control" id="InputBetreff" name="betreff" placeholder="Betreff *">
  </div>
  <div class="form-group">
    <textarea class="form-control" id="InputNachricht" rows="8" name="nachricht" placeholder="Anfrage *"></textarea>
  </div>
  <input class="form-control" type="checkbox" name="datenschutz_checkbox" id="datenschutz_checkbox">
  <div id="datenschutz_bestaetigen_text">
    <p>
      Bitte bestätigen Sie vor dem Absenden unsere <a href="datenschutz.php" class="datenschutz-link" id="datenschutz_bestaetigen_link">Datenschutzerklärung</a>
    </p>
  </div>
  <p>Bitte füllen Sie die mit * gekennzeichneten Felder aus. Wenn Sie uns per Kontaktformular Anfragen zukommen lassen, werden Ihre Angaben aus dem Anfrageformular 
  inklusive der von Ihnen dort angegebenen Kontaktdaten zwecks Bearbeitung der Anfrage und für den Fall von 
  Anschlussfragen bei uns gespeichert. Diese Daten geben wir nicht ohne Ihre Einwilligung weiter.
  </p>
  <button type="submit" id="absende_button" class="btn btn-ar btn-primary g-recaptcha" data-sitekey="6Le9G44UAAAAAFWn5iu5F8xeOFeGDCsjflksdjfsd" data-callback="onSubmit">Absenden</button>
  <div id="erfolg_kontaktformular">
    <p></p>
  </div>
</form>

Damit Invisible Recaptcha richtig funktioniert, braucht Ihr einen Site-Key, der clientseitig im Absende-Button unter data-sitekey eingebunden wird und einen Secret-Key, der später serverseitig abgerufen wird. Ansonsten betrachten wir das Verfahren einfach als Black-Box. Die nötigen Daten stellt Google hier zur Verfügung: https://www.google.com/recaptcha. Dort kann man verschiedene Schlüsselpaare für verschiedene Websites anlegen. Damit die Recaptcha-Funktion auf Eurer Website läuft muss die entsprechende API im head-Bereich eingebunden werden. Außerdem binden wir an der Stelle auch gleich jQuery ein. Auf diesem Javascript-Framework basiert der weitere JS-Code:

<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

Am Ende des body-Bereichs unserer Website binden wir den Javascript-Code zum Versenden der Formular-Daten ein:

<script defer>
function onSubmit(token) {
$.post("kontaktformular_senden.php",$("#kontaktformular").serialize(),function(msg){
  if(msg=="erfolg"){
    $("#erfolg_kontaktformular").html("<div class='alert alert-success margin-bottom-30'>\n<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>&times;
                                      </button>\n<strong>Vielen Dank!</strong> Ihre Nachricht wurde an uns versendet!\n</div>\n");
  }
  else
  {  
    if(msg=="fail"){
      $("#erfolg_kontaktformular").html("<div class='alert alert-danger margin-bottom-30'>\n<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>&times;
                                        </button>\n<strong>Fehler!</strong> Ihre Nachricht konnte nicht versendet werden.\n</div>\n");
    }
    if(msg.indexOf("felder") >= 0){
      if(msg.indexOf("1") >= 0){
        $("#InputName").css('background-color','#FF0000');
        $("#InputName").addClass("empty-field");
        $("#InputName").attr("placeholder", "Bitte geben Sie Ihren Namen ein!");
        $("#InputName").css('color','#FFFFFF');
      }
      else
      {
        $("#InputName").css('color','#495057');
        $("#InputName").css('background-color','#FFFFFF');
      }
      if(msg.indexOf("2") >= 0){
        $("#InputBetreff").css('background-color','#FF0000');
        $("#InputBetreff").addClass("empty-field");
        $("#InputBetreff").attr("placeholder", "Bitte geben Sie einen Betreff ein!");
        $("#InputBetreff").css('color','#FFFFFF');
      }
      else
      {
        $("#InputBetreff").css('color','#495057');
        $("#InputBetreff").css('background-color','#FFFFFF');
      }
      if(msg.indexOf("3") >= 0){
        $("#InputNachricht").css('background-color','#FF0000');
        $("#InputNachricht").addClass("empty-field");
        $("#InputNachricht").attr("placeholder", "Bitte geben Sie einen Betreff ein!");
        $("#InputNachricht").css('color','#FFFFFF');
      }
      else
      {
        $("#InputNachricht").css('color','#495057');
        $("#InputNachricht").css('background-color','#FFFFFF');
      }
      if(msg.indexOf("4") >= 0){
        $("#datenschutz_bestaetigen_text").css('color','#FF0000');
        $("#datenschutz_bestaetigen_text a").css('color','#FF0000');
      }
      else
      {
        $("#datenschutz_bestaetigen_text").css('color','#FFFFFF');
        $("#datenschutz_bestaetigen_text a").css('color','#FFFFFF');
      }
    }
    grecaptcha.reset();
  }
});
}
</script>

Ohne auf jedes Detail des Scripts einzugehen, ein paar Anmerkungen dazu. Ihr seht, die Formulardaten werden an das PHP-Script kontaktformular_senden.php per AJAX übermittelt. Gibt das serverseitige Script den String "erfolg" zurück, dann wird im <div>-Element mit der id erfolg_kontaktformular eine entsprechende Erfolgsmeldung angezeigt. Dies wird durch jQuery ausgeführt.

Wird vom Server der String "fail" zurückgegeben hat die Mailfunktion auf Serverseite versagt. Ihr könnt Euch später das serverseitige Script anschauen und die Rückgabewerte anhand der if-Schleifen weiter spezifizieren, aber darum soll es hier erst einmal nicht gehen.

Interessanter sind die folgenden Zeilen in unserem Javascript. Es wird gecheckt, ob im Rückgabewert des Servers das Wort "felder" enthalten ist. Wenn ja, dann möchte uns das Script mitteilen, dass einzelne Formularfelder leer oder mit ungültigem Inhalt übergeben wurden. Jetzt wird geprüft, ob in dem String die Ziffer 1 enthalten ist. Ist dies der Fall, wurde das Im Feld Name kein Wert übergeben. Wie gesagt, auf das serverseitige Script kommen wir gleich. Jetzt schauen wir uns erst einmal an, wie dieses Ereignis clientseitig gehandelt wird. Wir verpassen dem entsprechenden Input-Feld eine rote Hintergrundfarbe und eine weiße Textfarbe. Das Attribut "placeholder" versehen wir mit dem Hinweis "Bitte geben Sie Ihren Namen ein!". Ist keine "1" übergeben wurden, war das Namensfeld ausgefüllt. In diesem Fall wird die Hintergrundfarbe wieder auf weiß und die Vordergrundfarbe auf grau gesetzt. Nach diesem Prinzip wird jedes Formularfeld überprüft, indem ihm verschiedene Ziffern zugeordnet werden.

Am Ende ist ganz entscheidend, dass das Recaptcha wieder zurückgesetzt wird, da es sonst beim nächsten Versuch nicht funktioniert. Das erreicht man mit grecaptcha.reset();

Schauen wir uns an, wie das ganz nun serverseitig aussieht:

 <?php
$fehlerstring="";

if($_POST['name']=="" OR $_POST['betreff']=="" OR $_POST['nachricht']=="" OR !filter_var($_POST['email'], FILTER_VALIDATE_EMAIL) OR !isset($_POST['datenschutz_checkbox'])){
  if($_POST['name']==""){
    $fehlerstring=$fehlerstring."1";
  }
  if($_POST['betreff']==""){
    $fehlerstring=$fehlerstring."2";
  }
  if($_POST['nachricht']==""){
    $fehlerstring=$fehlerstring."3";
  }
  if(!isset($_POST['datenschutz_checkbox'])){
    $fehlerstring=$fehlerstring."4";
  }
  if (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
    $fehlerstring=$fehlerstring."8";
  }
  echo "felder".$fehlerstring;
  exit();
}

function getCurlData($url)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_TIMEOUT, 10);
curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16");
$curlData = curl_exec($curl);
curl_close($curl);
return $curlData;
}

$msg='';
if($_SERVER["REQUEST_METHOD"] == "POST")
{
$recaptcha=$_POST['g-recaptcha-response'];
if(!empty($recaptcha))
{
$google_url="https://www.google.com/recaptcha/api/siteverify";
// Hier müsst Ihr Euren Secret-Key einfügen
$secret='6Le70zUUAAAAAALSqQxhlEGjX5Z6sdgsdfsdfg';
$ip=$_SERVER['REMOTE_ADDR'];
$url=$google_url."?secret=".$secret."&response=".$recaptcha."&remoteip=".$ip;
$res=getCurlData($url);
$res= json_decode($res, true);
//reCaptcha success check 
if($res['success'])
{
  $to = 'info@zwanzig12-webdesign.de';
  $subject = 'Kontaktformular zwanzig12 Website';
  $headers = 'From: ' . $_POST['email'] . "\r\n" .
  'Reply-To: ' . $_POST['email'] . "\r\n" .
  'Content-type: text/html; charset=utf-8' . "\r\n" .
  'X-Mailer: PHP/' . phpversion();  

  $message = 'Name: ' . $_POST['name'] . "<br>" .
         'E-mail: ' . $_POST['email'] . "<br>" .
         'Betreff: ' . $_POST['betreff'] . "<br>" .
         'Nachricht: ' . $_POST['nachricht'];
         
  if(mail($to, $subject, $message, $headers)){  
    echo "erfolg";
  }
  else
  {
    echo "fail";
  }
}

else
  
  {
    echo "fail2";
  }
}
else
  
  {
    echo "fail2";
  }
}
else
  
  {
    echo "fail2";
  }
?>

Wichtig an dem Script ist mir hier in erster Linie einmal der erste Teil, mit der Validierung der clientseitig übermittelten Daten. In der ersten if-Schleife wird gecheckt, ob eines der Pflichtfelder leer ist, oder ob das eMail-Feld keine sinnvolle eMail-Adresse enthält. Je nachdem, was festgestellt wurde, wird dem Fehlerstring, der an den Client zurück übermittelt wird, die entsprechende Ziffer angehängt. Damit kann das clientseitige Script, das wird bereits betrachtet haben, die entsprechende Meldung ausgeben und das Formularfeld mit jQuery hervorheben.

Sind alle Felder korrekt ausgefüllt, beginnt das serverseitige Script mit der Recaptcha-Validierung und sendet im Erfolgsfall die Kontaktformular-Daten an eine im Script festgelegte eMail-Adresse. Wichtig ist, dass der korrekte Secret-Key im serverseitigen Script hinterlegt wird, denn sonst schlägt der Versand fehl.

Und nun viel Spaß beim Nach- und Umbauen für Eure Zwecke. Bei Fragen könnt Ihr Euch gerne an mich wenden unter info(at)zwanzig12-webdesign.de.