Tidbits @ Kassemi

A collection of opinions, thoughts, tricks and misc. information.

Sunday, August 28, 2005

 

Proving human existence: user authentication images

You've seen them everywhere (and probably have already programmed yours, so this is useless, but if not), and you know they're effective at keeping scripts from posting comments (at least) to your site. They are simple images that contain a string of numbers and letters, that the user needs to type into an input box to prove they're not a shell script. We'll use my image class that I posted yesterday to start this out. Don't make fun of my image class for missing certain obvious things that I realized where missing today. I didn't fix the image class yet, but I might if I ever get an audience and that audience wants a good one. Here is the class that extends the image class for creating those functions:



/*
* Basic PHP Image class
* Copyright (C) 2005 James Kassemi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.

* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.

* You can view the LGPL here: http://www.gnu.org/licenses/lgpl.html */
class image_manipulation extends image {
public $image;
public $width;
public $height;
public $color;

function new_image($width=200, $height=200){
$this->width = $width;
$this->height = $height;
$this->image = imagecreatetruecolor($width, $height);
imageantialias($this->image, true);
$background = imagecolorallocate($this->image, 225, 228, 169);
imagefill($this->image, 0, 0, $background);
}

function get_color($red, $green, $blue){
return imagecolorallocate($this->image, $red, $green, $blue);
}

function create_swirl_template($x, $y, $color, $angle_step=.05, $start_size=5, $size_stepx=.5, $size_stepy=.5, $revolutions=5, $rand_step_var=0){
$x = rand(0, $this->width);
$y = rand(0, $this->height);

$angle = rand(0, 360);
$sizex = $start_size;
$sizey = $start_size;

$steps_total = (360/$angle_step) * $revolutions;
for($i=0;$i<$steps_total;$i++){
if($rand_step_var > 0){
$size_0x = rand(-$rand_step_var, $rand_step_var) + $sizex;
$size_0y = rand(-$rand_step_var, $rand_step_var) + $sizey;
}else{
$size_0x = $sizex;
$size_0y = $sizey;
}
$y_0 = $size_0y * sin($angle);
$x_0 = $size_0x * cos($angle);
/* Optimizations */
while((
(($x_0 + $x) > $this->width || ($y_0 + $y) > $this->height) ||
(($x_0 + $x) < 0 || ($y_0 + $y) < 0)
)
&& $i < $steps_total){
$angle = $angle + $angle_step;
$sizex = $sizex + $size_stepx;
$sizey = $sizey + $size_stepy;
$x_0 = $size_0x * cos($angle);
$y_0 = $size_0y * sin($angle);
$i++;
}

/* END Optimizations */
$color_s = $color[rand(0,count($color)-1)];
//imagesetpixel($this->image, $x + $x_0, $y + $y_0, $color_s);
imagefilledellipse($this->image, $x + $x_0, $y+$y_0, 2, 2, $color_s);
$angle = $angle + $angle_step;
$sizex = $sizex + $size_stepx;
$sizey = $sizey + $size_stepy;
}

}
function string_it($string, $color){
$temp_string_placement = imagecreatetruecolor($this->width, $this->height);
imageantialias($temp_string_placement, true);
$background = imagecolorallocate($temp_string_placement, 125, 125, 125);
imagefill($temp_string_placement, 0, 0, $background);
$black = imagecolorallocate($temp_string_placement, 0, 0, 0);
$red = imagecolorallocate($temp_string_placement, 255, 255, 255);
$font = imagepsloadfont('./font.pfb');
$box_width = ($this->width / strlen($string))-10;
for($i=0;$i $x = $box_width * $i + 20;
$y = rand(40, $this->height);
if($i%2 == 0){
imagepstext($temp_string_placement, substr($string, $i, 1), $font, 40, $black, $background, $x, $y);
}else{
imagepstext($temp_string_placement, substr($string, $i, 1), $font, 40, $red, $background, $x, $y);
}
}

for($i=0;$i<$this->width;$i++){
for($i2=0;$i2<$this->height;$i2++){
$rgb = ImageColorAt($temp_string_placement, $i, $i2);

if($rgb == 0){
$color_s = $color[rand(0,2)];
imagefilledellipse($this->image, $i, $i2, 3, 3, $color_s);
}elseif($rgb==16777215){
$color_s = $color[rand(3,5)];
imagefilledellipse($this->image, $i, $i2, 3, 3, $color_s);
}

}

}
}

function print_display(){
$this->output($this->image, 'jpg', 100);
}

}


Okay. This one is pretty cool, really... I started out with grand intentions, but ended up with something that works just as well, and is a little quicker. What I was trying to do was create a spiral blur over text that was placed on an image. This would make it difficult for text recognition software to figure out what the text was... I would randomly create a spiral for every different blur, so they couldn't plot it out.

What I did instead was just use the spiral art, as it works without needing to add the additional blur layer... Just for kicks before I get started, with a few small changes to the spiral code, I generated these...


create_swirl_template(200,40,$colors, 0.01, 0.5, 0.2, 0.1, 3, 6);


create_swirl_template(250,250,$colors, 0.2, 0.5, 0.05, 0.04, 6, 5);


create_swirl_template(250,250,$colors, 0.002, 0.5, 0.05, 0.04, 6, 5);

Okay. I'm done showing off today's fun side of the work... We actually had something to do, right? Notice the commented imagepixelset() line? It was changed to imagefilledelipse() to make the shapes a little more clear... The following can be used to produce verification images:


