Другая реализация в интересах Flask и в интересах WTForms
Давайте сожжем их GPU для глубокого изучения.
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
Недавний
- Скрытие первичных ключей базы данных UUID вашего веб-приложения
- Don't Repeat Yourself (DRY) с Jinja2
- SQLAlchemy, PostgreSQL, максимальное количество строк для user
- Показать значения в динамических фильтрах SQLAlchemy
- Безопасная передача данных с помощью шифрования Public Key и pyNaCl
- rqlite: альтернатива dist с высокой степенью готовности и SQLite
Большинство просмотренных
- Используя Python pyOpenSSL для проверки SSL-сертификатов, загруженных с хоста
- Использование UUID вместо Integer Autoincrement Primary Keys с SQLAlchemy и MariaDb
- Подключение к службе на хосте Docker из контейнера Docker
- Использование PyInstaller и Cython для создания исполняемого файла Python
- SQLAlchemy: Использование Cascade Deletes для удаления связанных объектов
- Flask Удовлетворительный запрос API проверка параметров запроса с помощью схем Маршмэллоу