handler/stop: add no js solution option
This commit is contained in:
6 files changed
+142
-8
No files matched your search
+80
-1
@@ -154,6 +154,19 @@
|
||||
transition: ease-in-out 0.1s;
|
||||
}
|
||||
|
||||
.link,
|
||||
.link:visited,
|
||||
.link:link,
|
||||
.link:focus,
|
||||
.link:active {
|
||||
color: #c03940;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (pointer:none),
|
||||
(pointer:coarse) {
|
||||
.big-icon {
|
||||
@@ -198,10 +211,76 @@
|
||||
|
||||
<noscript>
|
||||
<p class="text-description">
|
||||
This website uses <a href="https://github.com/vaxerski/checkpoint">checkpoint</a> and requires javascript. Please enable javascript to complete the Proof of Work challenge.
|
||||
This website uses <a class="link" href="https://github.com/vaxerski/checkpoint">checkpoint</a> and
|
||||
requires
|
||||
javascript to
|
||||
automatically verify you are not a bot.<br /><br />
|
||||
You seem to be running without Javascript enabled, and thus will have to do this manually.<br /><br />
|
||||
Paste this command into your Linux terminal and provide the output below:
|
||||
</p>
|
||||
|
||||
<p class="code-block">
|
||||
perl -MDigest::SHA=sha256_hex -e '$nonce="{{ tl:text challengeNonce }}"; $d={{ tl:text
|
||||
challengeDifficulty
|
||||
}}; for($i=0;; $i++){ $h=sha256_hex($nonce.$i); if(substr($h,0,$d) eq "0"x$d){ print "$i\n"; last }}'
|
||||
</p>
|
||||
|
||||
<form action="/checkpoint/challengeNoJs" method="get">
|
||||
<div>
|
||||
<label class="form-label" for="name">Enter the output: </label>
|
||||
<input class="form-input" type="text" name="solution" id="solution" required />
|
||||
</div>
|
||||
<input class="input-hidden" type="text" name="fingerprint" id="fingerprint"
|
||||
value="{{ tl:text challengeFingerprint }}" />
|
||||
<input class="input-hidden" type="text" name="challenge" id="challenge"
|
||||
value="{{ tl:text challengeNonce }}" />
|
||||
<input class="input-hidden" type="text" name="sig" id="sig" value="{{ tl:text challengeSignature }}" />
|
||||
<input class="input-hidden" type="text" name="timestamp" id="timestamp"
|
||||
value="{{ tl:text challengeTimestamp }}" />
|
||||
<input class="input-hidden" type="text" name="difficulty" id="difficulty"
|
||||
value="{{ tl:text challengeDifficulty }}" />
|
||||
<input class="form-button" type="submit" value="Send" />
|
||||
</form>
|
||||
|
||||
<style>
|
||||
.form-label {
|
||||
color: #fff;
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-button {
|
||||
margin-left: 9.5rem;
|
||||
width: calc(100%-19rem);
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
margin-left: 9.5rem;
|
||||
width: calc(100%-19rem);
|
||||
}
|
||||
|
||||
.code-block {
|
||||
background-color: #161616;
|
||||
color: #d9d9d9;
|
||||
border-radius: 1rem;
|
||||
font: "Monospace";
|
||||
margin: 1rem;
|
||||
width: calc(100% - 3rem);
|
||||
max-width: 100%;
|
||||
text-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
padding: 0.5rem;
|
||||
text-align: left;
|
||||
border: 1px solid #280d0e;
|
||||
}
|
||||
|
||||
.input-hidden {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.require-js {
|
||||
display: none;
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,2 +1,2 @@
|
||||
<!DOCTYPE html><head><title>STOP! - Checkpoint</title></head><body style="background-color:#0e0e0e;overflow:hidden"><style>@font-face{font-family:"Noto Sans";src:url(/checkpoint/NotoSans.woff)}*{font-family:"Noto Sans"}.middle-box{display:block;position:absolute;width:30rem;height:calc(100% - 2rem);left:calc(50% - 15rem);top:1rem;background-color:#111;border-radius:.5rem;border:1px solid #280d0e}.big-icon{margin:0;padding:0;font-size:8rem;color:#fff;width:100%;text-align:center}.subtext{margin:0;padding:0;font-size:3rem;color:#d9d9d9;width:100%;text-align:center}.text-hr{margin:auto;margin-top:1rem;margin-bottom:1rem;padding:0;background-color:#444;width:50%;box-shadow:none;text-align:center;height:1px;border:none}.text-hr-small{margin:auto;margin-top:1rem;margin-bottom:1rem;padding:0;background-color:#444;width:25%;box-shadow:none;text-align:center;height:1px;border:none}.text-description{margin:0;padding:0;font-size:1rem;color:#d9d9d9;width:85%;margin-left:7.5%;text-align:center}.text-question{margin:0;margin-top:4rem;padding:0;font-size:1.4rem;color:#d9d9d9;width:85%;margin-left:7.5%;text-align:center}.bottom-bar{position:absolute;width:100%;height:auto;bottom:.4rem;left:0}.bottom-hash-text{margin:0;padding:0;font-size:1rem;color:#d9d9d9;width:85%;margin-left:7.5%;text-align:center}.bottom-credit-text{display:block;margin:0;padding:0;font-size:.5rem;color:#4b4b4b;width:98%;text-align:right;transition:ease-in-out .1s;margin-top:.2rem}.bottom-credit-text:hover,.bottom-credit-text:link:hover,.bottom-credit-text:visited:hover{color:#764343;cursor:pointer}.bottom-credit-text:link,.bottom-credit-text:visited{color:#4b4b4b}.bottom-progress{position:relative;height:3px;background-color:#222;width:80%;margin-left:10%;margin-bottom:.8rem}.bottom-progress-light{position:absolute;left:0;top:0;width:0%;height:3px;background-color:#692225;transition:ease-in-out .1s}@media (pointer:none),(pointer:coarse){.big-icon{margin-top:10rem}.middle-box{width:90%;left:5%}.subtext{font-size:6rem}.text-description{font-size:3rem}.text-hr{height:3px}.bottom-hash-text{font-size:2.5rem}.bottom-credit-text{font-size:1rem}}</style><div class="middle-box"><p class="big-icon">🛑</p><p class="subtext" id="subtext">STOP!</p><hr class="text-hr"><noscript><p class="text-description">This website uses<a href="https://github.com/vaxerski/checkpoint">checkpoint</a>and requires javascript. Please enable javascript to complete the Proof of Work challenge.</p><style>.require-js{display:none}</style></noscript><p class="text-description require-js">Verifying that you are not a bot. This might take a short moment.<br><br>You do not need to do anything.</p><p class="text-question require-js">Why am I seeing this?</p><hr class="text-hr-small require-js"><p class="text-description require-js">This website protects itself from AI bots and scrapers by asking you to complete a cryptographic challenge before allowing you entry.</p><div class="bottom-bar require-js"><div class="bottom-progress"><div class="bottom-progress-light" name="progress-light"></div></div><p class="bottom-hash-text" id="results">Difficulty {{ tl:text challengeDifficulty }}, elapsed 0ms, 0h, 0h/s</p><a class="bottom-credit-text" href="https://github.com/vaxerski/checkpoint"><i>Powered by checkpoint v{{ tl:text checkpointVersion }}</i></a></div></div>
|
||||
<script type="text/javascript">setTimeout(async function(){var e=0,t=Date.now();let l="{{ tl:text challengeNonce }}",n=parseInt("{{ tl:text challengeDifficulty }}");function $(e){return e>1e6?parseInt(e/1e5)/10+"M":parseInt(e/100)/10+"k"}function r(e){return e>1e3?parseInt(e/100)/10+"s":e+"ms"}function o(e){return Math.floor((1-1/(Math.pow(e/Math.pow(2,4*n)*3,2)+1))*100)}function i(e){document.getElementById("results").innerHTML="Success! Completed challenge after "+$(e)+" iterations, in "+r(Math.floor(Date.now()-t))+".",document.getElementsByName("progress-light")[0].style.width="100%";let o=JSON.stringify({challenge:l,solution:e,difficulty:n,sig:"{{ tl:text challengeSignature }}",fingerprint:"{{ tl:text challengeFingerprint }}",timestamp:"{{ tl:text challengeTimestamp }}"});fetch("/checkpoint/challenge",{headers:{"Content-Type":"application/json"},method:"POST",body:o}).then(e=>{200==e.status?(console.log("Got token."),window.location.reload()):console.log("Server error")})}let s=new Uint8Array(l.length+10);for(let a=0;a<l.length;a++)s[a]=l.charCodeAt(a);for(;;){let c=0===e?1:~~Math.floor(Math.log10(e))+1;for(let g=c-1;g>=0;g--)s[l.length+(c-1-g)]=48+~~Math.floor(e/Math.pow(10,g)%10);let _=new Uint8Array(await window.crypto.subtle.digest("sha-256",s.slice(0,l.length+c))),f=0;for(;f<n&&(f%2==0?_[~~(f/2)]>>4:15&_[~~(f/2)])==0;f++);if(f>=n){i(e),console.log("Success: it "+e+": "+Array.from(_).map(e=>e.toString(16).padStart(2,"0")).join(""));break}if(++e%11377==0){let h=Math.floor(Date.now()-t),u=e/(h/1e3);document.getElementById("results").innerHTML="Difficulty: "+n+", elapsed "+r(h)+", "+$(e)+"h, "+$(u)+"h/s",document.getElementsByName("progress-light")[0].style.width=o(e)+"%"}}},100);var currentTitle=1;setInterval(()=>{let e=["STOP","HALT","STÓJ","ARRÊT","СТІЙ"];document.getElementById("subtext").innerHTML=e[currentTitle]+"!",++currentTitle>=e.length&&(currentTitle=0)},2e3);</script></body>
|
||||
<!DOCTYPE html><html><head><title>STOP! - Checkpoint</title></head><body style="background-color:#0e0e0e;overflow:hidden"><style>@font-face{font-family:"Noto Sans";src:url(/checkpoint/NotoSans.woff)}*{font-family:"Noto Sans"}.middle-box{display:block;position:absolute;width:30rem;height:calc(100% - 2rem);left:calc(50% - 15rem);top:1rem;background-color:#111;border-radius:.5rem;border:1px solid #280d0e}.big-icon{margin:0;padding:0;font-size:8rem;color:#fff;width:100%;text-align:center}.subtext{margin:0;padding:0;font-size:3rem;color:#d9d9d9;width:100%;text-align:center}.text-hr{margin:auto;margin-top:1rem;margin-bottom:1rem;padding:0;background-color:#444;width:50%;box-shadow:none;text-align:center;height:1px;border:none}.text-hr-small{margin:auto;margin-top:1rem;margin-bottom:1rem;padding:0;background-color:#444;width:25%;box-shadow:none;text-align:center;height:1px;border:none}.text-description{margin:0;padding:0;font-size:1rem;color:#d9d9d9;width:85%;margin-left:7.5%;text-align:center}.text-question{margin:0;margin-top:4rem;padding:0;font-size:1.4rem;color:#d9d9d9;width:85%;margin-left:7.5%;text-align:center}.bottom-bar{position:absolute;width:100%;height:auto;bottom:.4rem;left:0}.bottom-hash-text{margin:0;padding:0;font-size:1rem;color:#d9d9d9;width:85%;margin-left:7.5%;text-align:center}.bottom-credit-text{display:block;margin:0;padding:0;font-size:.5rem;color:#4b4b4b;width:98%;text-align:right;transition:ease-in-out .1s;margin-top:.2rem}.bottom-credit-text:hover,.bottom-credit-text:link:hover,.bottom-credit-text:visited:hover{color:#764343;cursor:pointer}.bottom-credit-text:link,.bottom-credit-text:visited{color:#4b4b4b}.bottom-progress{position:relative;height:3px;background-color:#222;width:80%;margin-left:10%;margin-bottom:.8rem}.bottom-progress-light{position:absolute;left:0;top:0;width:0%;height:3px;background-color:#692225;transition:ease-in-out .1s}.link,.link:active,.link:focus,.link:link,.link:visited{color:#c03940;text-decoration:none}.link:hover{text-decoration:underline}@media (pointer:none),(pointer:coarse){.big-icon{margin-top:10rem}.middle-box{width:90%;left:5%}.subtext{font-size:6rem}.text-description{font-size:3rem}.text-hr{height:3px}.bottom-hash-text{font-size:2.5rem}.bottom-credit-text{font-size:1rem}}</style><div class="middle-box"><p class="big-icon">🛑</p><p class="subtext" id="subtext">STOP!</p><hr class="text-hr"><noscript><p class="text-description">This website uses<a class="link" href="https://github.com/vaxerski/checkpoint">checkpoint</a>and requires javascript to automatically verify you are not a bot.<br><br>You seem to be running without Javascript enabled, and thus will have to do this manually.<br><br>Paste this command into your Linux terminal and provide the output below:</p><p class="code-block">perl -MDigest::SHA=sha256_hex -e '$nonce="{{ tl:text challengeNonce }}"; $d={{ tl:text challengeDifficulty }}; for($i=0;; $i++){ $h=sha256_hex($nonce.$i); if(substr($h,0,$d) eq "0"x$d){ print "$i\n"; last }}'</p><form action="/checkpoint/challengeNoJs" method="get"><div><label class="form-label" for="name">Enter the output:</label><input class="form-input" type="text" name="solution" id="solution" required></div><input class="input-hidden" type="text" name="fingerprint" id="fingerprint" value="{{ tl:text challengeFingerprint }}"> <input class="input-hidden" type="text" name="challenge" id="challenge" value="{{ tl:text challengeNonce }}"> <input class="input-hidden" type="text" name="sig" id="sig" value="{{ tl:text challengeSignature }}"> <input class="input-hidden" type="text" name="timestamp" id="timestamp" value="{{ tl:text challengeTimestamp }}"> <input class="input-hidden" type="text" name="difficulty" id="difficulty" value="{{ tl:text challengeDifficulty }}"> <input class="form-button" type="submit" value="Send"></form><style>.form-label{color:#fff;display:block;text-align:center}.form-button{margin-left:9.5rem;width:calc(100%-19rem);display:block;position:relative}.form-input{margin-left:9.5rem;width:calc(100%-19rem)}.code-block{background-color:#161616;color:#d9d9d9;border-radius:1rem;font:Monospace;margin:1rem;width:calc(100% - 3rem);max-width:100%;text-wrap:break-word;overflow-wrap:break-word;padding:.5rem;text-align:left;border:1px solid #280d0e}.input-hidden{visibility:hidden;position:absolute}.require-js{display:none}</style></noscript><p class="text-description require-js">Verifying that you are not a bot. This might take a short moment.<br><br>You do not need to do anything.</p><p class="text-question require-js">Why am I seeing this?</p><hr class="text-hr-small require-js"><p class="text-description require-js">This website protects itself from AI bots and scrapers by asking you to complete a cryptographic challenge before allowing you entry.</p><div class="bottom-bar require-js"><div class="bottom-progress"><div class="bottom-progress-light" name="progress-light"></div></div><p class="bottom-hash-text" id="results">Difficulty {{ tl:text challengeDiffiLine truncated
|
||||
<script type="text/javascript">setTimeout(async function(){var e=0,t=Date.now();let l="{{ tl:text challengeNonce }}",n=parseInt("{{ tl:text challengeDifficulty }}");function $(e){return e>1e6?parseInt(e/1e5)/10+"M":parseInt(e/100)/10+"k"}function r(e){return e>1e3?parseInt(e/100)/10+"s":e+"ms"}function o(e){return Math.floor((1-1/(Math.pow(e/Math.pow(2,4*n)*3,2)+1))*100)}function i(e){document.getElementById("results").innerHTML="Success! Completed challenge after "+$(e)+" iterations, in "+r(Math.floor(Date.now()-t))+".",document.getElementsByName("progress-light")[0].style.width="100%";let o=JSON.stringify({challenge:l,solution:e,difficulty:n,sig:"{{ tl:text challengeSignature }}",fingerprint:"{{ tl:text challengeFingerprint }}",timestamp:"{{ tl:text challengeTimestamp }}"});fetch("/checkpoint/challenge",{headers:{"Content-Type":"application/json"},method:"POST",body:o}).then(e=>{200==e.status?(console.log("Got token."),window.location.reload()):console.log("Server error")})}let s=new Uint8Array(l.length+10);for(let a=0;a<l.length;a++)s[a]=l.charCodeAt(a);for(;;){let c=0===e?1:~~Math.floor(Math.log10(e))+1;for(let g=c-1;g>=0;g--)s[l.length+(c-1-g)]=48+~~Math.floor(e/Math.pow(10,g)%10);let _=new Uint8Array(await window.crypto.subtle.digest("sha-256",s.slice(0,l.length+c))),f=0;for(;f<n&&(f%2==0?_[~~(f/2)]>>4:15&_[~~(f/2)])==0;f++);if(f>=n){i(e),console.log("Success: it "+e+": "+Array.from(_).map(e=>e.toString(16).padStart(2,"0")).join(""));break}if(++e%11377==0){let h=Math.floor(Date.now()-t),u=e/(h/1e3);document.getElementById("results").innerHTML="Difficulty: "+n+", elapsed "+r(h)+", "+$(e)+"h, "+$(u)+"h/s",document.getElementsByName("progress-light")[0].style.width=o(e)+"%"}}},100);var currentTitle=1;setInterval(()=>{let e=["STOP","HALT","STÓJ","ARRÊT","СТІЙ"];document.getElementById("subtext").innerHTML=e[currentTitle]+"!",++currentTitle>=e.length&&(currentTitle=0)},2e3);</script></body></html>
|
||||
@@ -46,6 +46,38 @@ CChallenge::CChallenge(const std::string& jsonResponse) {
|
||||
m_valid = true;
|
||||
}
|
||||
|
||||
CChallenge::CChallenge(const Pistache::Http::Request& reqResponse) {
|
||||
auto& q = reqResponse.query();
|
||||
|
||||
if (!q.has("solution")
|
||||
|| !q.has("fingerprint")
|
||||
|| !q.has("challenge")
|
||||
|| !q.has("timestamp")
|
||||
|| !q.has("sig")
|
||||
|| !q.has("difficulty"))
|
||||
return;
|
||||
|
||||
m_challenge = q.get("challenge").value();
|
||||
m_fingerprint = q.get("fingerprint").value();
|
||||
m_sig = q.get("sig").value();
|
||||
|
||||
try {
|
||||
m_issued = std::chrono::system_clock::time_point(std::chrono::seconds(std::stoull(q.get("timestamp").value())));
|
||||
} catch (std::exception& e) { return; }
|
||||
|
||||
if (!g_pCrypto->verifySignature(getSigString(), m_sig))
|
||||
return;
|
||||
|
||||
const auto SHA = g_pCrypto->sha256(m_challenge + q.get("solution").value());
|
||||
|
||||
for (size_t i = 0; i < m_difficulty; ++i) {
|
||||
if (SHA.at(i) != '0')
|
||||
return;
|
||||
}
|
||||
|
||||
m_valid = true;
|
||||
}
|
||||
|
||||
std::string CChallenge::fingerprint() const {
|
||||
return m_fingerprint;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
#include <pistache/http.h>
|
||||
|
||||
class CChallenge {
|
||||
public:
|
||||
CChallenge() = default;
|
||||
CChallenge(const std::string& fingerprint, const std::string& challenge, int difficulty);
|
||||
CChallenge(const std::string& jsonResponse);
|
||||
CChallenge(const Pistache::Http::Request& reqResponse);
|
||||
|
||||
std::string fingerprint() const;
|
||||
std::string challenge() const;
|
||||
|
||||
+23
-4
@@ -195,7 +195,15 @@ void CServerHandler::onRequest(const Pistache::Http::Request& req, Pistache::Htt
|
||||
|
||||
if (req.resource() == "/checkpoint/challenge") {
|
||||
if (req.method() == Pistache::Http::Method::Post)
|
||||
challengeSubmitted(req, response);
|
||||
challengeSubmitted(req, response, true);
|
||||
else
|
||||
response.send(Pistache::Http::Code::Bad_Request, "Bad Request");
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.resource() == "/checkpoint/challengeNoJs") {
|
||||
if (req.method() == Pistache::Http::Method::Get)
|
||||
challengeSubmitted(req, response, false);
|
||||
else
|
||||
response.send(Pistache::Http::Code::Bad_Request, "Bad Request");
|
||||
return;
|
||||
@@ -323,11 +331,15 @@ void CServerHandler::onTimeout(const Pistache::Http::Request& request, Pistache:
|
||||
response.send(Pistache::Http::Code::Request_Timeout, "Timeout").then([=](ssize_t) {}, PrintException());
|
||||
}
|
||||
|
||||
void CServerHandler::challengeSubmitted(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response) {
|
||||
void CServerHandler::challengeSubmitted(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response, bool js) {
|
||||
const auto JSON = req.body();
|
||||
const auto FINGERPRINT = fingerprintForRequest(req);
|
||||
|
||||
const auto CHALLENGE = CChallenge(req.body());
|
||||
CChallenge CHALLENGE;
|
||||
if (!js)
|
||||
CHALLENGE = CChallenge(req);
|
||||
else
|
||||
CHALLENGE = CChallenge(req.body());
|
||||
|
||||
if (!CHALLENGE.valid()) {
|
||||
response.send(Pistache::Http::Code::Bad_Request, "Bad request");
|
||||
@@ -353,7 +365,12 @@ void CServerHandler::challengeSubmitted(const Pistache::Http::Request& req, Pist
|
||||
response.headers().add(
|
||||
std::make_shared<SetCookieHeader>(std::string{TOKEN_COOKIE_NAME} + "=" + TOKEN.tokenCookie() + "; Domain=" + hostDomain + "; HttpOnly; Path=/; Secure; SameSite=Lax"));
|
||||
|
||||
response.send(Pistache::Http::Code::Ok, "Ok");
|
||||
if (js)
|
||||
response.send(Pistache::Http::Code::Ok, "Ok");
|
||||
else {
|
||||
response.headers().add<Pistache::Http::Header::Location>("/");
|
||||
response.send(Pistache::Http::Code::Moved_Permanently, "");
|
||||
}
|
||||
}
|
||||
|
||||
void CServerHandler::serveStop(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response, int difficulty) {
|
||||
@@ -376,6 +393,8 @@ void CServerHandler::serveStop(const Pistache::Http::Request& req, Pistache::Htt
|
||||
page.add("challengeTimestamp", CTinylatesProp(CHALLENGE.timestampAsString()));
|
||||
page.add("hostDomain", CTinylatesProp(hostDomain));
|
||||
page.add("checkpointVersion", CTinylatesProp(CHECKPOINT_VERSION));
|
||||
|
||||
response.setMime(Pistache::Http::Mime::MediaType("text/html"));
|
||||
response.send(Pistache::Http::Code::Ok, page.render().value_or("error"));
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class CServerHandler : public Pistache::Http::Handler {
|
||||
void proxyPass(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response);
|
||||
void proxyPassInternal(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response, bool async = false);
|
||||
void proxyPassAsync(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response);
|
||||
void challengeSubmitted(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response);
|
||||
void challengeSubmitted(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response, bool js);
|
||||
std::string fingerprintForRequest(const Pistache::Http::Request& req);
|
||||
std::string ipForRequest(const Pistache::Http::Request& req);
|
||||
|
||||
|
||||
Reference in new issue
Block a user