Security

Captcha

Picture captchas made on your own server: distorted letters as a PNG data URL and the answer to keep in the session.

Version 1.0.0 · 2 functions · 1 crate

#forms#spam#images#sign-up

Functions

What use captcha.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use captcha_new, captcha_check from captcha (Quillo 0.6).

captcha_new(length: int, hard: bool) → mapor an error

A new captcha of length characters (4 to 8): { image, answer }. image is a data:image/png;base64,... URL for an <img src>, 220 by 110 pixels; keep answer on the server (in the session), never in the page. hard adds more noise and waves.

captcha_check(answer: str, typed: str) → bool

Whether what the visitor typed is the answer: spaces around it ignored, and capitals too, since some letters look alike in both. An empty answer on either side is false.

Get it

$ quillo install captcha               # captcha.qwer into this folder$ quillo install captcha --example     # and its example, captcha.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/captcha.qwer$ curl -O https://quillolang.org/plugins/files/captcha.qwe

Then, in your program, all of it or only what you call:

main.qwe
use captcha.qweruse captcha_new, captcha_check from captcha      // Quillo 0.6: only these

It uses this crate, which cargo fetches the first time the plugin is built:

[dependencies]
captcha = "0.0.9"

Example

captcha.qwe
// A contact form with a picture captcha; the answer stays in the session.////   quillo run captcha.qwe      then open http://localhost:8080/contactuse "captcha.qwer"route GET /contact {    let c = captcha_new(5, false)?    set_session({ captcha: c.answer }, { max_age: "10m" })    <form method="post" action="/contact">        <img src={c.image} alt="The letters to type"/>        <input name="answer" autocomplete="off" placeholder="The letters above"/>        <textarea name="message"></textarea>        <button>Send</button>    </form>}route POST /contact { form: { answer: str, message: str } } {    if !captcha_check(session().captcha ?? "", answer) { reply 400 "The letters did not match: try a new picture." }    set_session({})    { sent: message }}route GET /check {    let c = captcha_new(6, true)?    { length: len(c.answer), png: starts_with(c.image, "data:image/png;base64,"), right: captcha_check(c.answer, upper(c.answer)), wrong: captcha_check(c.answer, "nope") }}
$ quillo run captcha.qwe$ curl 'localhost:8080/contact'$ curl 'localhost:8080/check'

Source

captcha.qwer
//! Picture captchas made on your own server: distorted letters as a PNG data URL and the answer, for sign-up and contact forms without a third party.//! version: 1.0.0//!//! [dependencies]//! captcha = "0.0.9"use captcha::filters::{Dots, Noise, Wave};use captcha::Captcha;use std::collections::HashMap;/// A new captcha of `length` characters (4 to 8): `{ image, answer }`. `image` is a `data:image/png;base64,...` URL for an/// `<img src>`, 220 by 110 pixels; keep `answer` on the server (in the session), never in the page. `hard` adds more noise and waves.pub fn captcha_new(length: i64, hard: bool) -> Result<HashMap<String, String>, String> {    let n = length.clamp(4, 8) as u32;    let mut c = Captcha::new();    c.add_chars(n);    if hard {        c.apply_filter(Noise::new(0.5)).apply_filter(Wave::new(2.0, 20.0)).apply_filter(Wave::new(2.0, 20.0).vertical()).apply_filter(Dots::new(15));    } else {        c.apply_filter(Noise::new(0.3)).apply_filter(Wave::new(2.0, 12.0)).apply_filter(Dots::new(6));    }    c.view(220, 110);    let answer = c.chars_as_string();    let image = c.as_base64().ok_or("the picture could not be made")?;    Ok(HashMap::from([        ("image".to_string(), format!("data:image/png;base64,{}", image)),        ("answer".to_string(), answer),    ]))}/// Whether what the visitor typed is the answer: spaces around it ignored, and capitals too, since some letters look alike in both./// An empty answer on either side is false.pub fn captcha_check(answer: &str, typed: &str) -> bool {    let (a, t) = (answer.trim().to_lowercase(), typed.trim().to_lowercase());    !a.is_empty() && a.len() == t.len() && a.bytes().zip(t.bytes()).fold(0u8, |acc, (x, y)| acc | (x ^ y)) == 0}
Thread
76Useful? Like it, or add to its thread below.

Thread 0 posts

On Captcha: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.