ソースを参照

Added capcha

master
Daniel Covington 5日前
コミット
c7687ee27f
9個のファイルの変更97行の追加3行の削除
  1. +6
    -0
      app/controllers/OrderApiController.asp
  2. +2
    -1
      app/controllers/OrderController.asp
  3. +7
    -1
      app/controllers/RequestOrderController.asp
  4. +1
    -0
      app/controllers/autoload_controllers.asp
  5. +48
    -0
      app/models/TurnstileValidator.asp
  6. +20
    -1
      app/views/Order/continue.asp
  7. +4
    -0
      app/views/RequestOrder/index.asp
  8. +7
    -0
      public/web.config
  9. +2
    -0
      widget

+ 6
- 0
app/controllers/OrderApiController.asp ファイルの表示

@@ -66,6 +66,12 @@ Class OrderApiController_Class
Exit Sub Exit Sub
End If End If


Dim turnstileToken : turnstileToken = Request.ServerVariables("HTTP_X_TURNSTILE_TOKEN")
If Not VerifyTurnstileToken(turnstileToken, Request.ServerVariables("REMOTE_ADDR")) Then
WriteJsonError "403 Forbidden", "Verification challenge failed. Please refresh the page and try again."
Exit Sub
End If

Dim answers Dim answers
On Error Resume Next On Error Resume Next
json().loadJSON GetRawJsonFromRequest() json().loadJSON GetRawJsonFromRequest()


+ 2
- 1
app/controllers/OrderController.asp ファイルの表示

@@ -29,11 +29,12 @@ Class OrderController_Class
' The order form itself is a placeholder for now - this only confirms the token is valid. ' The order form itself is a placeholder for now - this only confirms the token is valid.
'------------------------------------------------------------------------------------------------------------------- '-------------------------------------------------------------------------------------------------------------------
Public Sub Continue() Public Sub Continue()
Dim token, order, isValid, csrfToken, municipalityName
Dim token, order, isValid, csrfToken, municipalityName, turnstileSiteKey
token = Trim(Request.QueryString("token")) token = Trim(Request.QueryString("token"))
isValid = False isValid = False
csrfToken = "" csrfToken = ""
municipalityName = "" municipalityName = ""
turnstileSiteKey = GetAppSetting("TurnstileSiteKey")
Set order = Nothing Set order = Nothing


If Len(token) > 0 Then If Len(token) > 0 Then


+ 7
- 1
app/controllers/RequestOrderController.asp ファイルの表示

@@ -28,7 +28,7 @@ Class RequestOrderController_Class
' GET: show the "request an order" form (email + jurisdiction number) ' GET: show the "request an order" form (email + jurisdiction number)
'------------------------------------------------------------------------------------------------------------------- '-------------------------------------------------------------------------------------------------------------------
Public Sub Index() Public Sub Index()
Dim formValues, emailValue, jurisdictionValue, csrfToken
Dim formValues, emailValue, jurisdictionValue, csrfToken, turnstileSiteKey


Set formValues = FormCache().DeserializeForm("RequestOrder") Set formValues = FormCache().DeserializeForm("RequestOrder")
FormCache().ClearForm "RequestOrder" FormCache().ClearForm "RequestOrder"
@@ -41,6 +41,7 @@ Class RequestOrderController_Class
End If End If


csrfToken = HTMLSecurity().GetAntiCSRFToken("RequestOrder") csrfToken = HTMLSecurity().GetAntiCSRFToken("RequestOrder")
turnstileSiteKey = GetAppSetting("TurnstileSiteKey")
%> %>
<!--#include file="../views/RequestOrder/index.asp" --> <!--#include file="../views/RequestOrder/index.asp" -->
<% <%
@@ -73,6 +74,11 @@ Class RequestOrderController_Class
AddValidationError errorList, "That jurisdiction number could not be verified." AddValidationError errorList, "That jurisdiction number could not be verified."
End If End If


