[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

[ANN] flatulent-0.0.3

ara.t.howard

7/5/2007 6:41:00 AM


(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.co...
http://rubyforge.org/projects/cod...

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/po...
http://drawohara.tumblr.com/po...
http://drawohara.tumblr.com/po...

ONLINE DEMO OF AJAX METHOD

http://fortytwo.merseine.nu:3000/flat... -- 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




21 Answers

Brad Phelan

7/5/2007 8:44:00 AM

0

ara.t.howard wrote:
>
> (the demo has been updated too)

Demo seems broken to me

http://fortytwo.merseine.nu:3000...

returns gibberish when viewed in firefox. However when I copy paste the
text I get
____
|___ __) | __ _ _____
__ __ |__ < ______ | / __ \ \ / / ___) | | ____| | |_ | |
\ \_/ / |____/ | | |__ _/ _| | | |_
\ / _|___ \ |_|__| |_
| | \_| ___)||) | \____/
|_| \|____/ | _ _ _
_/_ \_ \\ | /_
|_ _ \ _ | /
| \)| | | _


Wierd?????

_ _____ _
_______ _ | | __ \ _ ___
| ____| _ | |__) | |__ | |__ __ _ | | ___/ / | \ ) |
| __| | | | | | | /\ / /
| |____ | | _ | |_| | ( _ / /_
|______|\ _| | _| __ |____|
||) | | |____ | | |
__ |______|
| | __ |
_ _ / \ _
_ |_ //

I tried it a few times and got the same result.
Viewing in the web browser breaks but copy
and paste works well.

--
brad phelan
http://xt...

Patrick Hurley

7/5/2007 12:52:00 PM

0

On 7/5/07, ara.t.howard <ara.t.howard@gmail.com> wrote:
> ONLINE DEMO OF AJAX METHOD
>
> http://fortytwo.merseine.nu:3000/flat... -- try to break it!

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

James Gray

7/5/2007 1:14:00 PM

0

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/flat... -- try to
>> break it!
>
> I can only guess 1 in 5 of the "images" -- that is one way to cut down
> on spammers :-)

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

http://grayproductions.net/ruby/images/scary_c...

Yikes!

James Edward Gray II

Lionel Bouton

7/5/2007 1:17:00 PM

0

Patrick Hurley wrote the following on 05.07.2007 14:51 :
> On 7/5/07, ara.t.howard <ara.t.howard@gmail.com> wrote:
>> ONLINE DEMO OF AJAX METHOD
>>
>> http://fortytwo.merseine.nu:3000/flat... -- try to break it!
>
> I can only guess 1 in 5 of the "images" -- that is one way to cut down
> on spammers :-)
> 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

ara.t.howard

7/5/2007 3:06:00 PM

0


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 :-)
> pth


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

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




Jannis Harder

7/6/2007 6:29:00 PM

0

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='.*?'>(.*?)<\/
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

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


ara.t.howard

7/7/2007 5:18:00 PM

0


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! ;-)

-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

7/7/2007 5:26:00 PM

0

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! ;-)
>
> -a
> --
> 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.


--
Chris Carter
concentrationstudios.com
brynmawrcs.com

ara.t.howard

7/7/2007 5:35:00 PM

0


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
--
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama




Jannis Harder

7/7/2007 5:41:00 PM

0



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