Skip to content

Commit 022c77c

Browse files
Replace hCaptcha hostname security check with informational telemetry
Per hCaptcha docs, the hostname field in siteverify responses is browser-derived and must not be used for authentication. Remove ExpectedHostname from CaptchaOptions and stop failing captcha verification on hostname mismatch. Instead, log ReportedHostname and ExpectedHostname (derived from SiteSettings.BaseUrl) as structured properties on every successful verification. This enables App Insights / Log Analytics queries to bucket hostname distributions for diagnostics without blocking users. HCaptcha__ExpectedHostname env var can now be removed from all deployed environments.
1 parent 920c021 commit 022c77c

2 files changed

Lines changed: 9 additions & 15 deletions

File tree

EssentialCSharp.Web/Services/CaptchaOptions.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,4 @@ public class CaptchaOptions
2626
/// </summary>
2727
public string JavaScriptUrl { get; set; } = "https://js.hcaptcha.com/1/api.js";
2828

29-
/// <summary>
30-
/// If set, the hostname returned in the hCaptcha siteverify response is compared against this value.
31-
/// Set to null (default) to skip hostname validation — required when using test keys or in development.
32-
/// In production, set to the canonical hostname (e.g., "essentialcsharp.com").
33-
/// </summary>
34-
public string? ExpectedHostname { get; set; }
3529
}

EssentialCSharp.Web/Services/CaptchaService.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44

55
namespace EssentialCSharp.Web.Services;
66

7-
public partial class CaptchaService(IHttpClientFactory clientFactory, IOptions<CaptchaOptions> optionsAccessor, ILogger<CaptchaService> logger) : ICaptchaService
7+
public partial class CaptchaService(IHttpClientFactory clientFactory, IOptions<CaptchaOptions> optionsAccessor, IOptions<SiteSettings> siteSettingsAccessor, ILogger<CaptchaService> logger) : ICaptchaService
88
{
99
private IHttpClientFactory ClientFactory { get; } = clientFactory;
1010
private CaptchaOptions Options { get; } = optionsAccessor.Value;
11+
private SiteSettings SiteSettings { get; } = siteSettingsAccessor.Value;
1112

1213
// Explicit overload used by integration tests: https://docs.hcaptcha.com/#verify-the-user-response-server-side
1314
public async Task<HCaptchaResult?> VerifyAsync(string secret, string response, string sitekey, CancellationToken cancellationToken = default)
@@ -49,13 +50,12 @@ public partial class CaptchaService(IHttpClientFactory clientFactory, IOptions<C
4950

5051
HCaptchaResult? result = await PostVerification(postData, cancellationToken);
5152

52-
if (result is { Success: true } && Options.ExpectedHostname is { Length: > 0 } expectedHostname)
53+
if (result is { Success: true })
5354
{
54-
if (!string.Equals(result.Hostname, expectedHostname, StringComparison.OrdinalIgnoreCase))
55-
{
56-
LogHostnameMismatch(logger, expectedHostname, result.Hostname);
57-
result.Success = false;
58-
}
55+
string expectedHostname = Uri.TryCreate(SiteSettings.BaseUrl, UriKind.Absolute, out Uri? baseUri)
56+
? baseUri.Host
57+
: SiteSettings.BaseUrl;
58+
LogHostnameVerified(logger, result.Hostname, expectedHostname);
5959
}
6060

6161
return result;
@@ -81,8 +81,8 @@ public partial class CaptchaService(IHttpClientFactory clientFactory, IOptions<C
8181
}
8282
}
8383

84-
[LoggerMessage(Level = LogLevel.Warning, Message = "hCaptcha hostname mismatch: expected {Expected}, got {Actual}")]
85-
private static partial void LogHostnameMismatch(ILogger<CaptchaService> logger, string expected, string? actual);
84+
[LoggerMessage(Level = LogLevel.Information, Message = "hCaptcha hostname: reported={ReportedHostname}, expected={ExpectedHostname}")]
85+
private static partial void LogHostnameVerified(ILogger<CaptchaService> logger, string reportedHostname, string expectedHostname);
8686

8787
[LoggerMessage(Level = LogLevel.Error, Message = "hCaptcha siteverify request failed")]
8888
private static partial void LogSiteverifyFailed(ILogger<CaptchaService> logger, Exception exception);

0 commit comments

Comments
 (0)