Created
December 25, 2024 00:20
-
-
Save pabloko/76906fdebd6e7888021207b79384b47c to your computer and use it in GitHub Desktop.
Revisions
-
pabloko created this gist
Dec 25, 2024 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,81 @@ # PoWCaptcha - Proof of Work CAPTCHA **PoWCaptcha** is a simple, transparent CAPTCHA system based on Proof of Work (PoW) that aims to stop bots with minimal user interaction. It leverages the same principles as cryptocurrencies and blockchain to validate human users without requiring image recognition or complex puzzles. ## Why PoWCaptcha? Captchas are getting less effective. OCR tools, AI, and captcha solving services are quicker than humans and cheap. At the same time, exposing a contact form without protection invites spam and bot traffic. **PoWCaptcha** offers a minimal wall to deter lazy bots—making the verification process computationally expensive for bots but invisible to humans. Instead of bothering the user with complex tasks, PoWCaptcha shifts the burden to the computer, performing backend calculations involving: - User data - Server integrity - Token expiration - Proof of Work validation ## Key Features - **Proof of Work**: The system perform cryptographic work to validate and obtain the data. - **Transparent User Experience**: The CAPTCHA process doesn’t interfere with user interaction but leverages the computer’s processing power. - **Minimal Bot Resistance**: The challenge is designed to be computationally expensive for bots but lightweight for legitimate users. - **Configurable**: Customize token expiration, difficulty, and other settings to balance between security and user experience. - **Privacy**: Hosted solution that does not involve user tracking or third site sign-in. ## Installation & Setup Simply put `PoWCaptcha.php` in your project. The library will handle token generation and validation. ### Example Usage #### 1. Generate a Token Use the API to generate a new token: ``` PoWCaptcha.php?getToken ``` #### 2. Validate a Token To validate the token, use: ``` PoWCaptcha.php?validateToken=XXXX... ``` Returns HTTP `200` and JSON data if valid, `401` if invalid. #### 3. Embeddable JavaScript ``` <script src="/PoWCaptcha.php?script"></script> ``` Basic example: ```html <form method="get" action="/PoWCaptcha.php"> <input id="token" name="validateToken" type="text" value="Calculating..." /> <button>Check</button> </form> <script src="/PoWCaptcha.php?script"></script> <script> PoWCaptcha({username: "user01", password: "test01password@@"}, (tok) => { token.value = tok; }); </script> ``` The `PoWCaptcha` function generates a token based on user data (like username and password) and obtains a valid token for the provided data that the server can retrive. ## Proof of Work Process 1. **Token Generation**: The server creates a token using a random string, timestamp, and a cryptographic hash. 2. **Computational Challenge**: The client (user's computer) must solve a PoW challenge and validate the token with data. 3. **Validation**: The server verifies that the submitted token matches the expected result by recalculating the cryptographic hash and obtains the data. ## Security Considerations - **Integrity**: Ensures the token has not been tampered with using a secret key. - **Expiration**: Tokens are time-limited to prevent replay attacks. - **Effort for Bots**: The proof-of-work mechanism forces bots to expend computational resources, reducing the chance of automated / brute force attacks. This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,129 @@ <?php /** * PoWCaptcha - proof of work captcha * GitHub @pabloko (gists) */ // BEGIN CONFIGURE $PRIV_TOKEN = "@@Your private and secret token@@"; // used as salt for integrity check $HASH_COUNT = 5; // amount of bytes to calculate on the proof-of-work $VALID_TIME = 3600; // time in seconds while token remains valid $ENABLE_API = true; //weather to allow api via GET // END CONFIGURE // Utility... function generateRandomString($length = 4) { $characters = '0123456789abcdef'; $charactersLength = strlen($characters); $randomString = ''; for ($i = 0; $i < $length; $i++) $randomString .= $characters[random_int(0, $charactersLength - 1)]; return $randomString; } // Generate a fresh token valid for configured period of time function generateHashString() { global $PRIV_TOKEN; global $HASH_COUNT; $rnd = generateRandomString(16); $timestamp = time(); $expectedPart = generateRandomString($HASH_COUNT); return base64_encode($rnd . ";" . $timestamp . ";" . sha1($rnd . ";" . $timestamp . ";" . $PRIV_TOKEN . ";" . $expectedPart) . ";" . $expectedPart . ";X-Response-Hash;"); } // Validate a PoW recalculated token with data function validateHashString($token) { global $PRIV_TOKEN; global $VALID_TIME; $decodedString = base64_decode($token); $parts = explode(";", $decodedString); if (count($parts) !== 6) return null; $timeCheck = time() - (int)$parts[1]; // check is valid in time if ($timeCheck > $VALID_TIME || $timeCheck < 0) return null; // do integrity check if ($parts[2] !== sha1($parts[0] . ";" . (int)$parts[1] . ";" . $PRIV_TOKEN . ";" . $parts[3])) return null; // do proof-of-work verification if (!str_ends_with(sha1($parts[2] . $parts[5] . (int)$parts[4]), $parts[3])) return null; return base64_decode($parts[5]); } // simple API // obtain a fresh token // PoWCaptcha.php?getToken if ($ENABLE_API && isset($_GET['getToken'])) { header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); die(generateHashString()); } // check a PoW recalculated token and output data or http error // PoWCaptcha.php?validateToken=XXXX... if ($ENABLE_API && isset($_GET['validateToken'])) { header('Content-Type: application/json'); header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); $result = validateHashString($_GET['validateToken']); http_response_code($result ? 200 : 401); die($result); } // embeddable script output // PoWCaptcha.php?script if ($ENABLE_API && isset($_GET['script'])) { header('Content-Type: application/javascript'); header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); ?>/** * PoWCaptcha - proof of work captcha * GitHub @pabloko (gists) */ <?php // function PoWCaptcha(data, callback) // { // var _token = '< ?php echo generateHashString(); ? >' // function createWorker(fn) // { // var blob = new Blob(['self.onmessage = ', fn.toString()], { type: 'text/javascript' }); // var url = window.URL.createObjectURL(blob); // return new Worker(url); // } // function tPoW(e) // { // function SHA1(f){function $(f,$){return f<<$|f>>>32-$}function r(f){var $,r,o,e="";for($=0;$<=6;$+=2)r=f>>>4*$+4&15,o=f>>>4*$&15,e+=r.toString(16)+o.toString(16);return e}function o(f){var $,r,o="";for($=7;$>=0;$--)o+=(r=f>>>4*$&15).toString(16);return o}var e,_,t,a,C,h,x,n,c,d=Array(80),A=1732584193,u=4023233417,s=2562383102,i=271733878,g=3285377520,m=(f=function f($){$=$.replace(/\r\n/g,"\n");for(var r="",o=0;o<$.length;o++){var e=$.charCodeAt(o);e<128?r+=String.fromCharCode(e):e>127&&e<2048?(r+=String.fromCharCode(e>>6|192),r+=String.fromCharCode(63&e|128)):(r+=String.fromCharCode(e>>12|224),r+=String.fromCharCode(e>>6&63|128),r+=String.fromCharCode(63&e|128))}return r}(f)).length,p=[];for(_=0;_<m-3;_+=4)t=f.charCodeAt(_)<<24|f.charCodeAt(_+1)<<16|f.charCodeAt(_+2)<<8|f.charCodeAt(_+3),p.push(t);switch(m%4){case 0:_=2147483648;break;case 1:_=f.charCodeAt(m-1)<<24|8388608;break;case 2:_=f.charCodeAt(m-2)<<24|f.charCodeAt(m-1)<<16|32768;break;case 3:_=f.charCodeAt(m-3)<<24|f.charCodeAt(m-2)<<16|f.charCodeAt(m-1)<<8|128}for(p.push(_);p.length%16!=14;)p.push(0);for(p.push(m>>>29),p.push(m<<3&4294967295),e=0;e<p.length;e+=16){for(_=0;_<16;_++)d[_]=p[e+_];for(_=16;_<=79;_++)d[_]=$(d[_-3]^d[_-8]^d[_-14]^d[_-16],1);for(_=0,a=A,C=u,h=s,x=i,n=g;_<=19;_++)c=$(a,5)+(C&h|~C&x)+n+d[_]+1518500249&4294967295,n=x,x=h,h=$(C,30),C=a,a=c;for(_=20;_<=39;_++)c=$(a,5)+(C^h^x)+n+d[_]+1859775393&4294967295,n=x,x=h,h=$(C,30),C=a,a=c;for(_=40;_<=59;_++)c=$(a,5)+(C&h|C&x|h&x)+n+d[_]+2400959708&4294967295,n=x,x=h,h=$(C,30),C=a,a=c;for(_=60;_<=79;_++)c=$(a,5)+(C^h^x)+n+d[_]+3395469782&4294967295,n=x,x=h,h=$(C,30),C=a,a=c;A=A+a&4294967295,u=u+C&4294967295,s=s+h&4294967295,i=i+x&4294967295,g=g+n&4294967295}var c=o(A)+o(u)+o(s)+o(i)+o(g);return c.toLowerCase()} // var pow = "" // try { // var part = atob(e.data).split(';') // if (part.length != 6 && part[4] !== "X-Response-Hash") return null; // var i = 0; // while(!SHA1(part[2] + part[5] + i).endsWith(part[3])) {i++;} // part[4] = i // pow = btoa(part.join(';')) // } catch (ex) { /*console.error(ex);*/ } // self.postMessage(pow) // } // var tPoWworker = createWorker(tPoW) // tPoWworker.postMessage(btoa(atob(_token) + btoa(JSON.stringify(data)))) // tPoWworker.onmessage = function (e) // { // if (callback) callback(e.data == "" ? null : e.data) // tPoWworker.terminate(); // } // } ?>function PoWCaptcha(r,e){var t,o,a,n=(t=function r(e){function t(r){function e(r,e){return r<<e|r>>>32-e}function t(r){var e,t,o,a="";for(e=0;e<=6;e+=2)t=r>>>4*e+4&15,o=r>>>4*e&15,a+=t.toString(16)+o.toString(16);return a}function o(r){var e,t,o="";for(e=7;e>=0;e--)o+=(t=r>>>4*e&15).toString(16);return o}var a,n,_,h,c,f,s,$,C,i=Array(80),d=1732584193,u=4023233417,g=2562383102,p=271733878,l=3285377520,v=(r=function r(e){e=e.replace(/\r\n/g,"\n");for(var t="",o=0;o<e.length;o++){var a=e.charCodeAt(o);a<128?t+=String.fromCharCode(a):a>127&&a<2048?(t+=String.fromCharCode(a>>6|192),t+=String.fromCharCode(63&a|128)):(t+=String.fromCharCode(a>>12|224),t+=String.fromCharCode(a>>6&63|128),t+=String.fromCharCode(63&a|128))}return t}(r)).length,A=[];for(n=0;n<v-3;n+=4)_=r.charCodeAt(n)<<24|r.charCodeAt(n+1)<<16|r.charCodeAt(n+2)<<8|r.charCodeAt(n+3),A.push(_);switch(v%4){case 0:n=2147483648;break;case 1:n=r.charCodeAt(v-1)<<24|8388608;break;case 2:n=r.charCodeAt(v-2)<<24|r.charCodeAt(v-1)<<16|32768;break;case 3:n=r.charCodeAt(v-3)<<24|r.charCodeAt(v-2)<<16|r.charCodeAt(v-1)<<8|128}for(A.push(n);A.length%16!=14;)A.push(0);for(A.push(v>>>29),A.push(v<<3&4294967295),a=0;a<A.length;a+=16){for(n=0;n<16;n++)i[n]=A[a+n];for(n=16;n<=79;n++)i[n]=e(i[n-3]^i[n-8]^i[n-14]^i[n-16],1);for(n=0,h=d,c=u,f=g,s=p,$=l;n<=19;n++)C=e(h,5)+(c&f|~c&s)+$+i[n]+1518500249&4294967295,$=s,s=f,f=e(c,30),c=h,h=C;for(n=20;n<=39;n++)C=e(h,5)+(c^f^s)+$+i[n]+1859775393&4294967295,$=s,s=f,f=e(c,30),c=h,h=C;for(n=40;n<=59;n++)C=e(h,5)+(c&f|c&s|f&s)+$+i[n]+2400959708&4294967295,$=s,s=f,f=e(c,30),c=h,h=C;for(n=60;n<=79;n++)C=e(h,5)+(c^f^s)+$+i[n]+3395469782&4294967295,$=s,s=f,f=e(c,30),c=h,h=C;d=d+h&4294967295,u=u+c&4294967295,g=g+f&4294967295,p=p+s&4294967295,l=l+$&4294967295}var C=o(d)+o(u)+o(g)+o(p)+o(l);return C.toLowerCase()}var o="";try{var a=atob(e.data).split(";");if(6!=a.length&&"X-Response-Hash"!==a[4])return null;for(var n=0;!t(a[2]+a[5]+n).endsWith(a[3]);)n++;a[4]=n,o=btoa(a.join(";"))}catch(_){}self.postMessage(o)},o=new Blob(["self.onmessage = ",t.toString()],{type:"text/javascript"}),a=window.URL.createObjectURL(o),new Worker(a));n.postMessage(btoa(atob("<?php echo generateHashString(); ?>")+btoa(JSON.stringify(r)))),n.onmessage=function(r){e&&e(""==r.data?null:r.data),n.terminate()}}<?php die(); } ?>