How to implement reCaptcha V3 in ASP.NET
Asked Answered
M

5

25

Does anyone have a full implementation demo of reCaptcha V3 in ASP.NET?

I found this article: Google Recaptcha v3 example demo

At the moment I am using reCaptcha V2 with the following code:

public bool RecaptchaValidate()
    {
        string Response = Request.Form["g-recaptcha-response"];//Getting Response String Append to Post Method
        bool Valid = false;
        //Request to Google Server
        var CaptchaSiteKey = Settings["NewUserRegCaptchaSecretSiteKey"].ToString();
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create
        (" https://www.google.com/recaptcha/api/siteverify?secret=" + CaptchaSiteKey + "&response=" + Response);
        try
        {
            //Google recaptcha Response
            using (WebResponse wResponse = req.GetResponse())
            {

                using (StreamReader readStream = new StreamReader(wResponse.GetResponseStream()))
                {
                    string jsonResponse = readStream.ReadToEnd();

                    JavaScriptSerializer js = new JavaScriptSerializer();
                    ReCaptchaObject data = js.Deserialize<ReCaptchaObject>(jsonResponse);// Deserialize Json

                    Valid = Convert.ToBoolean(data.success);
                }
            }

            return Valid;
        }
        catch (WebException ex)
        {
            throw ex;
        }
    }

On the view.ascx page I have:

<%@ Register TagPrefix="recaptcha" Namespace="Recaptcha" Assembly="Recaptcha" %>

<script src='https://www.google.com/recaptcha/api.js'></script>

<scrip>
var recap = grecaptcha.getResponse();
if (recap.length == 0) {
                $("#verifyhuman").css("display", "block");
            }
</script>

 <div class="g-recaptcha" data-sitekey="<%=ReCaptchaPublicKey%>" id="recaptcha" data-callback="recaptchaCallback"></div>
Muleteer answered 3/12, 2018 at 8:32 Comment(6)
Hope it helps mehmetcakmaz.com.tr/google-recaptcha-v3-kullanimiHulky
Can someone give an answer without using MVC? The link above makes more sense but it is in Turkish.Muleteer
Did you try to translate it?Hulky
You looking to use this with WebForms?Patrilineal
I'd add the '<WebMethod>' attribute above your RecaptchaValidate method and call it with $.ajax.Patrilineal
I would just like to mention that since using ReCaptcha V3, I have been getting a lot of false positives. Yes, you can change the threshold but there is no way for a user to be able to prove they are indeed a human once the code prevents them sending a form or etc. I would say, stay with V2 for now until Google improves this.Muleteer
E
2

The accepted answer isn't following the Google's spec for sending the response and checking the action. Its Http requests will exhaust the number of sockets also. This is my implementation.

Browser

// Could be called from an event or another piece of code.
function FunctionToCall(term) {

    // Google reCaptcha check
    grecaptcha.ready(function() {
        grecaptcha.execute(reCaptchaSiteKey, {action: "search"}).then(function(token) {

            // You can take the response token Google returns, check it server side using
            // the GoogleReCaptcha class and respond with a pass or fail. If a pass, run a block of code client side.
            // { ... block of code ... }
            
            // Or if you want to secure an endpoint that your sending request too. 
            // Send the response token with the request to your endpoint and check the response token server side and respond with a pass or fail.
            // Use the repsonse to show a message or redirect site, etc

        });
    });

}

Server

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

public class GoogleReCaptcha
{
    public class ReCaptchaResponse
    {
        public bool success { get; set; }
        public double score { get; set; }
        public string action { get; set; }
        public DateTime challenge_ts { get; set; }
        public string hostname { get; set; }
        [JsonProperty("error-codes")]
        public List<string> error_codes { get; set; }
    }

