11

How to create simple javascript/jquery client side captcha?

GEOCHET
  • 21,119
  • 15
  • 74
  • 98
Vlad Omelyanchuk
  • 3,021
  • 7
  • 29
  • 35
  • 5
    What is the point of a client-side CAPTCHA?!? The whole purpose of CAPTCHA is to prevent the server from responding to requests that are made by non-humans. What are you trying to achieve? Spam-bots will not be executing your JavaScript. Humans will. Spam-bots will not get annoyed (even if they could). Humans can, and will. – Amadan Jul 07 '10 at 15:38
  • This practice is not recommended as executing the captcha only tin the frontend open the door to the robots to crawling your DOM and scripts and avoid your human detection script. – Danhdds Feb 04 '21 at 15:25

11 Answers11

12

Why don't you use reCAPTCHA ? It's free, very efficient, and provides accessibility functionnalities.

3rgo
  • 3,115
  • 7
  • 31
  • 44
10

It can be done with HTML and a simple JavaScript code. Have a look at this:

 function Captcha(){
     var alpha = new Array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
   'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', 
       '0','1','2','3','4','5','6','7','8','9');
     var i;
     for (i=0;i<6;i++){
         var a = alpha[Math.floor(Math.random() * alpha.length)];
         var b = alpha[Math.floor(Math.random() * alpha.length)];
         var c = alpha[Math.floor(Math.random() * alpha.length)];
         var d = alpha[Math.floor(Math.random() * alpha.length)];
         var e = alpha[Math.floor(Math.random() * alpha.length)];
         var f = alpha[Math.floor(Math.random() * alpha.length)];
         var g = alpha[Math.floor(Math.random() * alpha.length)];
                      }
         var code = a + ' ' + b + ' ' + ' ' + c + ' ' + d + ' ' + e + ' '+ f + ' ' + g;
         document.getElementById("mainCaptcha").innerHTML = code
   document.getElementById("mainCaptcha").value = code
       }
function ValidCaptcha(){
     var string1 = removeSpaces(document.getElementById('mainCaptcha').value);
     var string2 =         removeSpaces(document.getElementById('txtInput').value);
     if (string1 == string2){
            return true;
     }else{        
          return false;
          }
}
function removeSpaces(string){
     return string.split(' ').join('');
}
.capt{
 background-color:grey;
 width: 300px;
 height:100px;
 
}

#mainCaptcha{
 position: relative;
 left : 60px;
 top: 5px;
 
}

#refresh{
 position:relative;
 left:230px;
 width:30px;
 height:30px;
 bottom:45px;
 background-image: url(rpt.jpg);
}

#txtInput, #Button1{
 position: relative;
 left:40px;
 bottom: 40px;
}
<link rel="stylesheet" type="text/css" href="estilo.css" />
<script type="text/javascript" src="script.js"></script>    
<body onload="Captcha();"> 
   <div class="capt"> 
   <h2 type="text" id="mainCaptcha"></h2>
   <p><input type="button" id="refresh" onclick="Captcha();"/></p>            <input type="text" id="txtInput"/>    
   <input id="Button1" type="button" value="Check" onclick="alert(ValidCaptcha());"/>
   </div>
</body>
Floern
  • 33,559
  • 24
  • 104
  • 119
Victor Machado
  • 127
  • 1
  • 2
5

here you are ;)

var captchaText;
$(function() {
  var pre = $('#captcha');
  captchaText = pre.text();
  pre.text('');

  var lines = ['', '', '', '', '']
  for (var ixLetter = 0; ixLetter < captchaText.length; ixLetter++) {
    var letter = captchaText.substr(ixLetter, 1);

    var letterLines = letters[letter];
    for (var ix = 0; ix < 5; ix++) {
      lines[ix] = lines[ix] + '  ' + letterLines[ix];
    }
  }
  for (var ix = 0; ix < 5; ix++) {
    pre.append(lines[ix] + '\n');
  }
});

function check() {
  if ($('#captchaCheck').val() == captchaText) {
    alert('you are probably human');
  } else {
    alert('you probably made a mistake. Don\'t worry. To err is also human.');
  }
}
var letters = {
  h: [
    'HH  HH',
    'HH  HH',
    'HHHHHH',
    'HH  HH',
    'HH  HH'
  ],
  i: [
    'II',
    'II',
    'II',
    'II',
    'II'
  ]
  // etc
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


<pre id="captcha">hi</pre> Please type what you see: <input id="captchaCheck" /> <input type="button" value="Check" onclick="check()" />
Ullas Hunka
  • 2,119
  • 1
  • 15
  • 28
Jan Willem B
  • 3,787
  • 1
  • 25
  • 39
  • This is fun but if you use the real letters to represent the letters what is the point? :P Also consider that he is doing this to avoid bots so the submit button and href of the form should be written on a succesful captcha – Fabiano Soriani Jul 07 '10 at 18:54
  • 1
    @Fabiano: It doesn't matter, because it's just a joke. – Guffa Jul 10 '10 at 13:54
  • You should make this a stack snippet so we can use it! :D – Nic Jun 15 '16 at 22:11
  • You should take that text and then draw it to a canvas and try to un-normalize the letters by shifting them, rotating them, overlapping them slightly, things that make reading them hard. Do this all in a private function scope (you put it in the global scope) so that it's not accessible what the value of the text is except to the script, loop and check for the correct value or use events or whatever but don't expose the answer in the code to public scope javascript. Then your approach should work in theory, but probably won't be better than what ReCaptcha already offers (for anyone else) – OG Sean Aug 17 '20 at 20:38
4

I agree with all the comments that client side captcha is fundamentally flawed, and I don't know know tough the hurdles have to be to mitigate any amount of spam...

To get an impression of what you're up against, though, check xRumer and GSA in action: YouTube: xEvil vs GSA Captcha Breaker

The software is also able to gather and decipher artificial intelligence such as security questions (i.e. what is 2+2?) often used by forums upon registration. Since the latest version of XRumer, the software is capable of collecting such security questions from multiple sources and is much more effective in defeating them.

wikipedia.org/wiki/XRumer

References:


So, caveats aside, here's a trivial alternative using HTML5 validation that's probably as ineffective as the other posts here! Presumably spambots would just add formnovalidate before submission, and would identify a honeypot field.

<form class="input">
  <label for="number" class="title">
    What is three plus four?
  </label>
  <br>
  <input 
    name="number" 
    required="required"
    pattern="(7|seven)" 
    oninvalid="this.setCustomValidity('Sorry, please enter the correct answer')"
    oninput="this.setCustomValidity('')"
  >
  
  <!-- Bonus honeypot field, hidden from the user.  The server should discard this response if it contains any input -->
  <input name="decoy" style="display: none;" />

  <input type="submit">
</form>
ptim
  • 14,902
  • 10
  • 83
  • 103
2

Client-Side captchas do not provide the protection a server-generated captcha would because the server can not check if the solved captcha is solved correctly.

chiborg
  • 26,978
  • 14
  • 97
  • 115
1

That is not possible.

You could create something that looks like a CAPTCHA, but it would only run when it's not needed, i.e. when the page is shown in a browser. When it's needed it won't run, as the program trying to break in won't run the client side script.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • "That is not possible." - such a strong phrase, what proves itself wrong time after time – Fabiano Soriani Jul 07 '10 at 18:56
  • 1
    @Fabiano: Such a strong phrases are waranted in cases like this. A client side CAPTCHA would actually be worse than nothing at all, as it gives a false sense of security, but offers no real security what so ever. – Guffa Jul 07 '10 at 21:37
  • I see your point and agree on your comment. But about *being not possible* likely, isn't right – Fabiano Soriani Jul 09 '10 at 18:15
  • 1
    @Fabiano: If you want something that *looks* like a CAPTCHA, it's of course possible to do using client script, but to make one that actually works is not possible. – Guffa Jul 10 '10 at 13:52
1
 function Captcha(){
     var alpha = new Array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
        'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', 
            '0','1','2','3','4','5','6','7','8','9');
     var i;
     for (i=0;i<6;i++){
         var a = alpha[Math.floor(Math.random() * alpha.length)];
         var b = alpha[Math.floor(Math.random() * alpha.length)];
         var c = alpha[Math.floor(Math.random() * alpha.length)];
         var d = alpha[Math.floor(Math.random() * alpha.length)];
         var e = alpha[Math.floor(Math.random() * alpha.length)];
         var f = alpha[Math.floor(Math.random() * alpha.length)];
         var g = alpha[Math.floor(Math.random() * alpha.length)];
                      }
         var code = a + ' ' + b + ' ' + ' ' + c + ' ' + d + ' ' + e + ' '+ f + ' ' + g;
         document.getElementById("mainCaptcha").innerHTML = code
         document.getElementById("mainCaptcha").value = code
       }
function ValidCaptcha(){
     var string1 = removeSpaces(document.getElementById('mainCaptcha').value);
     var string2 =         removeSpaces(document.getElementById('txtInput').value);
     if (string1 == string2){
            return true;
     }else{        
          return false;
          }
}
function removeSpaces(string){
     return string.split(' ').join('');
}
<h1 This works pretty well. </h1>
0

if your purpose is to simply deflect most bots, you might be able to get away with a simple script that chooses 2 numbers at random and asks user to add them.

Scott Evernden
  • 39,136
  • 15
  • 78
  • 84
  • 1
    Ок, but if spam bot use form.Submit() it's validation is not saffely, how i may check condition before form.Submit() If atribut on submit hadles only when member click on button submit – Vlad Omelyanchuk Jul 07 '10 at 15:52
  • 4
    That's exactly what everyone keeps telling you - it is not possible. you can't catch `form.submit()`. And especially you can't catch someone who parses your web-page, constructs a POST request and contacts your server programmatically (without a browser, without JavaScript). – Amadan Jul 10 '10 at 15:23
0

Try this:

