Instantly share code, notes, and snippets.

Embed
What would you like to do?
<html>
<head></head>
<body>
<h1>NoCaptcha</h1>
<h2>Background</h2>
<p>This script shows that rate limiting can be passed off to the client. </p>
<p>This is extremely advantageous on a network like TOR where normal rate-limiting and utilities like Fail2Ban will not work as every connection is indistinguishable.</p>
<h2>Proof of work demo</h2>
<p>Once the number is factored into it's constituent primes, the submit button is enabled. The recieving script can use the primes much the same as a captcha.. just stop doing useful work unless the user proves something; in this case, that they spent at least a few seconds factoring a prime</p>
<p>Use admin/password for the demo.</p>
<?php
session_start();
// Never tell the user wether the prime or the password failed. This effectively stops bruteforce attempts and hampers automated signups on hidden services
$error_message = "There was an error.";
//If we've saved primes and they sent primes, check them
if(isset($_POST['prime']) && $_SESSION['primes']){
//If they're valid, great! Keep processing the login.
if($_POST['prime'] == $_SESSION['primes'] ){
//Don't ever do this IRL.
$credentials = array('admin'=>md5('password'));
if(md5($_POST['pass']) === $credentials[$_POST['user']]){
unset($_SESSION['primes']);
echo 'Welcome! You\'d likely get redirected at this point.';
}
else echo $error_message;
}
else echo $error_message;
}
else {
// Use way more primes. Like, WAY more primes. Ideally save a few million in a db and use a SQL query to get them at random. This is portable and works good enough for a demo.
$primes = array(42920617,42920623,42920641,42920651,42920653,42920681,42920687,42920701,42920741,42920743,42920819,42920827,42920863,42920879,42920887,42920897,42920909,42920929,42920939,42920951,42920953,42920959,42920989,42921001,42921023);
$a = $primes[mt_rand(0,count($primes)-1)];
$b = $primes[mt_rand(0,count($primes)-1)];
$primes = array($a, $b);
asort($primes);
$_SESSION['primes'] = $primes[0].','.$primes[1];
// Include a login script. I've included one inline.
?>
<form method="post">
<input type="text" name="user" id="user" value ="" placeholder="Username"><br>
<input type="password" name="pass" id="pass" value ="" placeholder="Password" ><br>
<input type="hidden" name="prime" id="prime" value ="<?php echo $a * $b ?>" >
<input type="submit" id="submit" value="Factoring primes..." disabled></input>
</form>
<script>
function factors(num)
{
var
n_factors = [],
i;
//skip 1...
for (i = 2; i <= Math.floor(Math.sqrt(num)); i += 1)
if (num % i === 0)
{
n_factors.push(i);
if (num / i !== i)
n_factors.push(num / i);
}
n_factors.sort(function(a, b){return a - b;}); // numeric sort
console.log(n_factors);
return n_factors;
}
// Ideally we'd do this AFTER the DOM is fully loaded.
var result = factors(document.getElementById('prime').value);
document.getElementById('prime').value = result;
document.getElementById('submit').value = 'Submit';
document.getElementById("submit").disabled = false;
</script>
<?php } ?>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment