【问题标题】:Validating Recaptcha 2 (No CAPTCHA reCAPTCHA) in ASP.NET's server side在 ASP.NET 的服务器端验证 Recaptcha 2 (No CAPTCHA reCAPTCHA)
【发布时间】:2015-03-02 02:45:37
【问题描述】:

The new Recaptcha 2 看起来很有希望,但我没有找到在 ASP.NET 的服务器端验证它的方法,

This answer中的if(Page.IsValid)对旧的Recaptcha有效,对新的Recaptcha无效,

如何在服务器端验证新的 reCAPTCHA?

【问题讨论】:

标签: c# asp.net vb.net recaptcha


【解决方案1】:

在阅读了很多资源之后,我最终写了这个类来处理the new ReCaptcha的验证:

Here 所述:当最终用户解决了 reCAPTCHA 时,将在 HTML 中填充一个新字段 (g-recaptcha-response)。

我们需要读取这个值并将其传递给下面的类来验证它:

在 C# 中:

在您页面后面的代码中:

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

if (IsCaptchaValid) {
    //Valid Request
}

班级:

  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;
    }

在 VB.NET 中:

在您页面后面的代码中:

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

班级:

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

【讨论】:

  • 如果用户不想导入 Newtonsoft.Json 并创建一个完整定义的 Json 对象,他们可以只使用来自 System.Web.Script.SerializationJavaScriptSerializer 并反序列化为一个普通的 'ol 对象,如图所示in this stackexchange answer
  • 你的回答真的很有帮助阿拉。这就是我为消除对 Newtonsoft 的依赖所做的:JavaScriptSerializer js = new JavaScriptSerializer(); MyObject 数据 = js.Deserialize(GoogleReply); var captchaResponse = data.success;返回验证码响应.ToString(); } 公共类 MyObject { 公共字符串成功 { 获取;放; } }
  • 谢谢阿拉。附带说明,在代码中,bool IsCaptchaValid = (ReCaptchaClass.Validate(EncodedResponse) == "True" ? true : false);,你不需要 ? true : false,这是多余的。
  • 我用这个总是返回 false 为什么会这样?
  • 只是说,Validate 函数的结果对于小 t 是正确的,因此我把头发拉出来为什么它不起作用。
【解决方案2】:

这是一个使用 JavaScriptSerializer 的版本。感谢 Ala 提供此代码的基础。

WebConfig 应用设置 - 在我的例子中,我已将密钥添加到 Web.Config 以允许在环境之间进行转换。如果需要,也可以在这里轻松加密。

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

ReCaptcha 类 - 一个简单的类,用于将响应参数连同您的密码一起发布到 Google 并进行验证。使用 .Net JavaScriptSerializer 类对响应进行反序列化,并从该 true 或 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;
    }
}

验证响应 - 检查控制器中 g-Recaptcha-Response 表单参数的有效性(或网络表单的代码)并采取适当的措施。

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
}   

【讨论】:

  • 对于那些对最简单的实现感兴趣的人来说,这似乎是一个很好的解决方案,尤其是在不使用 Newtonsoft 库的情况下。
  • 我选择了这个解决方案。简单,很好的解释,易于理解。
  • 如果已经是客户端验证,这不起作用,因为响应只成功一次,而不是两次,所以如果已经通过客户端验证,服务器第二次验证将返回 false...
【解决方案3】:

这些答案中的大多数似乎都比需要的复杂。他们也没有指定有助于防止拦截攻击的 IP (https://security.stackexchange.com/questions/81865/is-there-any-reason-to-include-the-remote-ip-when-using-recaptcha)。这是我决定的

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"];
    }
}

【讨论】:

  • 优秀的解决方案。简单有效。
【解决方案4】:

您可以使用“IsValidCaptcha()”方法在服务器端验证您的 google recaptcha。在以下方法中将您的密钥替换为“YourRecaptchaSecretkey”。

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;
 }

同时创建以下类。

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

【讨论】:

    【解决方案5】:

    根据doc,您只需将您的密钥和用户对 API 的回答发布并读取返回的“成功”属性

    简短回答:

            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!!!
    

    完整示例:

    假设,您在 IamNotARobotLogin.cshtml 中实现 this 页面。

    <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>
    

    假设您希望控制器保存,假设如果验证成功,则在会话中保存“I_AM_NOT_ROBOT”标志:

        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");
        }
    

    【讨论】:

      【解决方案6】:

      这是我的 Ala 解决方案的分支,目的是:

      • 在 POST 中发送参数
      • 清理表单输入
      • 包括请求者 IP 地址
      • 将机密存储在 Web.Config 中:

      在控制器中:

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

      实用类:

      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;
              }
          }       
      }
      

      【讨论】:

        【解决方案7】:

        This article 分步说明如何在您的模型上实现 ReCaptcha 验证属性。

        首先,创建 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;
                }
            }
        }
        

        然后在你的模型上添加验证属性。

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

        最后,您只需调用 ModelState.IsValid 方法

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

        等等! :)

        【讨论】:

        • 完美。此外,在属性类中添加“使用 Microsoft.Extensions.Configuration”。 IntelliSense 对我来说并不明显。
        【解决方案8】:

        此处发布另一个示例:

        RecaptchaV2.NET (Github)

        它还实现了 Recaptcha 2.0 的安全令牌选项(查看该位的完整源代码,我已经删除了相关的代码片段,仅用于验证结果)。

        这个不依赖 newtonsoft 的 json 解析器,而是使用内置的 .NET 解析器。

        这里是来自 RecaptchaV2.NET 库(来自 recaptcha.cs)的相关 sn-p 代码:

        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());
            }
          }
        }
        

        【讨论】:

        • 额外的 HttpWebRequest URL 参数“remoteip”有什么作用?我们没有使用“remoteip”参数,并且在接收请求响应时遇到间歇性问题。问题发生时响应为空。
        【解决方案9】:

        Google 的 ReCaptcha API 不再接受负载作为 GET 请求中的查询字符串参数。除非我通过 HTTP POST 发送数据,否则 Google 总是返回“错误”成功响应。这是 Ala 的(非常棒!)类的更新,它将有效负载 POST 到 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;
        }
        

        【讨论】:

          【解决方案10】:

          在服务器端使用动态验证验证码

          调用函数

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

          函数声明

          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;
          }
          

          【讨论】:

            【解决方案11】:

            我在this so post 中发布的示例使用 Newtonsoft.JSON 反序列化完整返回的 JSON,将数据发布到 Google(而不是使用查询字符串)将相关变量存储在 web.config 中,而不是硬编码。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-04-21
              • 2015-02-01
              • 2022-06-13
              • 1970-01-01
              • 2018-04-28
              • 1970-01-01
              相关资源
              最近更新 更多