CAPTCHA AuthState
Introduction and overview
The CAPTCHA AuthState CaptchaState is designed to ensure that a web form is filled out by a human and not by a machine. In the default configuration this is done by displaying a picture with a word in the user's language. The shape of the letters of each word are obfuscated, so that it is hard for a computer to recognize the characters in the image, while it is relatively easy for a human to do so.
The CAPTCHA generation goes through different stages:
- First a vectorized image is generated using a word from a given word list (see the
dictionaryDir
property). - Then each letter can be randomly rotated a little (
maxRotation
property). It is also possible to randomly choose different sizes for each letter (within a reasonable range, see thevariableSiz
e property). But most important, letters are shifted, so that the spaces between them vanish (adjust theshiftCharsLeft
property for the chosen font size). This is because OCR (Optical Character Recognition) programs first have to find the letters in the image by segmenting it. If the spaces between the letters vanish, the segmentation step is likely to fail and with that the character recognition. - After rotating, resizing and shifting, the whole word is rendered into a pixmap.
- As a last step, an image processing library is used to further distort the image (see the
distortionFilters
property).
The sequence diagram below illustrates the communication between the client, auth, proxy and logrend. The image is generated within the auth server and then sent, base64 encoded to the proxy where it is decoded and sent to the client. The text challenge, which is used to generate the image, is stored in the user session and finally compared to the user input.
Note that for this AuthState to work, AWT must run in headless mode. Ensure that "-Djava.awt.headless=true" is set in the JAVA_OPTS of the auth server.
- Use a big enough wordlist. You can generate random words and removed the vocals but actual words might be nicer to read.
- Readability and placement of the letters may need fine tuning
- Consider removing words with problematic letters (e.g. 1, I, l).
Description
The following table and chapters describe the characteristics of the AuthState.
Topic | Description |
---|---|
Class | ch.nevis.esauth.auth.states.captcha.CaptchaState |
Logging | Captcha |
Auditing | none |
Marker | CAPTCHA:callenge/response |
Methods | process (all events) |
Properties
userInputCaptcha
(string, ${inargs:userInputCaptcha})The source of the user input. By default, the user input is provided in the userInputCaptcha attribute of the inargs.
width
(integer, 200)Width of the generated image, in pixels.
height
(integer, 50)Height of the generated image, in pixels.
maxRotation
(integer, 11)Each letter is rotated randomly in the interval [0, maxRotation], in degrees.
shiftCharsLeft
(float, 1)Letters are placed by looking at their bounding box and placing them next to each other. This parameter allows to shift each letter to the left, so that they overlap with the previous letter. This obstructs the segmentation an OCR (Optical Character Recognition) software does before character recognition. Adjust this parameter for a chosen font size, so that the letters overlap slightly.
fontName
(string, Univers)Name of the font. If the font cannot be found, a fallback to Univers is made. If Univers can also not be found, we default to the first available font.
fontSize
(integer, 32)Size of the font in typographical points.
maxGenerate
(integer, 100)Number of times a user can load a new CAPTCHA image, without successful authentication.
timeout
(duration in seconds, 300)Time the user has to type in the CATPCHA image.
dictionaryDir
(string, nevisauth instance config directory)Directory which contains the word lists. The dictionaries in this directory must be named according to the following convention:
CaptchaWords_[language in two letters].txt
, so for example: CaptchaWords_en.txt. or CaptchaWords_de.txt. The dictionaries are text files with one word on each line. We recommend using words with a length of 6 characters up to 20 characters.math
(boolean, false)Whether to display simple math exercises instead of words. Does not require a directory when turned on.
placeRandom
(boolean, false)Places the words or math questions randomly within the picture.
variableSize
(boolean, false)Varies the size of each individual character.
colors
(string, "#190773, #9F0013, #2A4580")For each generated Captcha image, the color of the font is randomly chosen from the given list of colors. The colors are expected in hex values (from 00 to ff). The order of colors is red, green, blue and each individual string must start with a #. Strings are separated with "," and optional spaces. Note that this is the same convention as HTML uses to specify colors.
backgroundColor
(string, #FFFFFF)Background color of the rendered CAPTCHA image.
distortionFilters
(string, "twirl, water")Specifies a list of distortion filters. Distortion can be turned off by setting the value to "noDistortion". Available filters are: "twirl", "pinch", "diffuse", "marble" and "water". This plug-in uses the JH Labs Java Image Processing library.
minWordlength
(int, 6)Restricts rendered words to words with a specified minimum length or characters. Other words will be dropped from the dictionary dynamically.
maxWordlength
(int, 10)Restricts rendered words to words with a specified maximum length or characters. Other words will be dropped from the dictionary dynamically.
paddingHorizontal
(int, 10)Horizontal padding within the captcha image, in pixels. The text of the captcha is never placed closer to the border than this value.
paddingVertical
(int, 10)Vertical padding within the captcha image, in pixels. The text of the captcha text is never placed closer to the border than this value.
Input
none
Transitions
ok
User input matches the string used to generate the CAPTCHA image, or the math exercise was solved correctly, in case math CAPTCHAs are used.
Output
none
Errors
lasterror=1
lasterrorinfo=Login failed
Notes
When using this AuthState, make sure that the Java VM is started with AWT in headless mode. Add "-Djava.awt.headless=true" to the vmargs. Do so by typing nevisauth config vmargs (as root).
Example
<AuthState name="Captcha"
class="ch.nevis.esauth.auth.states.captcha.CaptchaState" final="false">
<ResultCond name="ok" next="PasswordLogin" />
<Response value="AUTH_CONTINUE">
<Gui name="CaptchaGui" label="login.test.label">
<GuiElem name="lasterror" type="error" label="${notes:lasterrorinfo}" value="${notes:lasterror}" />
<GuiElem name="info" type="info" label="login.test.text" />
<GuiElem name="captcha_image" type="image" value="${request:currentResource}&render_captcha&r=${system:random.number.8}"/>
<GuiElem name="userInputCaptcha" type="text" optional="true" label="Enter the word displayed in the image" />
<GuiElem name="isiwebuserid" type="text" label="userid.label" value="${request:loginId}" />
<GuiElem name="isiwebpasswd" type="pw-text" label="password.label" />
<GuiElem name="submit" type="button" label="submit.button.label" value="Login" />
</Gui>
</Response>
</AuthState>
Enhanced example for captcha re-generation
<GuiElem name="newCaptcha" type="button" label="new.captcha.button.label"/>
#macro(renderFormControls $gui)
.
.
#if ($guiElem.type == "submit" || $guiElem.type == "button" || $guiElem.type == "reset")
#if ($guiElem.name == "newCaptcha")
<input type="$guiElem.type" name="$guiElem.name"
value="$utils.escapeHtml($guiElem.label)" onClick="window.location = window.location.href;" class="button">
#else
<input type="submit" name="$guiElem.name"
value="$utils.escapeHtml($guiElem.label)" class="button">
#end
#end