web-dev-qa-db-fra.com

Validation de Recaptcha 2 (pas de CAPTCHA reCAPTCHA) côté serveur ASP.NET

Le nouveau Recaptcha 2 semble prometteur, mais je n'ai pas trouvé le moyen de le valider côté serveur ASP.NET,

if(Page.IsValid) dans Cette réponse , est valable pour l'ancien Recaptcha, mais pas pour le nouveau,

Comment valider le nouveau reCAPTCHA côté serveur?

64
Alaa

Après avoir lu de nombreuses ressources, j'ai fini par écrire cette classe pour gérer la validation de le nouveau ReCaptcha :

Comme mentionné Ici : Lorsqu'un utilisateur final résout un reCAPTCHA, un nouveau champ (g-recaptcha-response) sera renseigné au format HTML.

Nous devons lire cette valeur et la transmettre à la classe ci-dessous pour la valider:

En C #:

Dans le code derrière votre page:

string EncodedResponse = Request.Form["g-Recaptcha-Response"];
bool IsCaptchaValid = (ReCaptchaClass.Validate(EncodedResponse) == "true" ? true : false);

if (IsCaptchaValid) {
    //Valid Request
}

La classe:

  using Newtonsoft.Json;

    public class ReCaptchaClass
    {
        public static string Validate(string EncodedResponse)
        {
            var client = new System.Net.WebClient();

            string PrivateKey = "6LcH-v8SerfgAPlLLffghrITSL9xM7XLrz8aeory";

            var GoogleReply = client.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", PrivateKey, EncodedResponse));

            var captchaResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<ReCaptchaClass>(GoogleReply);

            return captchaResponse.Success.ToLower();
        }

        [JsonProperty("success")]
        public string Success
        {
            get { return m_Success; }
            set { m_Success = value; }
        }

        private string m_Success;
        [JsonProperty("error-codes")]
        public List<string> ErrorCodes
        {
            get { return m_ErrorCodes; }
            set { m_ErrorCodes = value; }
        }


        private List<string> m_ErrorCodes;
    }

En VB.NET:

Dans le code derrière votre page:

Dim EncodedResponse As String = Request.Form("g-Recaptcha-Response")
    Dim IsCaptchaValid As Boolean = IIf(ReCaptchaClass.Validate(EncodedResponse) = "True", True, False)

    If IsCaptchaValid Then
        'Valid Request
    End If

La classe:

Imports Newtonsoft.Json


Public Class ReCaptchaClass
    Public Shared Function Validate(ByVal EncodedResponse As String) As String
        Dim client = New System.Net.WebClient()

        Dim PrivateKey As String = "6dsfH-v8SerfgAPlLLffghrITSL9xM7XLrz8aeory"

        Dim GoogleReply = client.DownloadString(String.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", PrivateKey, EncodedResponse))

        Dim captchaResponse = Newtonsoft.Json.JsonConvert.DeserializeObject(Of ReCaptchaClass)(GoogleReply)

        Return captchaResponse.Success
    End Function

    <JsonProperty("success")> _
    Public Property Success() As String
        Get
            Return m_Success
        End Get
        Set(value As String)
            m_Success = value
        End Set
    End Property
    Private m_Success As String

    <JsonProperty("error-codes")> _
    Public Property ErrorCodes() As List(Of String)
        Get
            Return m_ErrorCodes
        End Get
        Set(value As List(Of String))
            m_ErrorCodes = value
        End Set
    End Property

    Private m_ErrorCodes As List(Of String)

End Class
135
Alaa

Voici une version qui utilise le JavaScriptSerializer. Merci Ala pour la base de ce code.

Paramètre de l'application WebConfig - J'ai ajouté la clé secrète à Web.Config dans mon cas pour autoriser les transformations entre environnements. Il peut également être facilement crypté ici si nécessaire.

<add key="Google.ReCaptcha.Secret" value="123456789012345678901234567890" />

La classe ReCaptcha - Une classe simple pour publier le paramètre de réponse avec votre secret sur Google et le valider. La réponse est désérialisée à l'aide de la classe .Net JavaScriptSerializer et renvoyée de cette valeur true ou false.

using System.Collections.Generic;
using System.Configuration;

public class ReCaptcha
{   
    public bool Success { get; set; }
    public List<string> ErrorCodes { get; set; }

    public static bool Validate(string encodedResponse)
    {
        if (string.IsNullOrEmpty(encodedResponse)) return false;

        var client = new System.Net.WebClient();
        var secret = ConfigurationManager.AppSettings["Google.ReCaptcha.Secret"];

        if (string.IsNullOrEmpty(secret)) return false;

        var googleReply = client.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secret, encodedResponse));

        var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();

        var reCaptcha = serializer.Deserialize<ReCaptcha>(googleReply);

        return reCaptcha.Success;
    }
}

