angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Другая реализация в интересах Flask и в интересах WTForms

Давайте сожжем их GPU для глубокого изучения.

4 июля 2019
post main image
Original photo unsplash.com/@barnikakovacs.

captcha В прошлом я писал, PHP чтобы ограничить подписку на рассылку новостей по электронной почте, работал хорошо, на самом деле он по-прежнему используется и сегодня. Вы не можете на самом деле блокировать регистрацию спама. Есть регистрационные роботы, но есть и люди, которым платят несколько баксов, чтобы залить ваш сайт фальшивыми или троллями. Такова реальность, и мы должны смотреть правде в глаза. А теперь есть еще и глубокое изучение, с помощью которого можно взломать captcha код всего за 15 минут.

Мы, разработчики веб-сайтов, должны выдвигать идеи по борьбе с поддельными регистрациями. Это невозможно, но мало что можно сделать, и добавление a captcha является одним из них.

Для многих, вероятно, скучных вещей, вы можете найти достаточно библиотек, делающих это за вас. Я, я не хочу пользоваться библиотекой, я хочу писать свои собственные Python, учить малыша. И поскольку я ценю конфиденциальность посетителей моих сайтов, я также не хочу использовать ReCaptcha или другое удаленное решение.

Представленное ниже captcha решение работает, но не закончено, просто необходимо добавить простое искажение. Она не совсем Flask конкретна, просто... Я впервые использовал BytesIO для сохранения изображения не в файл, а в память. Flask Особенность заключается в том, как отображать его в форме регистрации и на веб-странице. Обратите внимание, что это не полное решение, это лишь детали наиболее важных деталей.

Ниже приведен способ генерации. captcha captcha_fonts_path() - это функция, которую необходимо выполнить для импорта шрифта(ов) для captcha.

import math
import random
import secrets

from PIL import Image, ImageFont, ImageDraw, ImageOps
from io import BytesIO


def captcha_create():

    use_chars = ['A', 'b', 'C', 'd', 'e', 'f', 'h', 'K', 'M', 'N', 'p', 'R', 'S', 'T', 'W', 'X', 'Y', 'Z']
    use_nums = ['2', '3', '4', '6', '7', '8', '9']

    code_char_count = 5
    code_chars = []
    for i in range(code_char_count):
        s = random.randrange(2)
        if s & 1:
            code_chars.append( random.choice(use_chars) )
        else:
            code_chars.append( random.choice(use_nums) )

    # captcha dimensions
    w = 300
    h = 100
    background_color = (255, 255, 255)
    im_captcha = Image.new('RGB', (w, h), background_color)
    # equal width pieces for each code char
    w_code_char = int(math.floor(w/code_char_count))
    h_code_char = h

    ttf_file = os.path.join(captcha_fonts_path(), 'Arial.ttf')

    font_size = int(w_code_char * 3/4)
    draw_x = int(w_code_char/4)
    draw_y = int(h/4)

    # draw code chars one by one at different angle (with different font)
    w_code_char_offset = 0
    for code_char in code_chars:
        im_font = ImageFont.truetype(ttf_file, font_size)
        im_code_char = Image.new('RGB', (w_code_char, h_code_char), background_color)
        draw = ImageDraw.Draw(im_code_char)
        # see font size
        draw.text( (draw_x, draw_y), code_char, fill='black',  font=im_font)
        # random angle is between -20 - +20
        angle = random.randrange(40) - 20
        im_code_char_rotated = im_code_char.rotate(angle,  fillcolor=background_color)
        # paste char image into chars image
        im_captcha.paste(im_code_char_rotated, (w_code_char_offset, 0))
        w_code_char_offset += w_code_char

    # do some line / distortion stuff (to be implemented)

    # save (in memory) as jpg
    im_captcha_io = BytesIO()
    im_captcha.save(im_captcha_io, format='JPEG')

    captcha_code = ''.join(code_chars)
    captcha_image_data = im_captcha_io.getvalue()

    captcha_token = secrets.token_urlsafe()
    captcha_token_hashed = hash_string(captcha_token)

    return captcha_token, captcha_token_hashed, captcha_code, captcha_image_data

Для отображения captcha изображения Flask в браузере:

@auth.route('/captcha', methods=['GET'])
def captcha():

    captcha_token, captcha_token_hashed, captcha_code, captcha_image_data = captcha_create()

    resp = make_response(captcha_image_data)
    resp.content_type = "image/jpeg"
    return resp

Теперь мы также должны показать captcha изображение в форме. Я использую Jinja макросы, которые помещают все поля форм на странице.WTForms Это экономит столько времени! Чтобы поместить captcha изображение в форму нам нужен виджет. Как это сделать? Конечно, я не WTForms профессионал и считаю, что примеров виджетов должно быть гораздо больше. Вот мое решение. В form.py я создал виджет для CaptchaCodeField, он просто выводит изображение на экран:

class CaptchaCodeOutput(Input):
    def __call__(self, field, **kwargs):
        return kwargs.get('value', '<img src="' + field._value() + '" style="border: 1px solid #dddddd; ">')

class CaptchaCodeField(StringField):
    widget = CaptchaCodeOutput()
    

class AuthRegisterForm(FlaskForm):

    ....
    captcha_token = HiddenField('Captcha token')

    captcha_image_url = CaptchaCodeField(_l('Captcha image url'))

    captcha_code = StringField(_l('Code'), filters=[strip_whitespace], validators=[
        DataRequired()])

    accept_conditions = BooleanField(_l('I have read and accept the privacy policy *'),validators=[
        DataRequired()])

    submit = SubmitField(_l('Register'))

    def validate_password(self, field):
        password = field.data
        if not valid_password(password):
            raise ValidationError(valid_password_message())

Затем в view.py я помещаю URL captcha изображения в это поле перед вызовом ():

    ....
    captcha_image_url = url_for('auth.captcha', captcha_token=captcha_token)
    form.captcha_token.data = captcha_token
    form.captcha_image_url.data = captcha_image_url

    if captcha_error:
        flash(_l('The code you entered did not match the code from the image. Please try again.'))

    return render_template(
        'auth/register_step2.html', 
        form=form)

Я думаю, что все еще стоит потратить усилия, чтобы разместить captcha на вашем сайте, если посетители вашего сайта не должны за что-то платить. Конечно, сейчас у нас есть глубокое изучение, но инструментарий captcha разработчиков тоже сегодня более наполнен. Подумайте об использовании разных шрифтов, разных по ширине и высоте шрифтов, добавлении искажений, обратных цветов, будьте творческими, будьте непредсказуемы, давайте сожжем их графические процессоры для глубокого изучения! Вы также можете рассмотреть другие captcha решения, такие как вопросы и ответы.

ImportError: Модуль _imagingft C не установлен

Конечно, при использовании подушки появилось еще одно замечательное сообщение об ошибке, см. выше. Это можно решить, добавив freetype, freetype-dev. Вы можете проверить это, войдя в контейнер и запустив его в интерактивном режиме:

phython3

а потом печатать:

from PIL import features
features.check('freetype2')

Если freetype установлен, то вернется True, если нет, то вернется False. Потому что я использую и Alpine изображение, которое я должен был изменить в своем Dockerфайле:

FROM python:3.6-alpine as base
MAINTAINER Peter Mooring peterpm@xs4all.nl peter@petermooring.com

RUN mkdir /svc
WORKDIR /svc
COPY requirements.txt .

# install package dependencies
# COPY requirements.txt /requirements.txt, requirements.txt already copied 
# Solve 'No working compiler found' error, 
# see: https://github.com/gliderlabs/docker-alpine/issues/458
# Solve 'The headers or library files could not be found for jpeg, a required dependency when compiling Pillow from source.', 
# see https://blog.sneawo.com/blog/2017/09/07/how-to-install-pillow-psycopg-pylibmc-packages-in-pythonalpine-image/
# Solve: ImportError: The _imagingft C module is not installed (when using '.paste' of Pillow)
# Solved after adding freetype, freetype-dev


RUN rm -rf /var/cache/apk/* && \
    rm -rf /tmp/*

RUN apk update

# Instead, I run python setup.py bdist_wheel first, then run pip wheel -r requirements.txt for pypi packages.

RUN apk add --update \
    curl \
    python3 \ 
    pkgconfig \ 
    python3-dev \
    openssl-dev \ 
    libffi-dev \ 
    musl-dev \
    make \ 
    gcc \
    freetype \
    freetype-dev \
    jpeg-dev zlib-dev \
    libmagic \
    && rm -rf /var/cache/apk/* \
    && pip wheel -r requirements.txt --wheel-dir=/svc/wheels

# the wheels are now here: /svc/wheels

FROM python:3.6-alpine

RUN apk add --no-cache \
    freetype \
    freetype-dev \
    jpeg-dev zlib-dev \
    libmagic

COPY --from=base /svc /svc

WORKDIR /svc
RUN pip install --no-index --find-links=/svc/wheels -r requirements.txt

# after installation, remove wheels, does not free up space, probably because we are in new layer, too bad is some 20MB
#RUN rm -R *

# create and set working directory
RUN mkdir -p /home/flask/app/web
WORKDIR /home/flask/app/web

# copy app code into container
COPY . ./

# create group and user used in this container
RUN addgroup flaskgroup && adduser -D flaskuser -G flaskgroup && chown -R flaskuser:flaskgroup /home/flask

USER flaskuser

Ссылки / кредиты

How I developed a captcha cracker for my University's website
https://dev.to/presto412/how-i-cracked-the-captcha-on-my-universitys-website-237j

How to break a CAPTCHA system in 15 minutes with Machine Learning
https://medium.com/@ageitgey/how-to-break-a-captcha-system-in-15-minutes-with-machine-learning-dbebb035a710

Оставить комментарий

Комментируйте анонимно или войдите в систему, чтобы прокомментировать.

Комментарии

Оставьте ответ

Ответьте анонимно или войдите в систему, чтобы ответить.