Last active
September 26, 2024 18:50
-
-
Save mikeckennedy/66d6298106671a3c6215c9262c102d74 to your computer and use it in GitHub Desktop.
Python code for server-side Cloudflare validation (paired with client-side Cloudflare code)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# See details about Turnstile at https://blog.cloudflare.com/turnstile-private-captcha-alternative/ | |
from typing import Optional | |
import pydantic | |
import requests | |
cloudflare_secret_key: Optional[str] = None | |
class SiteVerifyRequest(pydantic.BaseModel): | |
secret: str | |
response: str | |
remoteip: Optional[str] | |
class SiteVerifyResponse(pydantic.BaseModel): | |
success: bool | |
challenge_ts: Optional[str] | |
hostname: Optional[str] | |
error_codes: list[str] = pydantic.Field(alias="error-codes", default_factory=list) | |
action: Optional[str] | |
cdata: Optional[str] | |
request_example = { | |
"secret": "0x5ABAAFAAAn72SdCAP75q6sPP9P6zooFZt", | |
"response": "???", | |
"remoteip": "1.2.3.4" | |
} | |
success_example = { | |
"success": True, | |
"challenge_ts": "2022-02-28T15:14:30.096Z", | |
"hostname": "example.com", | |
"error-codes": [], | |
"action": "login", | |
"cdata": "session_id-123456789" | |
} | |
failure_example = { | |
"success": False, | |
"hostname": "", | |
"error-codes": [ | |
"invalid-input-response" | |
] | |
} | |
def validate(turnstile_response: str, user_ip: Optional[str]) -> SiteVerifyResponse: | |
if not cloudflare_secret_key: | |
raise Exception("You must call turnstile.init() with a valid secret key before using this function.") | |
if not turnstile_response: | |
model = SiteVerifyResponse(success=False, hostname=None) | |
model.error_codes.append('Submitted with no cloudflare client response') | |
return model | |
url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify' | |
model = SiteVerifyRequest(secret=cloudflare_secret_key, response=turnstile_response, remoteip=user_ip) | |
try: | |
resp = requests.post(url, data=model.dict()) | |
if resp.status_code != 200: | |
model = SiteVerifyResponse(success=False, hostname=None) | |
model.error_codes.extend([ | |
f'Failure status code: {resp.status_code}', | |
f'Failure details: {resp.text}']) | |
return model | |
site_response = SiteVerifyResponse(**resp.json()) | |
return site_response | |
except Exception as x: | |
model = SiteVerifyResponse(success=False, hostname=None) | |
model.error_codes.extend([ | |
f'Failure status code: Unknown', | |
f'Failure details: {x}']) | |
return model | |
def init(secret_key: str): | |
global cloudflare_secret_key | |
if not secret_key: | |
return | |
cloudflare_secret_key = secret_key.strip() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey, I would imagine sure. Basically you include the turnsile JS from cloudflare in the HTML and then that gets posted along with the form that it's protecting and you can use this code to check whether the validation passed.