Validez la réponse - Vérifiez la validité du paramètre de formulaire g-Recaptcha-Response dans votre contrôleur (ou le code correspondant à un formulaire Web) et prenez les mesures appropriées.

var encodedResponse = Request.Form["g-Recaptcha-Response"];
var isCaptchaValid = ReCaptcha.Validate(encodedResponse);

if (!isCaptchaValid)
{
    // E.g. Return to view or set an error message to visible
}   
39
Paul

La plupart de ces réponses semblent plus complexes que nécessaire. Ils ne spécifient pas non plus l'adresse IP permettant d'éviter une attaque par interception ( https://security.stackexchange.com/questions/81865/ using-recaptcha ). Voici ce sur quoi je me suis installé

public bool CheckCaptcha(string captchaResponse, string ipAddress)
{
    using (var client = new WebClient())
    {
        var response = client.DownloadString($"https://www.google.com/recaptcha/api/siteverify?secret={ ConfigurationManager.AppSettings["Google.ReCaptcha.Secret"] }&response={ captchaResponse }&remoteIp={ ipAddress }");
        return (bool)JObject.Parse(response)["success"];
    }
}
12
Luke McGregor

Vous pouvez utiliser la méthode "IsValidCaptcha ()" pour valider votre recaptcha google côté serveur. Remplacez votre clé secrète par "YourRecaptchaSecretkey" dans la méthode suivante.

Public bool IsValidCaptcha()
 {
  string resp = Request["g-recaptcha-response"];
  var req = (HttpWebRequest)WebRequest.Create
            (https://www.google.com/recaptcha/api/siteverify?secret=+ YourRecaptchaSecretkey + "&response=" + resp);
     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;
 }

Créez également la classe suivante.

public class CaptchaResult
  {
   public string success { get; set; }
  }

Référence link

6
Tabish Usman

Voici ma fourchette de la solution d'Ala pour:

  • envoyer un paramètre en POST
  • pour désinfecter la saisie de formulaire
  • inclure l'adresse IP du demandeur
  • stocker le secret dans Web.Config:

Dans le contrôleur:

bool isCaptchaValid = await ReCaptchaClass.Validate(this.Request);
if (!isCaptchaValid)
{       
    ModelState.AddModelError("", "Invalid captcha");
    return View(model);
}

La classe utilitaire: 

public class ReCaptchaClass
{
    private static ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    private static string SecretKey = System.Configuration.ConfigurationManager.AppSettings["Google.ReCaptcha.Secret"];
    [JsonProperty("success")]
    public bool Success { get; set; }
    [JsonProperty("error-codes")]
    public List<string> ErrorCodes { get; set; }

    public static async Task<bool> Validate(HttpRequestBase Request)
    {
        string encodedResponse = Request.Form["g-Recaptcha-Response"];          
        string remoteIp = Request.UserHostAddress;          
        using (var client = new HttpClient())
        {
            var values = new Dictionary<string, string>
            {
               {"secret", SecretKey},
               {"remoteIp", remoteIp},
               {"response", encodedResponse}
            };
            var content = new FormUrlEncodedContent(values);
            var response = await client.PostAsync("https://www.google.com/recaptcha/api/siteverify", content);
            var responseString = await response.Content.ReadAsStringAsync();
            var captchaResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<ReCaptchaClass>(responseString);
            if ((captchaResponse.ErrorCodes?.Count ?? 0) != 0)
            {
                log.Warn("ReCaptcha errors: " + string.Join("\n", captchaResponse.ErrorCodes));
            }
            return captchaResponse.Success;
        }
    }       
}
3
Olivier de Rivoyre

Selon le doc , vous venez de poster votre clé secrète et la réponse de l’utilisateur à l’API et de lire la propriété "success"

RÉPONSE COURTE:

        var webClient = new WebClient();
        string verification = webClient.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secretKey, userResponse));
        if (JObject.Parse(verification)["success"].Value<bool>())
        {
            // SUCCESS!!!

EXEMPLE COMPLET:

Supposons que vous implémentiez this page dans IamNotARobotLogin.cshtml.

<head>
 <script src="https://www.google.com/recaptcha/api.js" async defer></script>
</head>
<body>
<form action="Login" method="POST">
  <div class="g-recaptcha" data-sitekey="your_site_key"></div><br/>
  <input type="submit" value="Log In">
</form>
</body>

Et supposons que vous souhaitiez que le contrôleur soit enregistré, disons, indicateur "I_AM_NOT_ROBOT" dans la session si la vérification a réussi:

    public ActionResult IamNotARobotLogin()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Login()
    {
        const string secretKey = "6LcH-v8SerfgAPlLLffghrITSL9xM7XLrz8aeory";
        string userResponse = Request.Form["g-Recaptcha-Response"];

        var webClient = new System.Net.WebClient();
        string verification = webClient.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secretKey, userResponse));

        var verificationJson = Newtonsoft.Json.Linq.JObject.Parse(verification);
        if (verificationJson["success"].Value<bool>())
        {
            Session["I_AM_NOT_A_ROBOT"] = "true";
            return RedirectToAction("Index", "Demo");
        }

        // try again:
        return RedirectToAction("IamNotARobotLogin");
    }