    public static async Task<(ReCaptchaResponse Response, bool HasPassed)> ReCaptchaPassed(string secretKey, string gRecaptchaToken, string expected_action)
    {
        try
        {
            // validate 
            if (string.IsNullOrWhiteSpace(secretKey) || string.IsNullOrWhiteSpace(gRecaptchaToken) || string.IsNullOrWhiteSpace(expected_action))
                return (null, false);

            // we use HttpClientFactory to avoid exhausting number of sockets available
            var httpClient = HttpClientFactory.Create();

            var verifyUrl = "https://www.google.com/recaptcha/api/siteverify";
            var parameters = new Dictionary<string, string>
            {
                {"secret", secretKey},
                {"response", gRecaptchaToken}
                //{"remoteip", "ip" } <= this is optional
            };
            using (HttpContent formContent = new FormUrlEncodedContent(parameters))
            {
                using (var response = await httpClient.PostAsync(verifyUrl, formContent).ConfigureAwait(false))
                {
                    // check HTTP response code
                    if (response.StatusCode != HttpStatusCode.OK)
                        return (null, false);

                    // get reCaptcha response
                    string gRecaptchaJsonresult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                    if (string.IsNullOrWhiteSpace(gRecaptchaJsonresult))
                        return (null, false);


                    // check reCaptcha response is successful
                    var recaptcha_response = JsonConvert.DeserializeObject<ReCaptchaResponse>(gRecaptchaJsonresult);
                    if (recaptcha_response == null)
                    {
                        //Logging.Log(new Logging.LogItem { Msg = $"Google RecCaptcha response is null" }, DefaultLogValues);
                        return (recaptcha_response, false);
                    }

                    if (!recaptcha_response.success)
                    {
                        var errors = string.Join(",", recaptcha_response.error_codes);
                        //Logging.Log(new Logging.LogItem { Msg = $"Google RecCaptcha error codes:\n{errors}" }, DefaultLogValues);
                        return (recaptcha_response, false);
                    }

                    // check reCaptcha response action
                    if (recaptcha_response.action.ToUpper() != expected_action.ToUpper())
                    {
                        //Logging.Log(new Logging.LogItem { Msg = $"Google RecCaptcha action doesn't match:\nExpected action: {expected_action} Given action: {recaptcha_response.action}" }, DefaultLogValues);
                        return (recaptcha_response, false);
                    }

                    // response score
                    // anything less than 0.5 is a bot
                    if (recaptcha_response.score < 0.5)
                        return (recaptcha_response, false);
                    else
                        return (recaptcha_response, true);
                }
            }
        }
        catch (Exception ex)
        {
            //Logging.Log(ex, DefaultLogValues);

            // default to false
            return (null, false);
        }
    }
}

You would call it like so..

var reCaptchaTask = GoogleReCaptcha.ReCaptchaPassed(Settings.GoogleReCaptcha.secret_key, SearchReq.gRecaptchaToken, "search");

Make sure to put your keys in a settings file and not in the code.

Enlist answered 31/5, 2022 at 13:47 Comment(0)
G
53

The simplest implementation:

  1. In your cshtml file (at the top)

    @section Scripts
    {
        <script src="https://www.google.com/recaptcha/api.js?render=your site key"></script>
        <script>
            grecaptcha.ready(function () {
                grecaptcha.execute('your site key', { action: 'homepage' }).then(function (token) {
                    document.getElementById("foo").value = token;
                });
            });
        </script>
    }
    
  2. In your cshtml, inside the form (just before </form>):

    <input type="hidden" id="foo" name="foo" />
    
  3. A function inside your Pagemodel class. See the documentation for the response object:

    public static bool ReCaptchaPassed(string gRecaptchaResponse)
    {
        HttpClient httpClient = new HttpClient();
    
        var res = httpClient.GetAsync($"https://www.google.com/recaptcha/api/siteverify?secret=your secret key no quotes&response={gRecaptchaResponse}").Result;
    
        if (res.StatusCode != HttpStatusCode.OK) 
        {
            return false;
        }
        string JSONres = res.Content.ReadAsStringAsync().Result;
        dynamic JSONdata = JObject.Parse(JSONres);
    
        if (JSONdata.success != "true" || JSONdata.score <= 0.5m)
        {
            return false;
        }
    
        return true;
    }
    
  1. Finally, inside your OnPostAsync() handler, at the top:

    if (!ModelState.IsValid) 
    {
        return Page();
    }
    else
    {
        if (!ReCaptchaPassed(Request.Form["foo"]))
        {
            ModelState.AddModelError(string.Empty, "You failed the CAPTCHA.");
            return Page();
        }
    }
    
Grapevine answered 20/6, 2019 at 15:38 Comment(10)
Re: c) doc says it should be a POST not a GET developers.google.com/recaptcha/docs/verifyIntellectualize
ReCaptchaPassed() is returning if the captcha token is valid. I think you want to return JSONdata.number and test if the number is over a certain amount to see if they passed the captcha.Arelus
Mike W, it has no meaning to be in GET. You want to test the captcha after the submit button is pressed. I use it in POST and works fine.Grapevine
I got this to work, but some additions: you didn't mention adding a "form submit button" (Possibly assumed). Also I had to deserialize the response JSON.Override
Be careful with the HttpClient usage. In this way it will exhaust the sockets on your server. thecodebuzz.com/…Fairtrade
is there any way to call on page load rather then a button submit or post the page?Lamoree
@Salim: You likely don't want to retrieve the token on page load, as the token expires within two minutes. As such, if a user takes more than two minutes to fill out a form after loading the page, the reCAPTCHA token validation will fail.Scoot
What is "foo"? Surely we could name it better than foo.Laoag
@Andrew, "foo" is a field to hold the recaptcha token that is then passed to the backend.Yeung
Apart from all the things said, inside of that ReCaptchaPassed method, you should also check jsonData.action value whether to see it is matched with the value you passed on your view for additional security measureFatherhood
A
16

Edit : I have added a demo project . Check this github repository . https://github.com/NIHAR-SARKAR/GoogleRecaptchav3-example-In-asp.net

From frontend (.aspx page) you need to send ajax request to pass the token to backend server . Using "recaptcha.execute" U can get the response , and pass the token using ajax request .Please check the code block .

 <script src="http://www.google.com/recaptcha/api.js?render=recaptchaSiteKey"></script>
<script>
 grecaptcha.ready(function() {
 grecaptcha.execute('recaptchaSiteKey', {action: 'homepage'}).then(function(token) {

            $.ajax({
                //pass the toket to Webmethod using Ajax
            });
          });
     });
</script>

Reference link: https://developers.google.com/recaptcha/docs/verify https://developers.google.com/recaptcha/docs/display#js_api

Now in the aspx.cs you need to write a "[WebMethod]" to receive the token from Ajax request .

    [WebMethod]
    public static void CaptchaVerify(string token)
    {
            var responseString = RecaptchaVerify(token);
            ResponseToken response = new ResponseToken();
            response = Newtonsoft.Json.JsonConvert.DeserializeObject<ResponseToken>(responseString.Result);

    }

To get the response from google recapcha api u need to use async call using httpClient . you also need to create a class which will contain same properties like the response string . After getting the "responseString" u need to convert the response to ResponseToken object by using Newtonsoft.Json. response = Newtonsoft.Json.JsonConvert.DeserializeObject<ResponseToken>(responseString.Result);

private string apiAddress = "https://www.google.com/recaptcha/api/siteverify";

private string recaptchaSecret = googleRecaptchaSecret;

        public async Task<string> RecaptchaVerify(string recaptchaToken)
        {
            string url = $"{apiAddress}?secret={recaptchaSecret}&response={recaptchaToken}";
            using (var httpClient = new HttpClient())
            {
                try
                {

                    string responseString=  httpClient.GetStringAsync(url).Result;
                    return responseString;

                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
            }
        }


        public class ResponseToken
        {

            public DateTime challenge_ts { get; set; }
            public float score { get; set; }
            public List<string> ErrorCodes { get; set; }
            public bool Success { get; set; }
            public string hostname { get; set; }
        }
Alyss answered 28/3, 2019 at 5:37 Comment(3)
Be careful with the HttpClient usage. In this way it will exhaust the sockets on your server. thecodebuzz.com/…Fairtrade
@Nihar Sarkar I have taken your fantastic test project to learn from, did a ton of Googling and learning, and built an "easy to use" solution for the ASP.NET Framework that anyone should be able to implement. I'd be curious to learn your opinion of this. I do mention you as the primary source of knowledge for much of the project: github.com/WestleyBennett/reCAPTCHAv3Economic
@Westley Bennett your source code looks good .Alyss
E
9

The accepted answer on this page is totally wrong!!! Google returns a score between 0 and 1 to indicate whether the submission is likely to be a bot or likely to be a human.

The success property returned only means that the recaptcha token was processed correctly.

It is the score property that should be checked, not the success property

These lines are the probelem

if (JSONdata.success != "true")
    return false;

return true;

The actual score to compare will probably be in a variable that can be adjusted if need be. Google recommends starting with 0.5.

So the code should change to something like:

var recaptchaScore = 0.5m; // this could be in appSettings or whereever/however you are storing your constants

if (JSONdata.success != "true" || JSONdata.score <= recaptchaScore)
    return false;

return true;

Of course you will likely want to add logging etc to this answer but this is the bare logic that is required.

Endocarp answered 20/3, 2021 at 11:6 Comment(0)
D
2

There are several Recaptcha libraries available for ASP.Net. I chose to use reCAPTCHA.AspNetCore because it provides an HtmlHelper.

Please note that this library only supports one ReCatpcha per page, and it doesn't support Recaptcha v3 passive monitoring on non-form pages.

Downtrodden answered 11/12, 2019 at 22:31 Comment(0)
E
2

The accepted answer isn't following the Google's spec for sending the response and checking the action. Its Http requests will exhaust the number of sockets also. This is my implementation.

Browser

// Could be called from an event or another piece of code.
function FunctionToCall(term) {

    // Google reCaptcha check
    grecaptcha.ready(function() {
        grecaptcha.execute(reCaptchaSiteKey, {action: "search"}).then(function(token) {

            // You can take the response token Google returns, check it server side using
            // the GoogleReCaptcha class and respond with a pass or fail. If a pass, run a block of code client side.
            // { ... block of code ... }
            
            // Or if you want to secure an endpoint that your sending request too. 
            // Send the response token with the request to your endpoint and check the response token server side and respond with a pass or fail.
            // Use the repsonse to show a message or redirect site, etc

        });
    });

}

Server

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

public class GoogleReCaptcha
{
    public class ReCaptchaResponse
    {
        public bool success { get; set; }
        public double score { get; set; }
        public string action { get; set; }
        public DateTime challenge_ts { get; set; }
        public string hostname { get; set; }
        [JsonProperty("error-codes")]
        public List<string> error_codes { get; set; }
    }

    public static async Task<(ReCaptchaResponse Response, bool HasPassed)> ReCaptchaPassed(string secretKey, string gRecaptchaToken, string expected_action)
    {
        try
        {
            // validate 
            if (string.IsNullOrWhiteSpace(secretKey) || string.IsNullOrWhiteSpace(gRecaptchaToken) || string.IsNullOrWhiteSpace(expected_action))
                return (null, false);

            // we use HttpClientFactory to avoid exhausting number of sockets available
            var httpClient = HttpClientFactory.Create();

            var verifyUrl = "https://www.google.com/recaptcha/api/siteverify";
            var parameters = new Dictionary<string, string>
            {
                {"secret", secretKey},
                {"response", gRecaptchaToken}
                //{"remoteip", "ip" } <= this is optional
            };
            using (HttpContent formContent = new FormUrlEncodedContent(parameters))
            {
                using (var response = await httpClient.PostAsync(verifyUrl, formContent).ConfigureAwait(false))
                {
                    // check HTTP response code
                    if (response.StatusCode != HttpStatusCode.OK)
                        return (null, false);

                    // get reCaptcha response
                    string gRecaptchaJsonresult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                    if (string.IsNullOrWhiteSpace(gRecaptchaJsonresult))
                        return (null, false);


                    // check reCaptcha response is successful
                    var recaptcha_response = JsonConvert.DeserializeObject<ReCaptchaResponse>(gRecaptchaJsonresult);
                    if (recaptcha_response == null)
                    {
                        //Logging.Log(new Logging.LogItem { Msg = $"Google RecCaptcha response is null" }, DefaultLogValues);
                        return (recaptcha_response, false);
                    }

                    if (!recaptcha_response.success)
                    {
                        var errors = string.Join(",", recaptcha_response.error_codes);
                        //Logging.Log(new Logging.LogItem { Msg = $"Google RecCaptcha error codes:\n{errors}" }, DefaultLogValues);
                        return (recaptcha_response, false);
                    }

                    // check reCaptcha response action
                    if (recaptcha_response.action.ToUpper() != expected_action.ToUpper())
                    {
                        //Logging.Log(new Logging.LogItem { Msg = $"Google RecCaptcha action doesn't match:\nExpected action: {expected_action} Given action: {recaptcha_response.action}" }, DefaultLogValues);
                        return (recaptcha_response, false);
                    }

                    // response score
                    // anything less than 0.5 is a bot
                    if (recaptcha_response.score < 0.5)
                        return (recaptcha_response, false);
                    else
                        return (recaptcha_response, true);
                }
            }
        }
        catch (Exception ex)
        {
            //Logging.Log(ex, DefaultLogValues);

            // default to false
            return (null, false);
        }
    }
}

You would call it like so..

var reCaptchaTask = GoogleReCaptcha.ReCaptchaPassed(Settings.GoogleReCaptcha.secret_key, SearchReq.gRecaptchaToken, "search");

Make sure to put your keys in a settings file and not in the code.

Enlist answered 31/5, 2022 at 13:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.