$image = new image_manipulation;
$image->new_image(250,100);
$colors = array($image->get_color(24, 64, 130),
$image->get_color(0, 0, 20),
$image->get_color(0 ,0 ,10));
$image->create_swirl_template(200,40,$colors, 0.03, 0.5, 0.2, 0.1, 3,2);
$colors = array($image->get_color(50, 50, 0),
$image->get_color(50, 50, 50),
$image->get_color(100 ,100 ,100));
$image->create_swirl_template(5,75,$colors, 0.04, 0.5, 0.2, 0.1, 3,2);
$colors = array($image->get_color(0, 64, 130),
$image->get_color(0, 0,0),
$image->get_color(0 ,0 ,255),
$image->get_color(50, 50, 50),
$image->get_color(0, 0,0),
$image->get_color(0 ,0 ,255) );
$image->string_it("R5f2g", $colors);
$image->print_display();


That look exactly like this (changes spiral position, character position randomly when live):



Or you can obviously change colors, and layer things differently. Here's an example where the text is positioned below the spirals, and the text color matches the spirals:


$image = new image_manipulation;
$image->new_image(250,100);
$colors = array($image->get_color(50, 50, 0),
$image->get_color(50, 50, 50),
$image->get_color(100 ,100 ,100),
$image->get_color(24, 64, 130),
$image->get_color(0, 0, 20),
$image->get_color(0 ,0 ,10) );
$image->string_it("R5f2g", $colors);
$colors = array($image->get_color(24, 64, 130),
$image->get_color(0, 0, 20),
$image->get_color(0 ,0 ,10));
$image->create_swirl_template(200,40,$colors, 0.03, 0.5, 0.2, 0.1, 3,2);
$colors = array($image->get_color(50, 50, 0),
$image->get_color(50, 50, 50),
$image->get_color(100 ,100 ,100));
$image->create_swirl_template(5,75,$colors, 0.04, 0.5, 0.2, 0.1, 3,2);

$image->print_display();





With a quick little hack we can make it even more difficult to isolate the text from the background:


function create_swirl_template($x, $y, $color, $angle_step=.05, $start_size=5, $size_stepx=.5, $size_stepy=.5, $revolutions=5, $rand_step_var=0, $line=false){
$x = rand(0, $this->width);
$y = rand(0, $this->height);
$x_old = 0;
$y_old = 0;

$angle = rand(0, 360);
$sizex = $start_size;
$sizey = $start_size;

$steps_total = (360/$angle_step) * $revolutions;
for($i=0;$i<$steps_total;$i++){
if($rand_step_var > 0){
$size_0x = rand(-$rand_step_var, $rand_step_var) + $sizex;
$size_0y = rand(-$rand_step_var, $rand_step_var) + $sizey;
}else{
$size_0x = $sizex;
$size_0y = $sizey;
}
$y_0 = $size_0y * sin($angle);
$x_0 = $size_0x * cos($angle);
/* Optimizations */
while((
(($x_0 + $x) > $this->width || ($y_0 + $y) > $this->height) ||
(($x_0 + $x) < 0 || ($y_0 + $y) < 0)
)
&& $i < $steps_total){
$angle = $angle + $angle_step;
$sizex = $sizex + $size_stepx;
$sizey = $sizey + $size_stepy;
$x_0 = $size_0x * cos($angle);
$y_0 = $size_0y * sin($angle);
$x_old = $x_0;
$y_old = $y_0;
$i++;
}

/* END Optimizations */
$color_s = $color[rand(0,count($color)-1)];
//imagesetpixel($this->image, $x + $x_0, $y + $y_0, $color_s);
if(!$line){
imagefilledellipse($this->image, $x + $x_0, $y+$y_0, 2, 2, $color_s);
}else{
imageline($this->image, ($x + $x_0), ($y + $y_0), ($x + $x_old), ($y + $y_old), $color_s);
imageline($this->image, ($x + $x_0 + 1), ($y + $y_0 + 1), ($x + $x_old + 1), ($y + $y_old + 1), $color_s);
imageline($this->image, ($x + $x_0 - 1), ($y + $y_0 - 1), ($x + $x_old - 1), ($y + $y_old - 1), $color_s);
}

$angle = $angle + $angle_step;
$sizex = $sizex + $size_stepx;
$sizey = $sizey + $size_stepy;
$x_old = $x_0;
$y_old = $y_0;
}

}


Which turns out something like this:



The color array can be programmed for any RGB color values. As characters are printed, they alternate between using indexes 0-2 and indexes 3-5 on the color array for the string_it function. There is also an extra spiral on this image, but it is not truly necessary. Performance isn't bad. I have been getting an average of a single second rendering on my test server, which is quite good for a PHP program... If the calculations were to be done in C/C++, and then the result imported into PHP it might be a bit faster, but that's outside the scope of my discussion.

By using the interlapping lines and different colors, it is very difficult to get a good selection of the text by changing anything in your image editing software, which also indicates it will be very hard for a script to do it. I used a postscript font, but you can adapt this to use ttf very easily.

Comments: Post a Comment



<< Home

Archives

August 2005   September 2005   October 2005   November 2005   December 2005   January 2006   February 2006   March 2006   April 2006   June 2006   July 2006   August 2006   September 2006   October 2006   November 2006  

This page is powered by Blogger. Isn't yours?