How To Implement A Captcha In An Erlang Web Application

In this post, I’ll show you a way to implement a Captcha challenge-response test in an Erlang web application. This method is a simplified version of what we use in our Business Management application

It takes part in an ongoing series of Erlang tutorials that can be found on our Erlang website

captcha

I’ll assume that you have a working Erlang node and a fresh export of the Mochiweb svn (Read our guide to set up Erlang and Mochiweb on Ubuntu if not already done).

So let’s start by installing ImageMagick:

sudo apt-get install imagemagick

Then, we’ll create a brand new app named ‘myapp’ using the new_mochiweb.erl script (Discover our video tutorial to start developing web apps on Erlang if you feel lost).

Here is a quick recap :

~/mochiweb/scripts/./new_mochiweb.erl myapp ~/
cd ~/myapp && make clean && make && ./start-dev.sh

At this point we should be able to browse this url http://127.0.0.1:8000

Now it’s time to write some Erlang code into our myapp_web.erl controller both in the GET and in the POST part of the case statement.

Briefly said, here is the big picture:

Starting with a modified index.html page (see the first step hereunder), we will call our application server at “/captcha” with the GET method.

The server will be in charge of :

  • Generate a random alphanumeric string,
  • Build a new image based on this string by calling Imagemagick through the os:cmd function. The image will be temporarily stored in the /tmp directory and will be deleted as soon as it is read,
  • Encrypt the string content by computing a SHA MAC message code based on a random generated key that we will store in ram
    using Ets

The newly created image along side with a cookie containing the SHA message will be sent back to the browser.

We will use the same “/captcha” url but this time with the POST method in order to submit a form with the code entered at the client.

A check will be performed at the server side using the cookie information, the code and the public key stored in ram.

Let’s go for real:

Step 1 – The HTML

Juste copy paste the following html replacing the content of:

~/myapp/priv/www/index.html

Step 2 – One for the GET, One for the POST

Here, the code speaks for itself, the most interesting part will be explained in the next step.

Two remarks though. First, in the ‘GET’ part, we are returning a response with a “image/png” content-type. Secondly, in the POST part, you will find an example of code for redirecting a request to another location.

We edit this file:

~/myapp/src/myapp_web.erl

and here is the code we should end with.

Step 3 – mycaptcha module

Now, the cool part ;)

We are going to create a new module:

~/myapp/src/mycaptcha.erl

and paste the following lines in it.

First there is the new() function which is used to create our ‘Captcha’. We use the ets module in order to store the tuple containing the randomly generated binary key. This key will be used to check if the string provided by the user and the cookie content match.

This new storage will be set as public, which means that the info will be accessible by subsequent calls.

Secondly, we build a new filename based on the current local time and we generate a random suite of alphanumerical characters. For this purpose, we use the generate_rand(Length) function which is accepting as the only argument the length of the string that we want to generate.

Then, we write the Imagemagick command for building a PNG image with a transparent background and save the result in /tmp/filname.png. We read and store the entire content of the file in a variable and delete the PNG.

Finally, using the crypto module, we build the SHA message based on the public stored key and our 5 characters string. The controller will be in charge of building the cookie and of sending back the binary image to the client.

Note : I’m doing some additional math on the message I want to encrypt in order to make things a little bit more difficult to the potential guessers. Since strings are considered as a list of integer in Erlang, it’s quite easy to sum the total of our characters for instance and insert the result into the message we want to encrypt. When the Captcha will be checked, we will take care of extracting this noisy info before the match.

It’s time to test it all:

cd ~/myapp && make

Browse to http://127.0.0.1:8000 and you should see a nice distorted image with an input field below (cfr top). We can click the image to get a new one, or enter the code and submit the form. If the check is ok, a “Cool!” message will be displayed on the screen.

Enjoy !

Note about Captcha and Spammers: Captcha is not a 100% secure method even if it remains a good way to protect your application from bot users. Spammers have recently succeeded at cracking Windows Live Hotmail and Google, so be sure to add some kind of algorithm to lower the risk.

5 thoughts on “How To Implement A Captcha In An Erlang Web Application

  1. This doesn’t work for me:( …. I can see the image and enter the string and it always gives a “CRASH REPORT” when the POST tries to retrive the cryptokey. The “captcha” table has disappeared. So I dumped some variables including “ets:all()” and found that all the “GET” requests come in on the same PID and the POST (for some reason) comes in on another PID. So PID#2 cannot see the table from PID#1.

    I don’t know mochiweb well enough to determine why it either killed or started another process. I am guessing that the client closed the socket and the child was terminated. I have tried w/ Opera and Firefox and get the same behaviour … I do not have IE handy to try it out too.

    But I think this shows a precarious process … right?

  2. Each request (Post or Get) starts its own process and that’s perfectly fine. In this article, in order to keep the example as simple as possible, I decided to use the ‘public’ option when creating the Ets table. This option allows any process to read or write to the table.
    That’s definitely not a good practice but it was good enough for the purpose of the post. To make things a little bit more robust I should have used Mnesia … and that’s exactly what I propose to do now. Just paste the following files in your src folder and compile. Enjoy ;)

    http://friendpaste.com/1NMX9kyo7qep1VJtgUSXKT
    http://friendpaste.com/3QgNpijQDRsecOI37FmGLl
    http://friendpaste.com/15I6lRT5dat4AZr0nz0Q1N

  3. That works a lot better for me:) … weird thing is I copied the example including making ets tables public. Strange that my POST process could not see the ets table. Hmmm :^? Thanks for the updates.

  4. Pingback: pozycjonowanie wroclaw