在 ASP.NET 的服务器端验证 Recaptcha 2 (No CAPTCHA reCAPTCHA)

Posted

技术标签:

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

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

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

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

【问题讨论】:

他们没有休息端点来验证 试试这个。您必须根据响应将用户响应发布到 google ans,然后继续。更多在这里。 developers.google.com/recaptcha/docs/verify @saravanan,你是对的,解决方案应该在这里,我会尝试编码。 试试这个Install-Package reCAPTCH.MVC 如果您正在寻找在 ASP.NET 中组合 Google reCAPTCHA v2 和 v3 的最新解决方案,请查看演示 techtolia.com/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 中,而不是硬编码。

【讨论】:

以上是关于在 ASP.NET 的服务器端验证 Recaptcha 2 (No CAPTCHA reCAPTCHA)的主要内容,如果未能解决你的问题,请参考以下文章

当我在 ASP.Net 中使用 CustomValidator 服务器端验证时,Simplemodal 关闭

如何让 jQuery 验证生成与服务器端 ASP .NET MVC 验证相同的标记?

使用正则表达式的 ASP.NET C# 客户端和服务器端验证

使用 qTip jQuery 插件的 ASP.NET MVC 验证

ASP.NET MVC 验证:导致 javascript 属性?

ASP.net 验证码(C#) MVC