3
epox

Un autre exemple est posté ici:

RecaptchaV2.NET (Github)

Il implémente également l'option de jeton sécurisé de Recaptcha 2.0 (regardez le code source complet pour ce bit, j'ai supprimé les morceaux de code pertinents UNIQUEMENT pour valider un résultat).

Celui-ci ne repose pas sur l'analyseur json de newtonsoft, mais utilise celui intégré .NET.

Voici l'extrait de code pertinent de la bibliothèque RecaptchaV2.NET (à partir de recaptcha.cs):

namespace RecaptchaV2.NET
{
  /// <summary>
  /// Helper Methods for the Google Recaptcha V2 Library
  /// </summary>
  public class Recaptcha
  {

    public string SiteKey { get; set; }
    public string SecretKey { get; set; }
    public Guid SessionId { get; set; }

    /// <summary>
    /// Validates a Recaptcha V2 response.
    /// </summary>
    /// <param name="recaptchaResponse">g-recaptcha-response form response variable (HttpContext.Current.Request.Form["g-recaptcha-response"])</param>
    /// <returns>RecaptchaValidationResult</returns>
    public RecaptchaValidationResult Validate(string recaptchaResponse)
    {
      RecaptchaValidationResult result = new RecaptchaValidationResult();

      HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://www.google.com/recaptcha/api/siteverify?secret=" + SecretKey + "&response="
        + recaptchaResponse + "&remoteip=" + GetClientIp());
      //Google recaptcha Response
      using (WebResponse wResponse = req.GetResponse())
      {
        using (StreamReader readStream = new StreamReader(wResponse.GetResponseStream()))
        {
          string jsonResponse = readStream.ReadToEnd();

          JavaScriptSerializer js = new JavaScriptSerializer();
          result = js.Deserialize<RecaptchaValidationResult>(jsonResponse.Replace("error-codes", "ErrorMessages").Replace("success", "Succeeded"));// Deserialize Json
        }
      }

      return result;
    }

    private string GetClientIp()
    {
      // Look for a proxy address first
      String _ip = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

      // If there is no proxy, get the standard remote address
      if (string.IsNullOrWhiteSpace(_ip) || _ip.ToLower() == "unknown")
        _ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];

      return _ip;
    }
  }

  public class RecaptchaValidationResult
  {
    public RecaptchaValidationResult()
    {
      ErrorMessages = new List<string>();
      Succeeded = false;
    }

    public List<string> ErrorMessages { get; set; }
    public bool Succeeded { get; set; }

    public string GetErrorMessagesString()
    {
      return string.Join("<br/>", ErrorMessages.ToArray());
    }
  }
}
1
Matt

L'API Google ReCaptcha n'accepte plus les données utiles en tant que paramètres de chaîne de requête dans une requête GET. Google renvoyait toujours une "fausse" réponse de succès, sauf si j'envoyais les données via HTTP POST. Voici une mise à jour de la classe (excellente!) D'Ala, qui envoie la charge utile au noeud final du service Google:

using Newtonsoft.Json;
using System.Net;
using System.IO;
using System.Text;

public class RecaptchaHandler
{
    public static string Validate(string EncodedResponse, string RemoteIP)
    {
        var client = new WebClient();

        string PrivateKey = "PRIVATE KEY";

        WebRequest req = WebRequest.Create("https://www.google.com/recaptcha/api/siteverify");
        string postData = String.Format("secret={0}&response={1}&remoteip={2}",
                                         PrivateKey,
                                         EncodedResponse,
                                         RemoteIP);

        byte[] send = Encoding.Default.GetBytes(postData);
        req.Method = "POST";
        req.ContentType = "application/x-www-form-urlencoded";
        req.ContentLength = send.Length;

        Stream sout = req.GetRequestStream();
        sout.Write(send, 0, send.Length);
        sout.Flush();
        sout.Close();

        WebResponse res = req.GetResponse();
        StreamReader sr = new StreamReader(res.GetResponseStream());
        string returnvalue = sr.ReadToEnd();

        var captchaResponse = JsonConvert.DeserializeObject<RecaptchaHandler>(returnvalue);

        return captchaResponse.Success;
    }

    [JsonProperty("success")]
    public string Success
    {
        get { return m_Success; }
        set { m_Success = value; }
    }

    private string m_Success;
    [JsonProperty("error-codes")]
    public List<string> ErrorCodes
    {
        get { return m_ErrorCodes; }
        set { m_ErrorCodes = value; }
    }

    private List<string> m_ErrorCodes;
}
1
Justin Gould

Utilisation de dynamic pour valider recaptcha côté serveur

Fonction d'appel

[HttpPost]
public ActionResult ClientOrderDetail(FormCollection collection, string EncodedResponse)
{
    Boolean Validation = myFunction.ValidateRecaptcha(EncodedResponse);

    return View();
}

Déclaration de fonction

public static Boolean ValidateRecaptcha(string EncodedResponse)
{
    string PrivateKey = "YourSiteKey";

    var client = new System.Net.WebClient();

    var GoogleReply = client.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", PrivateKey, EncodedResponse));

    var serializer = new JavaScriptSerializer();
    dynamic data = serializer.Deserialize(GoogleReply, typeof(object));

    Boolean Status = data["success"];
    string challenge_ts = data["challenge_ts"];
    string hostname = data["hostname"];

    return Status;
}
0
Arun Prasad E S

l'exemple que j'ai posté dans this donc post utilise Newtonsoft.JSON pour désérialiser l'intégralité du JSON renvoyé, publie les données sur Google (au lieu d'utiliser une chaîne de requête) stocke les variables pertinentes dans le fichier web.config plutôt que de les coder en dur.

0
Brent

Cet article Expliquez clairement, étape par étape, comment implémenter un attribut de validation ReCaptcha sur votre modèle.

Tout d'abord, créez l'attribut de validation Recaptcha.

namespace Sample.Validation
{
    public class GoogleReCaptchaValidationAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            Lazy<ValidationResult> errorResult = new Lazy<ValidationResult>(() => new ValidationResult("Google reCAPTCHA validation failed", new String[] { validationContext.MemberName }));

            if (value == null || String.IsNullOrWhiteSpace( value.ToString())) 
            {
                return errorResult.Value;
            }

            IConfiguration configuration = (IConfiguration)validationContext.GetService(typeof(IConfiguration));
            String reCaptchResponse = value.ToString();
            String reCaptchaSecret = configuration.GetValue<String>("GoogleReCaptcha:SecretKey");

            HttpClient httpClient = new HttpClient();
            var httpResponse = httpClient.GetAsync($"https://www.google.com/recaptcha/api/siteverify?secret={reCaptchaSecret}&response={reCaptchResponse}").Result;
            if (httpResponse.StatusCode != HttpStatusCode.OK)
            {
                return errorResult.Value;
            }

            String jsonResponse = httpResponse.Content.ReadAsStringAsync().Result;
            dynamic jsonData = JObject.Parse(jsonResponse);
            if (jsonData.success != true.ToString().ToLower())
            {
                return errorResult.Value;
            }

            return ValidationResult.Success;
        }
    }
}

Ajoutez ensuite l'attribut de validation sur votre modèle.

namespace Sample.Models
{
    public class XModel
    {
        // ...
        [Required]  
        [GoogleReCaptchaValidation]  
        public String GoogleReCaptchaResponse { get; set; }
    }
}

Enfin, il vous suffit d'appeler la méthode ModelState.IsValid

namespace Sample.Api.Controllers
{
    [ApiController]
    public class XController : ControllerBase
    {
        [HttpPost]
        public IActionResult Post(XModel model)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            // ...
        }
    }
}

Et voilà ! :)

0
rdhainaut