Reading majic's journal

May 14, 2005 05:24 # 35975

majic *** has all the information you need...

The story of how I made something useful

?% | 1

I've been messing with Ruby on Rails for a little while now trying to see if I can finally get something done on my website plans. I am a terrible procrastinator and when things get challenging I usually start dragging my feet. So the wheels have been spinning lately and I've been so preoccupied with other crap that I could barely focus on what I've been wanting to do with my website for GOD knows how long. Literally years, but I digress.

I've been trying to plan out a website for a long time and one aspect of the website will have anonymous posting. I wanted to add anonymous posting because it's extremely quick to add thoughts to a particular blurb/blog/headline/whatever. Normally websites just add a form to the end of the page being shown and you fill in a little bit of information and click the submit button and your thoughts are added to the page. While this is great the spam bots will find you sooner or later and start filling your pages up with Free Texas Holdem links. So the new thing website admins are doing to combat this is by adding an image embedded with a random code that you also have to enter before you can submit a form. This type of security test is known as CAPTCHA (defined below) and is something that has sparked my interest of late. Today I wrote up some code to demonstrate this and I hope that maybe somebody can find it useful (besides me).

The following represents a CAPTCHA "completely automated public Turing test to tell computers and humans apart" test. What you see is an image with an embedded code. This code can be used to validate a form has been submitted by a human and not a spam bot. The code in plain text represents the embedded code taken from a session variable.

This documents the code I used to generate this test. I'm hoping this can be used by others if they are getting into web development with Ruby on Rails. The actual image creation is done with GD which is available for a number of scripting languages and it can easily be applied to those as the API is pretty consistant.

Image

Plain Text

insert CAPTCHA image here...

3897

-- Oops -- Seems I updated my tutorial and I didn't think about the image I had posted here. The image doesn't correspond to the code below anymore. Silly me. I've updated it to make it more sophisticated. The tutorial can be found here

The code below is the ApplicationController of a test Rails app.

class ApplicationController < ActionController::Base
	require 'MyCodeImage'

	def index
		ci = MyCodeImage.new 
		ci.generate 6
		
		@session['captchaFilename'] = ci.captchaFilename
		@session['captchaCode'] = ci.captchaCode
		
		render("captcha")
	end
end

The following sample HTML illustrates how you'd insert the CAPTCHA image into your page.

<html>
<title>CAPTCHA Test</title>
<body>
<img src="images/captcha/<%= @session['captchaFilename'] %>">
</body>
</html>

The following code is of the MyImageCode class which creates the CAPTCHA image using the GD library and a small password generator class. This class creates an image with a random name. The image file name can be accessed by captchaFilename and the plain text code can be accessed by captchaCode. The files are saved in public/images/captcha and will be automatically deleted after they reach an age of 60 seconds. As far as I can tell letting them live that long should not cause any problems. If you have a suggestion about the image files and how they are handled please let me know.

class MyCodeImage
	require 'GD'
	require 'PasswordGenerator'

	attr_reader :captchaFilename, :captchaCode

	def initialize
		if ! FileTest.directory?("public/images/captcha") then
			Dir.mkdir("public/images/captcha")
		end
	end

	def generate length
		deleteOldCaptchaFiles
				
		# create a new image
		im = GD::Image.new((length*9)+5, 25)

		# allocate some colors
		black = im.colorAllocate(0,0,0)
		white = im.colorAllocate(255,255,255)

		code = PasswordGenerator.new
		code.gen length
		codeword = code.password

		im.string(GD::Font::GiantFont, 3, 5, codeword, white)

		filename = rand 99999
				
		imgFile = File.new("public/images/captcha/#{filename}.png", "w")
		
		im.png imgFile
	
		imgFile.close
		
		im.destroy
	
		@captchaFilename = filename.to_s + ".png"
		@captchaCode = codeword
	end

	private
	def deleteOldCaptchaFiles
		captchaDir = "public/images/captcha/"

		Dir.open(captchaDir) do |dir|
			dir.each do |file|
				unless (file=="." or file=="..") then
					# The CAPTCHA image will live for 60 seconds before it's deleted
					if Time.now.to_i - File.mtime(captchaDir + file).to_i > 60 then
						File.delete(captchaDir + file)
					end
				end
    			end
		end

	end
