nunojob:~ dscape/08$ echo The Black Sheep

Posts tagged ‘Ruby on Rails’

Open Source Online Testing System (OSOTS)

I simply can’t believe that I forgot to post this project in my blog. Anyway if you have to deploy a multiple choice test and you feel like messing arround with Ruby on Rails and DB2 here goes the link.

Open Source Online Testing System (OSOTS)

Advertisements

Information Retrieval using the Boolean Model in Ruby on Rails

DB2 Rocks

qs = Question.find_by_sql 
"select X.* from ots_schema.questions," +
  "XMLTABLE (\'$d/question\' passing document as \"d\" " + 
    "COLUMNS question_text VARCHAR(200)" + 
    "PATH \'question_text\') as X"
qs.first.question_text.lstrip
=> "Which of the following is the correct syntax to set the DB2COMM variable to TCPIP?\n  "

If DB2 was had a good DB2 driver and a ActivePureXML (or something adapter) it would so f*ckin rock. Just look at the sample. And the dynamic nature of ruby would enable the flexibility of xml documents.

Please IBM please. DB2 for mac and decent support on Ruby. Don’t make me write things like this no more:

# Once again fixing IBM_DB bugs the ugly way
# with_scope anyone?
add_index :'ots_schema.users', :login

or

t.column :document, :xml

CouchDB & Um Novo Semestre!

Bem todos os semestres faço um plano de acção do que vou fazer para além de o que é espectável de mim – ir as aulas, fazer os trabalhos, passar nos exames e ler pelo menos um livro de informática – de escolha livre – por mês.

Acredito na valorização pessoal e gosto muito de aprender coisas novas. E acredito que não existe melhor forma de o fazer que com projectos práticos!

Algumas das novidades do último semestre foram ter entrado no centro de apoio ao open-source do departamento de informática, ser eleito IBM DB2 Student Ambassador e ter entrado na melhor rede de bloggers de informática de portugal – prt.sc. Como projecto pessoal escolhi aprender – por sugestão do Ulisses CostaRuby & Ruby on Rails.

Dito isto resta uma dúvida: O que aprender este semestre?

Tenho que confessar que ando nas nuvens por terem aceite a minha colaboração no projecto CouchDB do rubyforge.

Como devem saber CouchDB é uma base de dados criada pelo Damien Katz (que trabalha agora na IBM), não relacional, desenvolvida em erlang que tem como objectivo guardar documentos e é RESTful. Os conceitos são excelentes e a forma de abordar o fault-tolerance e problemas de carga são daqueles conceitos que nos deixam a babar mortinhos por experimentar o brinquedo novo. Mas não se fica por aqui. Aconselho a lerem a wiki para terem uma noção do que é – e o que não é – o CouchDB. Só não gosto muito do facto de estar tão ligado a javascript e JSON – o primeiro porque nunca gostei muito de javascript e o segundo porque preferia yaml. Mas vá antes JSON que XML!

Outros tópicos que andam por aqui a passear – sendo que aqui é a minha cabeça – é a vontade de aprender um mínimo de Erlang e continuar o estudo de DB2 para tirar a certificação. Espero que quando acabar este semestre possa olhar para trás e sentir a mesma satisfação que sinto pelo semestre que já passou.

METS Standard with IBM DB2 Express C using XForms as user-interface and Ruby on Rails as a Rest Web-services

HIGH QUALITY VERSION DOWNLOAD HERE

Well, here is the long promised screen-cast. The amount of topics covered is simply huge. To get you ready for the screen-cast I prepared some other more introductory screen-cast as well as some articles on these subjects. I’m sorry that I don’t have the time to document the REST, but I really advice to invest some time learning it as it’s a very pragmatic way of delivering high quality web services.

I strongly advice you to download this screen-cast from rapidshare as both Youtube and Google Videos quality is really awful. You can download it from here.

This work was really fun to do. So I hope to have the opportunity to develop it further and manage nested rest routes like /mets/1/agents to return the agents of the first submission information package (sip) using some cool DB2 pureXML features. I really feel that with a good plugin to help users take full advantage of DB2 pureXML features and a little of imagination this web-service could be of some use.

I also expect to complete the xforms model as it is not indexing a fileptr to each category when such is selected. I hope to implement this soon enough.

Here are the associated resources I developed:

And here are the other two screencasts I produced to introduce you to XForms and METS:

I also advice you to take a look at this articles. All of them where very helpful to my work.

XmlSimple?

>> xml_plain = %q(<p>Just a <strong>p</strong> on bold</p>)
=> "<p>Just a <strong>p</strong> on bold</p>"
>> xml_hash = XmlSimple.xml_in xml_plain,
  'ForceArray' => true
=> {"strong"=>["p"], "content"=>["Just a ", " on bold"]}
>> xml_plain2 = XmlSimple.xml_out xml_hash, 
  'RootName' => 'p'
=> "<p>\n  <strong>p</strong>\n  <content>Just a </content>\n  <content> on bold</content>\n</p>\n"

Não querendo ser malvado com quem fez esta biblioteca acho que o mínimo expectável para uma operação de marshall/unmarshall é que seja reflexiva. Lá vou eu ler documentação para descobrir como se ensina o XMLSimple – nome curioso não é? – a ser o que nunca deveria deixar de ser. Reflexivo.

Já agora se alguém já programou web services em rails e sabe como transformar os params em xml de uma forma melhor, let me know!

Devolvam as passwords aos vossos utilizadores com Ruby on Rails 2.0

Uma coisa que me chateia imenso nas webapplications de trazer por casa – traduzindo aquelas que são feitas por colegas meus ou por outros amadores pela web fora – é o facto de guardarem quase sempre as passwords na base de dados em plaintext. Acho que não tem o direito de saber a password que uso, nem que a use exclusivamente nesse site (como é o caso)

Assim, se quem fez a aplicação for mal intencionado, pode simplesmente obter a vossa password escrevendo

select * from users where user.username = 'nick'

Mas mesmo que não sejam mal intencionados, outra pessoa pode aceder a base de dados. Ou podem simplesmente não se ter protegido contra sql injection. Há um milhão de razões pelas quais nunca se deve guardar as password em plaintext, sendo que a principal deles é o facto da password não ser vossa. É do utilizador. E isso faz toda a diferença! Não acreditam em mim certo? Estou a exagerar.

Convido-os a visitar esta página

Agora procurem por ‘qualquer_coisa. O resultado é

  • Pesquisa por “\’qualquer_coisa”:

Este \’ é importantíssimo pois significa que a página se esta a proteger contra ataques de sql injection. Caso contrario um hacker podia, com alguma sorte, obter informação privilegiada que estava contida nessa base de dados.

Mas ninguém faz um erro destes, certo? Errado. Vamos ao google e procurem php em páginas de Portugal. O sexto resultado, e único que testei, é a associação nacional de farmácias. Se procurarem por ‘qualquer_coisa o resultado é um não tão surpreendente erro. Isto é um exemplo de milhares, alguns dos quais onde vocês inserem as vossa password (e sabe-se lá o que mais) diariamente. Não se sentiam melhor se soubessem que nessa base de dados não está a vossa password?

Mas voltando ao assunto, a táctica que vou usar consiste em usar um algoritmo de one way hash chamado SHA. O utilizador, quando se regista no site, insere a sua password. Esta é processada (num processo que se chama de digest) para uma hash que contém não a password mas sim a encriptação dessa password em SHA. Como SHA é um algoritmo de one-way-hash, é fácil obter a hash única para qualquer palavra mas quase impossível de decifrar no sentido inverso. Para ser suficiente, mas não é.

Comecemos por ir para a consola do rails. Entrem no terminal e naveguem até onde tem a vossa Ruby on Rails web application. Agora escrevam.

./script/console

Quando lá estiverem podem verificar que aplicando este algoritmo a palavra porto obtemos sempre a mesma resposta.

>> password = 'porto'
=> "porto"
>> Digest::SHA256.hexdigest(password)
=> "01735c5ff1608734e4c38449a00b74bb9a8d5423ed548238da70178e9e803483"
>> Digest::SHA256.hexdigest(password)
=> "01735c5ff1608734e4c38449a00b74bb9a8d5423ed548238da70178e9e803483"

Não sei se repararam mas o nosso utilizador usa uma palavra pass muito fraca. Isto vai fazer a diferença, por o hacker pode obter a password facilmente se tiver a hash.

Como? Sim eu disse que era muito difícil crackar o SHA, alias não conheço ninguém que o tenha feito. Ninguém conhece :P

Mas o hacker pode pegar numa lista de palavras comuns nas passwords e, para cada uma das palavras associar a hash correspondente. Agora se tiver a vossa hash pode comparar com o dicionário que criou descobrir algumas das passwords da base de dados, aquelas que estavam na lista de palavras comuns.

Para resolver este problema usamos um salt. Um salt é também uma palavra (como a vossa password) que sera usada para evitar esta técnica. é mais simples mostrar que explicar, e por isso mesmo:

>> password = 'porto'
salt = [Array.new(6){rand(256).chr}.join].pack("m").chomp
>> salt = [Array.new(6){rand(256).chr}.join].pack("m").chomp
=> "19L6gVcD"
>> salt = [Array.new(6){rand(256).chr}.join].pack("m").chomp
=> "CqEVe632"
>> password = 'porto'
=> "porto"
>> Digest::SHA256.hexdigest(password + salt)
=> "c017e1c49096149097050c76f0406dce9245058e0d..."

De certeza que o hacker não vai procurar pela string portoCqEVe632 e, portanto, os nossos utilizadores estão seguros. Caso queiram saber um pouco mais sobre o que se passa no criação do salt, e que é aquele pack(‘m’) peçam nos comentários e eu escrevo um artigo sobre isso. Senão nunca mais saio daqui! :P.

Agora surge a pergunta, como usar esta informação em rails. Simples.

Vamos começar por criar um projecto:

rails secureusers && cd secureusers

Agora precisamos de criar o modelo de utilizador

./script/generate model User username:string \
  password_hash:string password_salt:string
rake db:migrate

Abram o vosso User.rb model, apaguem o que lá tem, e insiram o seguinte código

require 'digest/sha2'
class User < ActiveRecord::Base
# usei o attr_accessible em vez do
# attr_protected :password_hash, :password_salt
# porque é boa prática em Rails usar o enabled para 
# os campos acessíveis e não o contrário.
#
# Imaginem que tinham um campo isAdmin e se 
# esqueciam de por no protected.
# Ao dizerem que enabled só o :username impedem 
# o acesso a todo e qualquer outro campo,
# excepto aqueles que foram explicitamente 
# escolhidos por vocês.
  attr_accessible :username
  attr_accessor :password

  def before_save
    unless password.blank?
      salt = &#91;Array.new(6){rand(256).chr}.join&#93;.pack("m").chomp
      self.password_salt, self.password_hash = salt,
      Digest::SHA256.hexdigest(password + salt)
    end
  end

  def self.authenticate(username, password)
    user = User.find :first,
        :conditions => ['username = ?', username])
      if user.blank?
        raise "O utilizador não existe"
      elsif Digest::SHA256.hexdigest(password + user.password_salt) != user.password_hash
        raise "A autenticação falhou"
      else
        user
      end
  end
end

Usei o attr_accessible em vez do protected porque considero esta uma boa practica. Prefiro dizer quais os campos acessíveis que quais os que se deve proteger. Assim não corro o risco de me esquecer de proteger algum campo. E os que são para ver nota-se bem quando faltam :P Podem ler mais sobre isto aqui.

Depois de toda esta explicação o código deve ser muito facil de entender. Até para quem não sabe Ruby. Vamos testar?

>> nuno = User.new
=> #<user id: nil,
  username: nil,
  password_hash: nil,
  password_salt: nil,
  created_at: nil,
  updated_at: nil>
>> nuno.username = 'nuno'
=> "nuno"
>> nuno.password = 'porto'
=> "porto"
>> nuno.save
=> true
>> nuno
=> #</user><user id: 1, 
  username: "nuno",
  password_hash: "c490ea11d96be24ece2ca0dba11d84fc9b...",
  password_salt: "V+b7Sqjh",
  created_at: "2008-02-12 19:28:54",
  updated_at: "2008-02-12 19:28:54">

E agora autenticar:

User.authenticate 'nuno', 'porto'
=> #</user><user id: 1, 
  username: "nuno",
  password_hash: "c490ea11d96be24ece2ca0dba11d84fc9b...",
  password_salt: "V+b7Sqjh",
  created_at: "2008-02-12 19:28:54",
  updated_at: "2008-02-12 19:28:54">

Fixe. É so por no session[:user] ;) Para finalizar aconselho-vos vivamente a suportarem open-id para o processo de autenticação dos utilizadores. Existem plugins muito bons que o fazem, e podem ler mais sobre open-id no seu fantástico guia supersónico.