Dim turnstileToken : turnstileToken = Request.Form("cf-turnstile-response")
If Not VerifyTurnstileToken(turnstileToken, Request.ServerVariables("REMOTE_ADDR")) Then
AddValidationError errorList, "Please complete the verification challenge."
End If

If UBound(errorList) >= 0 Then If UBound(errorList) >= 0 Then
Dim i Dim i
For i = 0 To UBound(errorList) For i = 0 To UBound(errorList)


+ 1
- 0
app/controllers/autoload_controllers.asp ファイルの表示

@@ -1,5 +1,6 @@
<!--#include file="../models/OrdersRepository.asp" --> <!--#include file="../models/OrdersRepository.asp" -->
<!--#include file="../models/JurisdictionValidator.asp" --> <!--#include file="../models/JurisdictionValidator.asp" -->
<!--#include file="../models/TurnstileValidator.asp" -->
<!--#include file="../models/POBO_OrderDetails.asp" --> <!--#include file="../models/POBO_OrderDetails.asp" -->
<!--#include file="../models/OrderDetailsRepository.asp" --> <!--#include file="../models/OrderDetailsRepository.asp" -->
<!--#include file="../models/OrderDetailsLabels.asp" --> <!--#include file="../models/OrderDetailsLabels.asp" -->


+ 48
- 0
app/models/TurnstileValidator.asp ファイルの表示

