paint-brush
How to Make a Captcha in Pythonby@manushifva
1,138 reads
1,138 reads

How to Make a Captcha in Python

by manushifvaJune 23rd, 2021
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Captcha is becoming complex and impractical with time. So, I dedicated myself to make my own system that's more practical. I used Python to build the system. The system does: Read the source images, create a random icon, generate random icon and save the image with a random name. Store the answer to the database, and add the unique hash for the identifier. The engine checks the answer and updates the data, sets it as solved. If it's not valid, the engine will try the process 'till 3 times'
featured image - How to Make a Captcha in Python
manushifva HackerNoon profile picture

Captcha is becoming complex and impractical with time. Here is an example:

So, I dedicated myself to make my own captcha system that's more practical. At first, I needed some idea for the captcha, and here it is:

I used Python to build the captcha system. Here is the snippet:

import random, string
from PIL import Image, ImageDraw
import numpy as np

bomb = Image.open("bomb.png")

bomb = bomb.resize((round(bomb.size[0] * 0.5), round(bomb.size[1] * 0.5)))
bombMask = bomb.convert("L")

lock = Image.open("lock.png")
lock = lock.resize((round(lock.size[0] * 0.5), round(lock.size[1] * 0.5)))
lockMask = lock.convert("L")

def randomName():
    strData = string.ascii_letters + string.digits
    response = ''
    for x in range(16):
        response += random.choice(strData)

    return response

def buildCaptcha():
    bgColor = (random.randint(200, 255), random.randint(200, 255), random.randint(200, 255))
    img = Image.new('RGB', ((bomb.size[0]) * 5 + (40 * 6), bomb.size[1] + round(bomb.size[1] * 1.5)), color = bgColor)

    result = []
    posX = 40
    maxHeight = 0
    for x in range(5):
        num = random.randint(1, 2)

        rotateDeg = random.randint(0, 360)
        if (num == 1):
            result.append(0)
            thisImg = bomb.rotate(rotateDeg, expand=1)
            thisMask = bombMask.rotate(rotateDeg, expand=1)
        else:
            result.append(1)
            thisImg = lock.rotate(rotateDeg, expand=1)
            thisMask = lockMask.rotate(rotateDeg, expand=1)

        num = random.randint(1, 3)
        if (num == 2):
            thisImg = thisImg.transpose(Image.FLIP_LEFT_RIGHT)
        elif (num == 3):
            thisImg = thisImg.transpose(Image.FLIP_TOP_BOTTOM)

        thisImg = thisImg.convert('RGBA')
        data = np.array(thisImg)
        red, green, blue, alpha = data.T
        white_areas = (red == 255) & (blue == 0) & (green == 0)

        itemColor = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
        if (itemColor == bgColor):
            itemColor[0] = random.randint(0, 255)

        data[..., :-1][white_areas.T] = itemColor
        thisImg = Image.fromarray(data)

        img.paste(thisImg, (posX, 50), thisMask)
        posX += lock.size[1] + 40

    draw = ImageDraw.Draw(img) 
    for x in range(random.randint(50, 80)):
        draw.line((random.randint(0, img.size[0]),random.randint(0, img.size[1]), random.randint(0, img.size[0]), random.randint(0, img.size[1])), fill=bgColor,  width=random.randint(3, 5))
        
    for x in range(random.randint(50, 200)):
        dotX = random.randint(0, img.size[0])
        dotY = random.randint(0, img.size[1])
        diameter = random.randint(1, 10)

        draw.ellipse((dotX, dotY, dotX + diameter, dotY + diameter), fill=(random.randint(0, 255), random.randint(0, 255), random.randint(100, 255)))

    for x in range(random.randint(15, 30)):
        draw.line((random.randint(0, img.size[0]) , random.randint(0, img.size[1]), random.randint(0, img.size[0]) + 20, random.randint(0, img.size[1])), fill=(random.randint(0, 255), random.randint(0, 255), random.randint(100, 255)),  width=random.randint(1, 3))

    filename = randomName() + '.png'
    img.save('captcha/padlock/' + filename)
    file = open('answer.csv', 'a')
    file.writelines(' '.join(str(v) for v in result) + ',' + filename + '\n')

So, what the system does is:
1. Read the source images,

bomb.png
and
padlock.png.

2. Create the background image with a random color.
3. Generate the random icon.
4. Change the icon color with a random color.
5. Add some random lines and dots.
6. Save the image with a random name.
7. Store the file location and captcha answer in a file named
answer.csv
.
8. Repeat it 1000 times.

Here is the result:

And here is another sample:

After that, we need the engine and user interface for the captcha. For the engine, I used PHP with MySQL database, and for the user interface, I used bootstrap. Here is the engine work:

1. Read the

answer.csv
to receive random captcha location and the answer.
2. Store the answer to the database, and add the unique hash for the identifier.
3. Give the unique hash, and file location to the UI.
4. The UI process the data and turn back the user response with a unique hash (of course) to the engine.
5. The engine checks the answer.
6. If valid, the engine updates the captcha data, sets it as solved. If it's not valid, the engine will try the process 'till 3 times.

Here is the final result:

Before submission:

Captcha authentication process:

On success:

On fail:

This is my first article! Thanks for reading. Have a good day.