Validating Recaptcha 2 (No CAPTCHA reCAPTCHA) in ASP.NET's server side

The new Recaptcha 2 looks promising, but i didn't find a way to validate it in ASP.NET's server side,

if(Page.IsValid) in This answer, is valid for the old Recaptcha, but not the new one,

How to validate the new reCAPTCHA in server side?

111204 次浏览

在阅读了许多资源之后,我最终编写了这个类来处理 新的重现卡的验证:

正如前面提到的 给你: 当最终用户解决 reCAPTCHA 时,将在 HTML 中填充一个新字段(g-captcha-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

另一个例子发布在这里:

RecaptchaV2.NET (Github)

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

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

下面是 RecaptchaV2.NET 库中的相关代码片段(来自 apptcha.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());
}
}
}

下面是一个使用 JavaScriptSerializer 的版本。

WebConfig 应用程序设置 - 我已经把密钥加到网上了。在我的例子中,配置允许在环境之间进行转换。如果需要,这里也可以很容易地对它进行加密。

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

ReCaptcha Class -这是一个简单的类,可以将响应参数和秘密一起发布到 Google 并进行验证。方法对响应进行反序列化。类,并从该类返回 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;
}
}

验证 Response -检查 Controller 中 g-Recaptcha-Response 表单参数的有效性(或 web 表单的代码隐藏) ,并采取适当的操作。

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
}

这些答案中的大多数似乎比需要的更复杂。它们也没有指定有助于防止拦截攻击的 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"];
}
}

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

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

使用 Dynamic 在服务器端验证 captcha

调用函数

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

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

根据 医生,您只需将您的密钥和用户的答案发布到 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中实现了 这个页。

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

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

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

这篇文章 逐步清楚地解释了如何在模型上实现 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);
}
// ...
}
}
}

就是这样