[ANN] flatulent-0.0.3

(the demo has been updated too)

NAME

   flatulent : CAPTCHA for FIGLET.

SYNOPSIS

   the flatulent gem provides brain dead simple to use, but internally cunning,
   ascii art (figlet) captcha for ruby.

URI

   http://codeforpeople.com/lib/ruby
   http://rubyforge.org/projects/codeforpeople

HOW DO I GET FLATULENT?

   gem install flatulent

HISTORY
   0.0.3:
     - following are now all equivalent when posting (thanks botp)

         0==o==O==Q (zero, oh's, and queue)
         l==l (one and el)
         2==z==Z (two and z's)
         5==s==S (5 and s's)

     - random horizontal and vertical displacement of each char

     - vastly improved background noise based on figlet char shapes

     - inputs are case sensitive (thanks john joyce, chris carter)

     - expanded rails examples

   0.0.2

     - ajax gets stinky: Flatulent.ajax! the result of this new addition is
       that the captcha itself doesn't appear in the source file at all

     - blowfish encoding for timebomb and captcha fields

     - auto server key configuration using hostname and mac address

     - improved noise algorithm

     - improved character placement (chars shared edges to make ocr'ing harder)

   0.0.1

     - initial version

RAILS EXAMPLES

   REGULAR METHOD (LESS SECURE):

     def controller_action
       if params.has_key? 'flatulent'
         Flatulent.validate! params
       end

       render :inline => <<-html
         <html><body>
           #{ Flatulent.form }
         </body></html>
       html
     end

   AJAX METHOD (MORE SECURE):

     def controller_action
       if params.has_key? 'flatulent'
         Flatulent.validate! params
       end

       render :inline => <<-html
         <html>
           <head> <%= javascript_include_tag 'prototype' %> </head>
           <body>
             <form action='./' method='post'>
               <%= Flatulent.ajax %>
               <input type='submit' name='submit' value='submit' />
             </form>
           </body>
         </html>
       html
     end

DOCS

   see source in ./lib/*
   see the example rails project in ./rails

ONLINE SAMPLES

   http://drawohara.tumblr.com/post/4791838
   http://drawohara.tumblr.com/post/4944987
   http://drawohara.tumblr.com/post/4968766

ONLINE DEMO OF AJAX METHOD

   http://fortytwo.merseine.nu:3000/flatulent/ajax -- try to break it!

enjoy.

-a

···

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

I can only guess 1 in 5 of the "images" -- that is one way to cut down
on spammers :slight_smile:
pth

···

On 7/5/07, ara.t.howard <ara.t.howard@gmail.com> wrote:

ONLINE DEMO OF AJAX METHOD

   http://fortytwo.merseine.nu:3000/flatulent/ajax -- try to break it!

The method used in this captcha is very is to break. In fact I can
solve
the captchas 6 times as fast as it takes to generate them (locally)
in
only 63 lines of code. I do this by generating a regexp for each
possible
character. As the characters don't get damaged by the noise (as they
get
in most image bases captchas) this works all of the time.

$ ruby benchmark.rb
                   user system total real
generate: 0.160000 0.020000 0.180000 ( 0.192005)
setup: 0.030000 0.000000 0.030000 ( 0.025381)
break: 0.010000 0.000000 0.010000 ( 0.010908)
generate 200: 12.100000 1.000000 13.100000 ( 13.125787)
break 200: 2.050000 0.100000 2.150000 ( 2.152749)
$ wc -l deflatulent.rb /usr/local/lib/ruby/gems/1.8/gems/
flatulent-0.0.3/lib/flatulent.rb
      63 deflatulent.rb
     604 /usr/local/lib/ruby/gems/1.8/gems/flatulent-0.0.3/lib/
flatulent.rb
     667 total
$ cat benchmark.rb
require 'deflatulent'
require 'flatulent'
require 'benchmark'

defl = html = code = nil
pairs = Array.new(200)

GC.disable

Benchmark.bm(13) do |x|
  x.report("generate:") { flat = Flatulent.new; html = flat.form; code
= flat.string }
  x.report("setup:") { defl = Deflatulent.new }
  x.report("break:") { raise unless defl.deflatulent(html) ==
code }

  x.report("generate 200:") { 200.times{|index| flat = Flatulent.new;
pairs[index] = [flat.form,flat.string] } }
  x.report("break 200:") { pairs.map{|(html,code)| raise unless
defl.deflatulent(html) == code } }

end
$ cat deflatulent.rb
require 'flatulent'

class Deflatulent

  def initialize font="big"
    font = Text::Figlet::Font.new(File.join(Flatulent.fontdir,font
+".flf"))
    typesetter = Text::Figlet::Typesetter.new font
    letters = ('A'..'Z').to_a + ('1'..'9').to_a
    @lines_array = letters.map{|letter| [letter,
gen_figlet_lines_array(typesetter[letter])] }
  end

  def deflatulent string
    if string =~ /<pre id='[a-zA-Z0-9_-]+_element' style='.*?'>(.*?)<\/

/m

      string = $1

      [[/<\/?span>/,''],["&nbsp;"," "],["<br>","\n"],["&lt;","<"],
["&gt;",">"],["&quot;",'"'],["&amp;","&"]].each do |args|
        string.gsub!(*args)
      end
    end

    width = string.index("\n")
    string.tr!("\n","")
    solution =

    @lines_array.each do |(letter,(length,lines))|

      re = "(?="
      lines.each{|line| re << line << ".{#{width-length}}" }
      re << ")"

      string.scan(Regexp.new(re, Regexp::MULTILINE)) do
        solution[$~.begin(0) % width] = letter
      end
    end

    solution.join
  end

private
  def gen_figlet_lines_array string
    lines = string.split("\n")
    lines.shift while lines.first.strip.empty?
    lines.pop while lines.last.strip.empty?

    lines.each{|e|e[0,1]=""} while lines.all?{|e|e[0,1]==' '}
    lines.each{|e|e[-1,1]=""} while lines.all?{|e|e[-1,1]==' '}

    [lines[0].length,lines.map{|e|e.split('').map{|q|(q == ' ' ? '.' :
Regexp.escape(q))}.join}]
  end

end

if __FILE__ == $0
  defl = Deflatulent.new(ARGV[0] || "big")
  loop do
    input = ""
    while line=gets and not line.chomp.empty?
      input << line
    end
    puts defl.deflatulent(input)
    break unless line
  end
end

···

--
Jannis Harder

Wow, no kidding. Here are a couple it tried on me:

Yikes!

James Edward Gray II

···

On Jul 5, 2007, at 7:51 AM, Patrick Hurley wrote:

On 7/5/07, ara.t.howard <ara.t.howard@gmail.com> wrote:

ONLINE DEMO OF AJAX METHOD

   http://fortytwo.merseine.nu:3000/flatulent/ajax -- try to break it!

I can only guess 1 in 5 of the "images" -- that is one way to cut down
on spammers :slight_smile:

Patrick Hurley wrote the following on 05.07.2007 14:51 :

ONLINE DEMO OF AJAX METHOD

   http://fortytwo.merseine.nu:3000/flatulent/ajax -- try to break it!

I can only guess 1 in 5 of the "images" -- that is one way to cut down
on spammers :slight_smile:
pth

I can confirm there are sometimes problems with the output (on Firefox 2.0.0.4). 1/4 of the time no recognizable character is shown.

Other notes:

I don't like the obstrusive Ajax feature at all (I use NoScript...): what's the benefit? Spammers trying to get around captcha can easily make an extra step and make XmlHTTPRequests too... From what I understand, there's at least a way to generate pure HTML, but I'd still like to understand why AJAX is an option.

I'm not sure why there are nested span in the captcha :

<span><span><span><span><span><span><span>&nbsp;</span></span></span></span></span></span></span>

?! Is it to accomodate rendering bugs?

There are &nbsp; in a <pre> with style='...,white-space:pre,... '. Seems the author *really* wants to be sure that spaces can not be rendered with newlines...

Lionel

···

On 7/5/07, ara.t.howard <ara.t.howard@gmail.com> wrote:

yeah - it's a redering bug but i can't figure our what causes it - thanks!

-a

···

On Jul 5, 2007, at 6:51 AM, Patrick Hurley wrote:

I can only guess 1 in 5 of the "images" -- that is one way to cut down
on spammers :slight_smile:
pth

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

hmmm - not for me?

cfp:~ > ruby a.rb
                    user system total real
generate: 0.140000 0.020000 0.170000 ( 0.178928)
setup: 0.020000 0.000000 0.020000 ( 0.022138)
break: Flatulent.version : 0.0.4
a.rb:63: failed on attempt 1 (RuntimeError)
         from /opt/local/lib/ruby/1.8/benchmark.rb:293:in `measure'
         from /opt/local/lib/ruby/1.8/benchmark.rb:377:in `report'
         from a.rb:63
         from /opt/local/lib/ruby/1.8/benchmark.rb:177:in `benchmark'
         from /opt/local/lib/ruby/1.8/benchmark.rb:207:in `bm'
         from a.rb:59

cfp:~ > cat a.rb
require 'flatulent'
require 'benchmark'
require 'flatulent'

class Deflatulent
   def initialize font="big"
     font = Text::Figlet::Font.new(File.join(Flatulent.fontdir,font +".flf"))
     typesetter = Text::Figlet::Typesetter.new font
     letters = ('A'..'Z').to_a + ('1'..'9').to_a
     @lines_array = letters.map{|letter| [letter, gen_figlet_lines_array(typesetter[letter])] }
   end

   def deflatulent string
     if string =~ /<pre id='[a-zA-Z0-9_-]+_element' style='.*?'>(.*?)<\/ pre>/m
       string = $1
       [[/<\/?span>/,''],["&nbsp;"," "],["<br>","\n"],["&lt;","<"], ["&gt;",">"],["&quot;",'"'],["&amp;","&"]].each do |args|
         string.gsub!(*args)
       end
     end

     width = string.index("\n")
     string.tr!("\n","")
     solution =

     @lines_array.each do |(letter,(length,lines))|

       re = "(?="
       lines.each{|line| re << line << ".{#{width-length}}" }
       re << ")"

       string.scan(Regexp.new(re, Regexp::MULTILINE)) do
         solution[$~.begin(0) % width] = letter
       end
     end

     solution.join
   end

private
   def gen_figlet_lines_array string
     lines = string.split("\n")
     lines.shift while lines.first.strip.empty?
     lines.pop while lines.last.strip.empty?

     lines.each{|e|e[0,1]=""} while lines.all?{|e|e[0,1]==' '}
     lines.each{|e|e[-1,1]=""} while lines.all?{|e|e[-1,1]==' '}

     [lines[0].length,lines.map{|e|e.split('').map{|q|(q == ' ' ? '.' : Regexp.escape(q))}.join}]
   end
end

defl = html = code = nil
pairs = Array.new(200)

GC.disable
i = 0

begin
   Benchmark.bm(13) do |x|
     i += 1
     x.report("generate:") { flat = Flatulent.new; html = flat.form; code = flat.string }
     x.report("setup:") { defl = Deflatulent.new }
     x.report("break:") { raise "failed on attempt #{ i }" unless defl.deflatulent(html) == code }
     x.report("generate 200:") { 200.times{|index| flat = Flatulent.new; pairs[index] = [flat.form,flat.string] } }
     x.report("break 200:") { pairs.map{|(html,code)| raise unless defl.deflatulent(html) == code } }
   end
ensure
   puts "Flatulent.version : #{ Flatulent.version }"
end

nevertheless, i'm not for one second claiming flatulent is ready for prime time. however, i will state that i think it's quite a bit of work if you use it in the intended way, which is for the html to make an ajax call to get the flatulent source because this make said source available only to javascript. no doubt someone could crack it from there, but the latest version adds vertical and horizontal offset to each char. my version is turning that source into a png. anyhow, the attention is welcome - but next time send a patch! :wink:

-a

···

On Jul 6, 2007, at 12:29 PM, jannis@harderweb.de wrote:

The method used in this captcha is very is to break. In fact I can
solve
the captchas 6 times as fast as it takes to generate them (locally)
in
only 63 lines of code. I do this by generating a regexp for each
possible
character. As the characters don't get damaged by the noise (as they
get
in most image bases captchas) this works all of the time.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

Ara,
That is because you set defl and flat inside a block, without setting
the variables to nil before the block is executed, so they stay
existing for the actual decode stage.

···

On 7/7/07, ara.t.howard <ara.t.howard@gmail.com> wrote:

On Jul 6, 2007, at 12:29 PM, jannis@harderweb.de wrote:

> The method used in this captcha is very is to break. In fact I can
> solve
> the captchas 6 times as fast as it takes to generate them (locally)
> in
> only 63 lines of code. I do this by generating a regexp for each
> possible
> character. As the characters don't get damaged by the noise (as they
> get
> in most image bases captchas) this works all of the time.

hmmm - not for me?

cfp:~ > ruby a.rb
                    user system total real
generate: 0.140000 0.020000 0.170000 ( 0.178928)
setup: 0.020000 0.000000 0.020000 ( 0.022138)
break: Flatulent.version : 0.0.4
a.rb:63: failed on attempt 1 (RuntimeError)
         from /opt/local/lib/ruby/1.8/benchmark.rb:293:in `measure'
         from /opt/local/lib/ruby/1.8/benchmark.rb:377:in `report'
         from a.rb:63
         from /opt/local/lib/ruby/1.8/benchmark.rb:177:in `benchmark'
         from /opt/local/lib/ruby/1.8/benchmark.rb:207:in `bm'
         from a.rb:59

cfp:~ > cat a.rb
require 'flatulent'
require 'benchmark'
require 'flatulent'

class Deflatulent
   def initialize font="big"
     font = Text::Figlet::Font.new(File.join(Flatulent.fontdir,font
+".flf"))
     typesetter = Text::Figlet::Typesetter.new font
     letters = ('A'..'Z').to_a + ('1'..'9').to_a
     @lines_array = letters.map{|letter| [letter,
gen_figlet_lines_array(typesetter[letter])] }
   end

   def deflatulent string
     if string =~ /<pre id='[a-zA-Z0-9_-]+_element' style='.*?'>(.*?)<
\/ pre>/m
       string = $1
       [[/<\/?span>/,''],["&nbsp;"," "],["<br>","\n"],["&lt;","<"],
["&gt;",">"],["&quot;",'"'],["&amp;","&"]].each do |args|
         string.gsub!(*args)
       end
     end

     width = string.index("\n")
     string.tr!("\n","")
     solution =

     @lines_array.each do |(letter,(length,lines))|

       re = "(?="
       lines.each{|line| re << line << ".{#{width-length}}" }
       re << ")"

       string.scan(Regexp.new(re, Regexp::MULTILINE)) do
         solution[$~.begin(0) % width] = letter
       end
     end

     solution.join
   end

private
   def gen_figlet_lines_array string
     lines = string.split("\n")
     lines.shift while lines.first.strip.empty?
     lines.pop while lines.last.strip.empty?

     lines.each{|e|e[0,1]=""} while lines.all?{|e|e[0,1]==' '}
     lines.each{|e|e[-1,1]=""} while lines.all?{|e|e[-1,1]==' '}

     [lines[0].length,lines.map{|e|e.split('').map{|q|(q == ' ' ?
'.' : Regexp.escape(q))}.join}]
   end
end

defl = html = code = nil
pairs = Array.new(200)

GC.disable
i = 0

begin
   Benchmark.bm(13) do |x|
     i += 1
     x.report("generate:") { flat = Flatulent.new; html = flat.form;
code = flat.string }
     x.report("setup:") { defl = Deflatulent.new }
     x.report("break:") { raise "failed on attempt #{ i }" unless
defl.deflatulent(html) == code }
     x.report("generate 200:") { 200.times{|index| flat =
Flatulent.new; pairs[index] = [flat.form,flat.string] } }
     x.report("break 200:") { pairs.map{|(html,code)| raise unless
defl.deflatulent(html) == code } }
   end
ensure
   puts "Flatulent.version : #{ Flatulent.version }"
end

nevertheless, i'm not for one second claiming flatulent is ready for
prime time. however, i will state that i think it's quite a bit of
work if you use it in the intended way, which is for the html to make
an ajax call to get the flatulent source because this make said
source available only to javascript. no doubt someone could crack it
from there, but the latest version adds vertical and horizontal
offset to each char. my version is turning that source into a png.
anyhow, the attention is welcome - but next time send a patch! :wink:

-a
--
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama

--
Chris Carter
concentrationstudios.com
brynmawrcs.com

It seems that google groups added line breaks inside the regexp that
somehow
turned into spaces for you... try it without the space before pre...
if that
doesn't work I can upload my code somewhere...

···

On 7 Jul., 19:17, "ara.t.howard" <ara.t.how...@gmail.com> wrote:

   def deflatulent string
     if string =~ /<pre id='[a-zA-Z0-9_-]+_element' style='.*?'>(.*?)<
\/ pre>/m ########## there is a space before pre
       string = $1
       [[/<\/?span>/,''],["&nbsp;"," "],["<br>","\n"],["&lt;","<"],
["&gt;",">"],["&quot;",'"'],["&amp;","&"]].each do |args|
         string.gsub!(*args)
       end
     end

--
Jannis Harder

??

# defl = html = code = nil ### irrelevant
pairs = Array.new(200)

GC.disable
i = 0

begin
   Benchmark.bm(13) do |x|
     i += 1
     defl = html = code = nil ### irrelevant
     x.report("generate:") { flat = Flatulent.new; html = flat.form; code = flat.string }
     x.report("setup:") { defl = Deflatulent.new }
     x.report("break:") { raise "failed on attempt #{ i }" unless defl.deflatulent(html) == code }
     x.report("generate 200:") { 200.times{|index| flat = Flatulent.new; pairs[index] = [flat.form,flat.string] } }
     x.report("break 200:") { pairs.map{|(html,code)| raise unless defl.deflatulent(html) == code } }
   end
ensure
   puts "Flatulent.version : #{ Flatulent.version }"
end

it fails on the very first attempt:

cfp:~ > ruby a.rb
                    user system total real
generate: 0.140000 0.020000 0.170000 ( 0.179983)
setup: 0.020000 0.000000 0.020000 ( 0.022315)
break: Flatulent.version : 0.0.4
a.rb:64: failed on attempt 1 (RuntimeError)
         from /opt/local/lib/ruby/1.8/benchmark.rb:293:in `measure'
         from /opt/local/lib/ruby/1.8/benchmark.rb:377:in `report'
         from a.rb:64
         from /opt/local/lib/ruby/1.8/benchmark.rb:177:in `benchmark'
         from /opt/local/lib/ruby/1.8/benchmark.rb:207:in `bm'
         from a.rb:59

cheers.

-a

···

On Jul 7, 2007, at 11:25 AM, Chris Carter wrote:

Ara,
That is because you set defl and flat inside a block, without setting
the variables to nil before the block is executed, so they stay
existing for the actual decode stage.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

my version doesn't seem to have line breaks - here it is:

   http://drawohara.tumblr.com/post/5164285

thanks for having a go - i'm not sure this can be made to work, but i'm still very interested in an ImageMagick-less captcha system.

cheers

-a

···

On Jul 7, 2007, at 11:41 AM, jannis@harderweb.de wrote:

On 7 Jul., 19:17, "ara.t.howard" <ara.t.how...@gmail.com> wrote:

   def deflatulent string
     if string =~ /<pre id='[a-zA-Z0-9_-]+_element' style='.*?'>(.*?)<
\/ pre>/m ########## there is a space before pre
       string = $1
       [[/<\/?span>/,''],["&nbsp;"," "],["<br>","\n"],["&lt;","<"],
["&gt;",">"],["&quot;",'"'],["&amp;","&"]].each do |args|
         string.gsub!(*args)
       end
     end

It seems that google groups added line breaks inside the regexp that
somehow
turned into spaces for you... try it without the space before pre...
if that
doesn't work I can upload my code somewhere...
--
Jannis Harder

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

Huh, I guess I am blind then...

···

On 7/7/07, ara.t.howard <ara.t.howard@gmail.com> wrote:

On Jul 7, 2007, at 11:25 AM, Chris Carter wrote:

> Ara,
> That is because you set defl and flat inside a block, without setting
> the variables to nil before the block is executed, so they stay
> existing for the actual decode stage.
>

??

# defl = html = code = nil ### irrelevant
pairs = Array.new(200)

GC.disable
i = 0

begin
   Benchmark.bm(13) do |x|
     i += 1
     defl = html = code = nil ### irrelevant
     x.report("generate:") { flat = Flatulent.new; html = flat.form;
code = flat.string }
     x.report("setup:") { defl = Deflatulent.new }
     x.report("break:") { raise "failed on attempt #{ i }" unless
defl.deflatulent(html) == code }
     x.report("generate 200:") { 200.times{|index| flat =
Flatulent.new; pairs[index] = [flat.form,flat.string] } }
     x.report("break 200:") { pairs.map{|(html,code)| raise unless
defl.deflatulent(html) == code } }
   end
ensure
   puts "Flatulent.version : #{ Flatulent.version }"
end

it fails on the very first attempt:

cfp:~ > ruby a.rb
                    user system total real
generate: 0.140000 0.020000 0.170000 ( 0.179983)
setup: 0.020000 0.000000 0.020000 ( 0.022315)
break: Flatulent.version : 0.0.4
a.rb:64: failed on attempt 1 (RuntimeError)
         from /opt/local/lib/ruby/1.8/benchmark.rb:293:in `measure'
         from /opt/local/lib/ruby/1.8/benchmark.rb:377:in `report'
         from a.rb:64
         from /opt/local/lib/ruby/1.8/benchmark.rb:177:in `benchmark'
         from /opt/local/lib/ruby/1.8/benchmark.rb:207:in `bm'
         from a.rb:59

cheers.

-a

--
Chris Carter
concentrationstudios.com
brynmawrcs.com

That version still has an additional space in the html stripping
regexp. I think it was inserted by google groups when I pasted that
message (because it displays a line break here). Anyway here is a
version without any additional characters.
  http://pastie.caboo.se/76886
To avoid this cracking method one could change at least one of the
characters of each letter. This would break simple regexp attacks. But
I still think this wouldn't be too difficult to break. But even if
there is a simple way to crack a captcha it will stop most of the
spambots so I'm not saying this is something useless.

···

On 7 Jul., 19:52, "ara.t.howard" <ara.t.how...@gmail.com> wrote:

my version doesn't seem to have line breaks - here it is:

   http://drawohara.tumblr.com/post/5164285

--
Jannis Harder

i wouldn't say that :wink:

did i understand you correctly? i wasn't sure i did...

cheers.

-a

···

On Jul 7, 2007, at 11:42 AM, Chris Carter wrote:

Huh, I guess I am blind then...

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

cool. i've updated here

   http://drawohara.tumblr.com/post/5164285

but it's still failing (yay!)

note the new output - sample at bottom of above post. it's much harder.

fun stuff!

-a

···

On Jul 7, 2007, at 12:31 PM, jannis@harderweb.de wrote:

That version still has an additional space in the html stripping
regexp. I think it was inserted by google groups when I pasted that
message (because it displays a line break here). Anyway here is a
version without any additional characters.
  http://pastie.caboo.se/76886
To avoid this cracking method one could change at least one of the
characters of each letter. This would break simple regexp attacks. But
I still think this wouldn't be too difficult to break. But even if
there is a simple way to crack a captcha it will stop most of the
spambots so I'm not saying this is something useless.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

Testing with an updated flatulent version is cheating :wink: .... but I
love challenge, so here is an updated version successfully breaking
flatulent 0.0.4. 9 times faster than generating.
http://pastie.caboo.se/76923
This version should be able to break 0.0.3 and 0.0.4.

···

On 7 Jul., 20:43, "ara.t.howard" <ara.t.how...@gmail.com> wrote:

cool. i've updated here

   http://drawohara.tumblr.com/post/5164285

but it's still failing (yay!)

note the new output - sample at bottom of above post. it's much harder.

--
Jannis Harder

Testing with an updated flatulent version is cheating :wink: .... but I
love challenge,

have a whack at this then

  http://drawohara.tumblr.com/post/5126190

:wink:

so here is an updated version successfully breaking
flatulent 0.0.4. 9 times faster than generating.
http://pastie.caboo.se/76923
This version should be able to break 0.0.3 and 0.0.4.

awesome. i'll post it on my blog later. guess i'll have to make an image after all ;-(

-a

···

On Jul 7, 2007, at 3:28 PM, jannis@harderweb.de wrote:
--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

I think if you can kern the letters into each other, finding some way
to make them overlap, much of this approach (regex) will be defeated.
Couple that with using more fonts will increase the difficulty of
solving the problem.

pth

···

On 7/7/07, ara.t.howard <ara.t.howard@gmail.com> wrote:

awesome. i'll post it on my blog later. guess i'll have to make an
image after all ;-(

i agree.
ara, i've seen your raptcha. how about something like that but in text
mode. The pixel would represent a character (that varies). it's like
converting from bmp to ascii art. Your text captcha must be able to
display on text browsers, otherwise it has no use for me like most
other captchas. yes, i'm usually a text browser :wink:

···

On 7/8/07, Patrick Hurley <phurley@gmail.com> wrote:

On 7/7/07, ara.t.howard <ara.t.howard@gmail.com> wrote:
> awesome. i'll post it on my blog later. guess i'll have to make an
> image after all ;-(

I think if you can kern the letters into each other, finding some way
to make them overlap, much of this approach (regex) will be defeated.
Couple that with using more fonts will increase the difficulty of
solving the problem.

i've played with quite a few things, but not overlapping the the letters. i'll give it a shot.

thanks!

-a

···

On Jul 8, 2007, at 8:07 AM, Patrick Hurley wrote:

I think if you can kern the letters into each other, finding some way
to make them overlap, much of this approach (regex) will be defeated.
Couple that with using more fonts will increase the difficulty of
solving the problem.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama