Montag, 27. Juli 2020

ASP.net WebForms und "Googles" ReCaptcha

Nach verschiedenen Ausflügen in andere Bereiche meines Berufes (Desktop-, Mobile, IoT und reine HTML-Entwicklung) bin ich wieder bei einem Lieblinge angekommen den WebForms in ASP.net. Dort habe ich schon verschiedentlich "Captchas" eingebaut, auch das "ReCaptcha" von "Google", allerdings die Version 1.0, die nicht mehr unterstütz wird. Da gab es eine kleine DLL, die einem die meiste Arbeit abnahm, auf die wollte und konnte ich mich jetzt nicht verlassen.

Also habe ich ein wenig herumgesucht und bin, wie zumeist, bei "Stackoverflow" fündig geworden. Wie immer mußte man verschiedene Lösungsansätze zusammenbringen, um ein Ergebnis zu erzielen. Da es diesbezüglich Interesse zu geben scheint, will ich meinen Ansatz hier veröffentlichen.


Zunächst bauen wir den Zugriff auf die "Google-API" in den "Header" in der "Master.aspx" oder, wenn Ihr ohne "Master" arbeitet, in den Header der ".aspx"-Page ein, wo das "ReCaptcha" erscheinen soll:
Ich habe das so gelöst, daß ich in der "Master" einen "Content-Bereich für "JavaScript" in den Header geschrieben habe, benötige ich jetzt auf einer Seite spezielles "JavaScript", dann kann ich das auf den Pages bequem im Content plazieren. Dort kommt z.B. auch der HTML-Editor für die User rein, den benötige ich ja auch nicht auf allen Seiten.
Das sieht dann so aus:

Master.aspx

(...)
<html lang="en">
<head runat="server">
(...)
    <asp:ContentPlaceHolder ID="ScriptContent" runat="server">
    </asp:ContentPlaceHolder>
(...)
</head>
(...)


bei mir ist das die "Contact.aspx", da kommt rein:

(...)
<asp:Content ID="Content0" ContentPlaceHolderID="ScriptContent" runat="server">
(...)
    <script type="text/javascript" src="https://www.google.com/recaptcha/api.js" async            defer></script>
</asp:Content>
(...)


Ansonsten baut den Teil irgendwie in den "Header" ein:
<script type="text/javascript" src="https://www.google.com/recaptcha/api.js" async defer></script>

und anschließend den DIV-Tag, wo das ReCptcha-Widget gerendert werden soll:

<div id="dvCaptcha" class="g-recaptcha" data-sitekey="<%: ReCaptcha_Key %>"></div>


Den "ReCaptcha_Key" machen wir uns im Quellcode zugänglich (ich arbeite ausschließlich mit C#. solltet Ihr VB oder etwas ähnliches verwenden, müßt Ihr das leicht anpassen).

Bei mir heißt dann die "CodeBehind": Contact.aspx.cs und die startet mit:

{
    public partial class Contact : Page
    {
        (...)
        protected static string ReCaptcha_Key = "<RECaptcha Site Key>";

        protected static string ReCaptcha_Secret = "<RECaptcha Secret Key>";
(...)


Diese Declaration macht die Variabeln für das "ReCaptch" pagewide verfügbar und wir konnten den "ReCaptcha_Key" auf der "aspx" einfügen.

Jetzt müssen wir unserem "ReCaptcha" Leben einhauchen (Funktionalität geben), sonst sieht es zwar schön aus, wird aber keinen Einfluß auf die Validierung unseres Formulars haben. Da ich grundsätzlich alle Daten validiere, habe ich diese Funktion einfach um einen Methodeaufruf erweitert:

        protected void btnSendMessage_Click(object sender, EventArgs e)
        {
(...)
            try
            {
(...)
                if (!IsValidCaptcha()) throw new ValidationException("The ReCaptcha is wrong!");
(...)
            catch (Exception ex)
            {
                lblResult.Text = ex.Message;
                lblResult.ForeColor = System.Drawing.Color.Red;
            }
        }


Jetzt müssen wir nur noch das "ReCaptcha" validieren lassen:

        public bool IsValidCaptcha()
        {
            string resp = Request["g-recaptcha-response"];
            string quest = string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", ReCaptcha_Secret, resp);
            var req = (HttpWebRequest)WebRequest.Create
                      (quest);
            using (WebResponse wResponse = req.GetResponse())
            {
                using (StreamReader readStream = new StreamReader(wResponse.GetResponseStream()))
                {
                    string jsonResponse = readStream.ReadToEnd();
                    JavaScriptSerializer js = new JavaScriptSerializer();
                    // Deserialize Json
                    CaptchaResult data = js.Deserialize<CaptchaResult>(jsonResponse);
                    if (Convert.ToBoolean(data.success))
                    {
                        return true;
                    }
                }
            }
            return false;
        }
        public class CaptchaResult
        {
            public string success { get; set; }
        }


Da es jetzt haufenweise Fehlermeldungen hagelt, fügt bitte an entsprechender Stelle noch die "Using-Anweisungen" ein:

using System.Net;
using System.IO;
using System.Web.Script.Serialization;
using System.ComponentModel.DataAnnotations;


Hier noch einige Links, die Ihr ggf. brauchen könnt.

ReCaptcha Homepage: https://www.google.com/recaptcha/intro/v3.html

Falls Du schon einen Account bei Google hast, kommst Du hier zur richtigen Admin-Console:
https://www.google.com/recaptcha/admin/site/xxxxxxx

Und hier habe ich die Lösung ursprünglich gefunden:
https://stackoverflow.com/questions/27764692/validating-recaptcha-2-no-captcha-recaptcha-in-asp-nets-server-side

und ein herzliches Danke schön an Tabish Usman.