@@ -0,0 +1,48 @@
<%
'=======================================================================================================================
' Cloudflare Turnstile (CAPTCHA) server-side verification
'=======================================================================================================================
' Verifies a Turnstile response token against Cloudflare's siteverify endpoint. Unlike
' JurisdictionValidator's fail-open behavior (missing reference data shouldn't block orders),
' this fails CLOSED: a missing token or a failed/unreachable verification call is treated as
' "not verified" rather than being let through, since the whole point is to block automated
' submissions.
'=======================================================================================================================

Const TURNSTILE_VERIFY_URL = "https://challenges.cloudflare.com/turnstile/v0/siteverify"

' Verifies a Turnstile response token (the value of the widget's "cf-turnstile-response"
' field / the token returned to the data-callback). remoteIp is optional - pass "" to omit it.
Function VerifyTurnstileToken(responseToken, remoteIp)
VerifyTurnstileToken = False

responseToken = Trim(responseToken)
If Len(responseToken) = 0 Then Exit Function

Dim secretKey : secretKey = GetAppSetting("TurnstileSecretKey")
If Len(secretKey) = 0 Then Exit Function

Dim body
body = "secret=" & Server.URLEncode(secretKey) & "&response=" & Server.URLEncode(responseToken)
If Len(remoteIp) > 0 Then body = body & "&remoteip=" & Server.URLEncode(remoteIp)

On Error Resume Next

Dim http : Set http = Server.CreateObject("Msxml2.ServerXMLHTTP")
http.setTimeouts 5000, 5000, 5000, 5000
http.Open "POST", TURNSTILE_VERIFY_URL, False
http.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
http.Send body

If Err.Number = 0 And http.Status = 200 Then
Dim re
Set re = New RegExp
re.Pattern = """success""\s*:\s*true"
re.IgnoreCase = True
VerifyTurnstileToken = re.Test(http.responseText)
End If

Err.Clear
On Error GoTo 0
End Function
%>

+ 20
- 1
app/views/Order/continue.asp ファイルの表示

@@ -25,6 +25,12 @@
%> %>
<link href="https://cdn.jsdelivr.net/npm/survey-core@2.5.36/survey-core.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/survey-core@2.5.36/survey-core.min.css" rel="stylesheet">


<div style="max-width: 760px; margin: 0 auto; padding: 0 16px;">
<div class="cf-turnstile" style="margin-bottom: 24px;"
data-sitekey="<%= H(turnstileSiteKey) %>"
data-callback="onTurnstileVerified"></div>
</div>

<div id="surveyContainer" style="max-width: 760px; margin: 0 auto 64px; padding: 0 16px;"></div> <div id="surveyContainer" style="max-width: 760px; margin: 0 auto 64px; padding: 0 16px;"></div>


<div id="colorPickerOverlay" style="display:none; position:fixed; inset:0; background:rgba(36,16,58,0.55); z-index:1000; align-items:center; justify-content:center; padding:16px;"> <div id="colorPickerOverlay" style="display:none; position:fixed; inset:0; background:rgba(36,16,58,0.55); z-index:1000; align-items:center; justify-content:center; padding:16px;">
@@ -43,12 +49,17 @@


<script src="https://cdn.jsdelivr.net/npm/survey-core@2.5.36/survey.core.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/survey-core@2.5.36/survey.core.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/survey-js-ui@2.5.36/survey-js-ui.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/survey-js-ui@2.5.36/survey-js-ui.min.js"></script>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<script> <script>
(function () { (function () {
"use strict"; "use strict";


var submitUrl = "<%= H(Routes().AppURL & "order/submit?token=" & Server.URLEncode(token)) %>"; var submitUrl = "<%= H(Routes().AppURL & "order/submit?token=" & Server.URLEncode(token)) %>";
var csrfToken = "<%= H(csrfToken) %>"; var csrfToken = "<%= H(csrfToken) %>";
var turnstileToken = "";
window.onTurnstileVerified = function (token) {
turnstileToken = token;
};


// Election-materials order form. Field (name) values match the OrderDetails table // Election-materials order form. Field (name) values match the OrderDetails table
// columns 1:1 - see db/migrations/20260728145000_create_order_details_table.asp and // columns 1:1 - see db/migrations/20260728145000_create_order_details_table.asp and
@@ -408,12 +419,20 @@
}); });
})(); })();


survey.onCompleting.add(function (sender, options) {
if (!turnstileToken) {
options.allowComplete = false;
alert("Please complete the verification challenge above before submitting.");
}
});

survey.onComplete.add(function (sender) { survey.onComplete.add(function (sender) {
fetch(submitUrl, { fetch(submitUrl, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-CSRF-Token": csrfToken
"X-CSRF-Token": csrfToken,
"X-Turnstile-Token": turnstileToken
}, },
body: JSON.stringify(sender.data) body: JSON.stringify(sender.data)
}) })


+ 4
- 0
app/views/RequestOrder/index.asp ファイルの表示

@@ -23,6 +23,10 @@
value="<%= H(jurisdictionValue) %>" /> value="<%= H(jurisdictionValue) %>" />
</div> </div>


<div class="cf-turnstile" style="margin-bottom: 24px;" data-sitekey="<%= H(turnstileSiteKey) %>"></div>

<button type="submit" class="button button-primary">Request order</button> <button type="submit" class="button button-primary">Request order</button>
</form> </form>
</section> </section>

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

+ 7
- 0
public/web.config ファイルの表示

@@ -61,6 +61,13 @@
--> -->
<add key="JurisdictionApiUrl" value="http://192.168.1.40:8081/api/jurisdictions" /> <add key="JurisdictionApiUrl" value="http://192.168.1.40:8081/api/jurisdictions" />
<add key="JurisdictionCacheMinutes" value="60" /> <add key="JurisdictionCacheMinutes" value="60" />

<!--
Cloudflare Turnstile (CAPTCHA) keys. These are DEV keys - replace with production
site/secret keys before going live.
-->
<add key="TurnstileSiteKey" value="0x4AAAAAAEUPEPHEnA9hPgbw" />
<add key="TurnstileSecretKey" value="0x4AAAAAAEUPEFnbhCe6ttr_Mq6yCozxRkg" />
</appSettings> </appSettings>


<system.webServer> <system.webServer>


+ 2
- 0
widget ファイルの表示

@@ -0,0 +1,2 @@
site key dev 0x4AAAAAAEUPEPHEnA9hPgbw
secret key dev 0x4AAAAAAEUPEFnbhCe6ttr_Mq6yCozxRkg

読み込み中…
キャンセル
保存

Powered by TurnKey Linux.