<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<script>
/* created dynamically by server when preparing the page */
// f function is on server and not known by bot and is random per result 
// maybe bcrypt with a unique salt per request
var encodedResult = "f('X')"; 
var serverRenderedPositionSequence = [
    [0,0],
    [10,10],
    [20,20],
    [30,30],
    [40,40],
    [50,50],
    [60,60],
    [70,70],
    [80,80],
    [90,90],
    [100,100],
    [100,100],
    [100,100],
    [100,100],
    [100,100],
    [100,0],
    [90,10],
    [80,20],
    [70,30],
    [60,40],
    [50,50],
    [40,60],
    [30,70],
    [20,80],
    [10,90],
    [0,100]
];

window.index = 0;

window.move=function(but){
      but.style.left = (serverRenderedPositionSequence[window.index][0]+"px");
      but.style.top = (serverRenderedPositionSequence[window.index][1]+"px");
      window.index++;
      if(window.index<serverRenderedPositionSequence.length)
      {
          setTimeout(function(){
              window.move(but);
          },125);
      }
}
window.onload=function(){
  var but = document.getElementById("decoy-button");
  window.index=0;
  window.move(but);
};
function post()
{
  // do something with
  var xhrData= {
    encodedResult:encodedResult, 
    result:document.getElementById('result').value
  };
  var postXhr=function(){ /* HTTP POST */}
  
  // server checks if decoded "encoded result" is equal to the result (X here)
  postXhr(xhrData);
}
</script>
</head>
<body>
    <input id="result" type="text" value="Enter the message you see" />
    <input id="test" onclick="post()" type="button" value="Post" />
   
    <input id="decoy-button" type="button" value="   click me bot" style="width:0px;padding:0;position:absolute; left:0px; top:0px;" />
    <input id="secret" type="button" onclick="window.index=0;move( document.getElementById('decoy-button'))" value="repeat sequence" />
</body>
</html>

Then the server would only use bcrypt to check if result is true.

Since the pattern matching requires dynamically observing the screen, it would cause some bots to fail. These would fail:

  • bots that click to post button immediately
  • bots that don't run javascript
  • bots that mis-click the decoy buttons
  • bots that can't understand the leap between coordinates in the sequence
    • for example when drawing X, it leaps from bot-right to top-right effectively drawing a vertical line on imaginary plane which may trick some bots easily
  • bots that use OCR with "fixed" screenshot timings

To further obfuscate any "canvas" hack, the server could add quick random bursts (with decreased latency between leaps) into the sequence such that resulting image would look unrecognizable but human will see what it writes between those random bursts.

To add one more depth, you can ask a math problem like

1+1

instead of just string.

But, when a bot memoizes the encoded result and re-sends it, the security is broken. So, have a "session" for client and not send any encoded result. Just the randomized sequence and check for result only by server-side. Of course the session also costs database time/space but at least website admin/editor does not see spam on the control panel of website.

huseyin tugrul buyukisik
  • 11,469
  • 4
  • 45
  • 97
-1

please try this

<script type="text/javascript">
    $('#submitcapt').click(function(){
        var captval = "This will not do nothing";
        $.ajax({
            type : "POST",
            url : "externals/reCaptcha/ajaxaction.php",
            data : {loadval:captval},
            success : function( msg ) {
                if(msg=="1"){

                }else{

                }
            }
        })
    });
</script>

In ajaxaction.php please put the following code

<?php
    session_start();
    $val=$_POST['loadval'];
    $message= $_SESSION['random_number'];
    if($val==$message) {
        echo "1";
    }else{
        echo "2";
    }
?> 
Sandy Gifford
  • 7,219
  • 3
  • 35
  • 65
-1
$(document).ready(function() {
    DrawCaptcha();
});

function refreshCap() {
    DrawCaptcha();
}

function DrawCaptcha() {
    var a = Math.ceil(Math.random() * 10) + '';
    var b = Math.ceil(Math.random() * 10) + '';
    var c = Math.ceil(Math.random() * 10) + '';
    var d = Math.ceil(Math.random() * 10) + '';
    var e = Math.ceil(Math.random() * 10) + '';
    var f = Math.ceil(Math.random() * 10) + '';
    var g = Math.ceil(Math.random() * 10) + '';
    var code = a + ' ' + b + ' ' + ' ' + c + ' ' + d + ' ' + e + ' ' + f + ' ' + g;
    var test = document.getElementById("ContentPlaceHolder1_txtCapcha").value = code;
    alert(test);
}

function ValidCaptcha() {
    var str1 = removeSpaces(document.getElementById('ContentPlaceHolder1_txtCapcha').value);
    var str2 = removeSpaces(document.getElementById('ContentPlaceHolder1_txtinputCapcha').value);
    if (str1 != str2) {
        alert("Properly enter the Security code.");
        document.getElementById('ContentPlaceHolder1_txtinputCapcha').focus() return false;
    }
}

function removeSpaces(string) {
    return string.split(' ').join('');
}
VVN
  • 1,607
  • 2
  • 16
  • 25