simple flask captcha validation
https://pypi.org/project/flask-ishuman/
|
|
||
|---|---|---|
| flask_ishuman | ||
| scripts | ||
| tests | ||
| .editorconfig | ||
| .gitignore | ||
| LICENSE | ||
| MANIFEST.in | ||
| pyproject.toml | ||
| README.md | ||
| setup.cfg | ||
| tox.ini | ||
flask-ishuman
simple flask captcha validation
usage
a good example of usage can be found in /tests/main.py, although heres a basic example
import flask
import flask_ishuman
app = flask.Flask(__name__)
h = flask_ishuman.IsHuman()
@app.get("/")
def index():
c = h.new()
return ... # now render it, like c.image() maybe or c.rawpng() or something
@app.post("/")
def validate():
code = flask.request.form.get("code") # this if u have a <form> that has name=code in it, but ur free to get the `code` in any way u want
# if code is None then itll return false regardless
if h.verify(code):
pass # captcha valid
else:
pass # captcha invalid
app.config["SECRET_KEY"] = h.rand.randbytes(2048)
# firefox throws warnings if these are not set
app.config["SESSION_COOKIE_SAMESITE"] = "None"
app.config["SESSION_COOKIE_SECURE"] = True
h.init_app(app)
app.run("127.0.0.1", 8080)
heres the functions and classes we have :
IsHuman-- captcha wrapper__init__(image_args: dict, audio_args: dict) -> None-- constructor, passesimage_argsto captcha.image.ImageCaptcha and same withaudio_args, just for audio ( underlying captcha library, although i forked it )cimageattr is an instance ofcaptcha.image.ImageCaptchacaudioattr is an instance ofcaptcha.audio.AudioCaptcharandis a cryptographically secure randomness source, orsecrets.SystemRandom()skeyis the unique captcha key in the sessionappis the flask app ( can beNoneifinit_app()was not called )pepperis the pepper of captchas ( also can bNoneifinit_app()was not called )
init_app(app: flask.Flask) -> Self-- initialize flask app, set up variables, configuration, generate keysrandom(length: int | None) -> str-- returns a random code oflengthlength, uses a random number inCAPTCHA_RANGElength by defaultdigest(code: str, salt: bytes | None) -> (bytes, bytes, float)-- returns a salted and peppered sha3-512 digest of a code, returns(digest, salt, timestamp)set_code(code: str) -> Self-- sets the captcha to a codeget_digest() -> (bytes, bytes, float) | None-- returns the current captcha digest if available, returns(digest, salt, timestamp), returnsNoneif unavailable or expiredverify(code: str | None, expire: bool = True) -> bool-- returns if a code is a valid hash, ifcodeisNonewill always returnFalse, which helps to work with flask apis likeflask.request.from.get, will also callexpire()ifexpire=True( default ) is passednew(code: str | None, length: str | None, set_c: bool = True)-- returns a newCaptchaGenerator, passes code as the code and usesrandom(length)by default,set_code()is called ifset_cisTrue, which is the defaultexpire() -> Self-- expire the current captchaexpired_dt(ts: float) -> bool-- check if the current captcha is expired according to itsts( timestamp )auto_expire(ts: float) -> bool-- runsexpire()ifexpired_dt()isTrue, returns the result ofexpired_dt()
CaptchaGenerator-- generate captchas__init__(code: str, cimage: captcha.image.ImageCaptcha, caudio: captcha.audio.AudioCaptcha) -> None-- constructor, takes in the captcha code and captcha helperscodeis the captcha codecimageis an instance ofcaptcha.image.ImageCaptchacaudiois an instance ofcaptcha.audio.AudioCaptcha
rawpng() -> bytes-- returns raw png data used inpng()rawwav() -> bytes-- returns raw wav data used inwav()png() -> str-- returns base64 encoded png of the image captchawav() -> str-- returns base64 encoded wav of the audio captchaimage(alt: str = "Image CAPTCHA") -> str-- returns html to embed for the captcha,altattr is set asalt, note thtaltis not escapedaudio(alt: str = "Audio CAPTCHA", controls: bool = True) -> str-- returns the audio captcha embedding html,altattr is not set, but embded in theaudioelement asalt, andcontrolsis added too ifcontrolsis set toTrue, note thtaltis not escaped
what u have to do is basically :
- create
IsHuman - call
init_appon it - call
newon it - use functions provided in
CaptchaGeneratorto display captcha- for example embed it in html using
.png()or have a route like/captcha.pngto return the actual png although do whatever u want
- for example embed it in html using
configuration
SECRET_KEY-- this is default in flask, set this to a secure random value, this is used for session storage and protection, will throw a warning if unsetCAPTCHA_SALT_LEN-- the salt length to use for salting of hashes, by default32CAPTCHA_CHARSET-- the charset to use in captchas, by default all ascii letters, digits and characters@#%?CAPTCHA_RANGE-- a 2 value tuple storing(from, to)arguments, used to generation of random captcha lengths, by default from 4 to 8 ((4, 8))CAPTCHA_EXPIRY-- a float, which defines the lifetime of a single captcha in seconds, by default it isNonewhich means the lifetime is infiniteCAPTCHA_PEPPER_SIZE-- the size of the pepper value, by default2048CAPTCHA_PEPPER_FILE-- the pepper file to use, which is like a constant salt not stored in the session, by defaultcaptcha_pepper
these should be a part of app.config, although optional -- will use default values if unspecified
best configuration practices
- large, cryptographically secure, random secret key
- note that in production you should keep the key the same for all workers
- a salt length that is anywhere from 16 to 64 bytes, dont go overboard though as that will increase the size of the session
- charset of readable characters when messed with in a captcha sense
- a sensible range, so it isnt too large like 100 characters or too small like 1 characters
- a short expiry time, but not so sort that users cant figure it out in time, maybe like 5 to 10 mins, keep in mind audio captchas if ur using them, audio captchas tend to take longer
- a big pepper size, maybe like from 512 to 4096 bytes
styling and selection
- image captchas get the
image-captchaid (<img id=... />) - audio captchas get the
audio-captchaid (<audio id=...><source /><audio>)
logging
all logging of flask-ishuman is done through logging.DEBUG