end

The last piece of code is the code generator for which I'm using a simple password generator class.

class PasswordGenerator
	require 'digest/md5'
	
	attr_reader :password
	
	def initialize
		@password="";
		
		chars="abcdefghijklmnopqrstuvwxyz"
		num="1234567890"
		spec='!@#$%^&*()-+=~'
		
		lower = chars.split('')
		upper = chars.upcase.split('')
		number = num.split('')
		special = spec.split('')
		
		@tokens = [lower, upper, number, special]
	end
	
	def gen length
		@password = ""
		
		length.times do
			type = rand @tokens.length
			seed = rand(@tokens[type].length-1)
			@password << @tokens[type][seed]
		end
	end

	def md5 
		Digest::MD5.hexdigest(@password)
	end
end

The actual image creation could be more sophisticated to make it extremely difficult or impossible for OCR software to decode. The point is that for most uses of this software spammers would not waste their time to try to spam your site. This is exactly what we want! What I did here was demonstrate that it's pretty easy to add this stuff to your website. I am new to Ruby (a little over 1 week) so the code I've written is probably less than optimal. If you see any areas for improvement please let me know.

This post was edited by majic on May 15, 2005.

May 14, 2005 13:16 # 35977

majic *** replies...

Re: The story of how I made something useful

The next piece of the puzzle is to make the image more sophisticated. Just about all the CAPTCHA images I've seen on blogs and other websites have special effects that distort the image to make it even more difficult for OCR (Optical Character Recognition) software.

But despite that I wonder how much trouble it'd be to try to crack this simple CAPTCHA. One would still have to write some piece of software to hit the page, get the image, crack the image then post to the form.

How much more difficult does adding the text effects make it? Do we have any people here familiar with GD that could give me a tip or two?

May 14, 2005 16:21 # 35980

Aynjell *** replies...

Re: The story of how I made something useful

The speed at which you pick up a new language and run with it absolultely amazes me. I hear that once you figure out hte core logic of programming, and no at least one language, all others are easy. I guess that means my starting with C++ will pay off in the end?

I should be ashamed of myself.

May 14, 2005 16:32 # 35981

majic *** replies...

Re: The story of how I made something useful

The speed at which you pick up a new language and run with it absolultely amazes me.

Well I appreciate that, I really do.

I guess that means my starting with C++ will pay off in the end?

That's what I started with but admittedly I am not that great with C++. I prefer scripting languages.

On a side note I've greatly enhanced the image sophistication over the last couple of hours. Now my CAPTCHA security image is looking more like how the professionals do it.

http://frankhale.org/captcha_test.jpg

May 14, 2005 18:19 # 35984

majic *** throws in his two cents...

Re: The story of how I made something useful

I've made the image quite a bit better than previous. Here is the latest page I made documenting it:

http://frankhale.org/CAPTCHA_RoR_Tutorial.html

CAPTCHA image

I like the looks of this one and I believe it'll be a little more secure.

Again if there is anyone toying around with Ruby on Rails and has some tips to make this code better please let me know. If you want the sample RoR app that I'm using to code this let me know and I'll put it online.

This post was edited by majic on May 14, 2005.

May 14, 2005 19:31 # 35986

Aynjell *** replies...

Sorry...

Hey, tell the guys I'm sorry to not be on IRC. I'm having a hard time getting anything useful in linux working with the reiser4 patchset. I've reinstalled about 5 times today trying to get the shit to work. :( So, I said fuck it and I'm install the vanilla kernel. I should be on shortly... :D

I will be starting my own patch-set. I am that pissed off. Features included:

Supermount. (This is manditory) :D
Possibly reiser4.

Anything past that is beyond me. :D Perhaps some input from you and the guys? I know I NEED supermount to feel at home... I've heard CK uses some stairfcase something or another that speeds stuff up. I know I want supermount for cedega, greatly improves cedega compatibility. Prolly 10 fold...

I should be ashamed of myself.

This post was edited by Aynjell on May 14, 2005.

May 14, 2005 19:46 # 35989

majic *** replies...

Re: Sorry...

Hey, tell the guys I'm sorry to not be on IRC.

dude #sandbox is sucking right now. Everybody just sits idle so I'm shopping around for a new channel. Idling sometimes is okay but when the channel is 80% idle (and only like 2 people ever talking consistantly) it's rediculous.

Sure we have more people logging in but we have less people actually talking.

The level of talking is just not where it needs to be, sorry. If I just wanted to see somebodies nick and not talk to them I'd use IM.

I'm not bitching, I'm telling the truth. Have fun sitting in silence in the #sandbox channel. Haha..

What is so good about Supermount?

May 14, 2005 22:10 # 35991

Aynjell *** replies...

Re: Sorry...

Supermount automatically mounts drives that contain removable media. They also do not hinder your opening the drive in question. Feel the need to open your drive? Press the button. Need data off of a cd? Put the disc in. No commands, no bullshit. If you are a desktop and or gamer, it is the most important patchset available to you. Many wine-based applications do not handle the mounting process very elegantly. This is a workaround with positive side effects. It makes you computer more comfortable and more or less steals window's one good idea. I figured it out by the way:

Alsa. Fucking sound libraries. Why couldn't the devs stick with OSS? It worked, it was there, and it kept things simple. Then they introduced alsa, and now drivers are harder to come by becase you have two mainstream sound daemons to code for, and then you have to code in support for the other, thus increasing work for payoff. Alsa was a bad idea... No, it was a good idea, but implemented too late or too soon. Timing was off, enough said. :(

Anyway, since it was not the kernel I was using, but the alsa library, I will be reinstalling tonight. I'll prolly install xchat and quickpakage it to my other drive, and then run a few more tests to verify that I was correct then I should be off again to restart the install. After I get it all installed, I can rapidly reproduce X, X Chat and gaim, though they will be recompiled after everything is in place. One again, sorry I've been away, as you can tell linux has been being mean to me. :(

I should be ashamed of myself.

This post was edited by Aynjell on May 14, 2005.

May 14, 2005 22:18 # 35993

majic *** replies...

Re: Sorry...

I'm confused why you have to keep starting from scratch. Any reason other than to change the filesystem from reiser4 to something that isn't as buggy?

May 14, 2005 23:13 # 35994

Aynjell *** replies...

Yep.

?% | 1

That was the initial assumption, so I reverted from reiser4 to reiser3. Now that I know for a fact that it's not filesystem based, I'll once again setup with reiser4. This time, I'll save all of my configuration files and all of my setup documents making it take me only about 5 mines. I'll also do a quickpgk world to take everything installed and wrap it up into a nice little package. That way install is a snap. Should be down for less than 30 minutes and back up with reiser4 and xorg. Anyway, it's the version of Alsa-lib i had running. Now everything is rock solid. No wierd issues except one:

My network card is failing. This network card is a part of the motherboard and as such, being dead, it's going to drive me nuts. Instead of buying another card, or board even, I'll just buy the upgrade I wanted. Prolly get another 512 of ram, a PCI express video card a 3700+ san diego, a very impressive chip if I read the specs right. 1MB of L2 cache. :D Anyway, I'm on right now, getcher arse on IRC fag boy.

I should be ashamed of myself.

May 15, 2005 03:32 # 36001

majic *** feels excited about...

Re: Yep.

I think next on the agenda for tutorials is a nice tute on how to do user authentication from a database table in Ruby on Rails. Yeah I'll document what I've done and throw it into a web page format with screenshots or something and put it online for anyones consumption...

@Ginsterbusch - Did you look into Ruby on Rails yet?


Small text Large text

Netalive Amp (Skin for Winamp)