Recientemente me estaban llegando emails avisándome que se estaban insertando nuevos comentarios en los posts del blog. Esto no debería suponer ningún problema si no fuese porque se trataban de mensajes de spam. Así que como llevaba un tiempo sin tocar Ruby On Rails, como ejercicio de programación he implementado un sistema captcha para los comentarios. La idea es que la próxima revisión del programa saque mayor partido al potencial de ruby y de ror para que insertar este sistema dentro del blog, resulte más fácil y por decirlo de alguna manera, ‘menos intrusivo’ e ir mejorando algunas cosillas, como por ejemplo que no haya necesidad de generar el fondo cada vez que se crea la imagen o que el grid no se superponga al texto(lo estuve intentando de mil maneras pero nada…)
Aquí está la clase del captcha:
require "RMagick"
include Magick
class RCaptcha
attr_reader :value
@@chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
def initialize(text_length)
@rand_text = rand_text(text_length)
end
def render(width, height)
text = @rand_text.split(//).join(' ')
step = 15
image = Image.new(width, height)
draw = Draw.new
draw.stroke('#AAA')
#grid - vertical
w = 0
while (w < width)
draw.line(w, 0, w, height)
w = w + step
end
#grid - horizontal
h = 0
while (h < height)
draw.line(0, h, width, h)
h = h + step
end
#border
draw.stroke('black')
draw.line(1, 1, width, 1)
draw.line(width - 1, 1, width - 1, height -1)
draw.line(width - 1, height - 1, 1, height - 1)
draw.line(1, height - 1, 1, 1)
draw.draw(image)
draw.gravity = CenterGravity
draw.font_weight = 500
draw.font_style = NormalStyle
draw.font_stretch = AnyStretch
draw.text_antialias = true
draw.pointsize = 16
#draw.text(0, 0, @rand_text)
draw.annotate(image, 0, 0, 0, 0, text)
draw.draw(image)
image.format = 'png'
#image.display
return image.to_blob
end
def rand_text(length)
text = ""
for i in 0..length - 1
text << @@chars[rand(@@chars.size - 1)]
end
return text
end
def value=(new_value)
@value = new_value
end
def is_valid
return @rand_text == @value
end
end
en el controlador tendriamos que añadir un método que se encargase de generar un captcha nuevo y guardarlo en la sesión:
def render_captcha
if session[:captcha].nil?
session[:captcha] = RCaptcha.new(4)
end
captcha = session[:captcha]
data = captcha.render(80, 20)
send_data(data, :type => "image/png", :disposition => "inline")
end
y para mostrar la imagen bastaría con insertar una imagen que hiciese una llamada a este método y añadir un campo de texto donde se insertará el código generado para su posterior verificación:
<img src="/blog/render_captcha" alt="código de seguridad" id="captcha" />
<%= text_field("captcha", "value", :size => 5) %>
y ya por último, antes de insertar el comentario, comprobaremos en el controlador que el código enviado corresponde con el generado por el captcha y actuaremos en consecuencia:
def add_comment
...
captcha = session[:captcha]
captcha.value = params[:captcha][:value]
if captcha.is_valid
...
end
Está claro que no es el último grito en sistemas anti-spam, pero si sirve para frenar un poco a los ‘vende viagras’, pues bienvenido sea.