GitHub hackeado, Homakov, Rails e o que isso tem a ver com CakePHP

Olá! Dia primeiro de março de 2012, um carinha conhecido como Homakov abriu uma issue no projeto do Rails no GitHub. Ele a abriu porque, segundo ele, muitos não conheciam ou não praticavam uma prática de segurança que protegia certas colunas do banco de dados e eram expostas nos models do framework.

Infelizmente só consegui parar para escrever este post 10 dias depois. Mas acredito que vá valer a pena. Além de explicar o caso, também abordei como se proteger disto ao utilizar CakePHP. Por fim, selecionei algumas imagens de memes que surgiram depois deste caso para nos alegrar um pouco =).

O que aconteceu?

Dois dias depois de criar a issue, o Homakov, não satisfeito com a atenção que deram a ele, decidiu brincar e mostrar quão perigoso era tudo isso. Ele, então, commitou direto no master do repositório do rails. Ele utilizou o problema descrito na issue para adicionar sua chave no projeto e então commitar. Ele inclusive descreve no seu blog como fez isto.

Ele não fez nenhum estrago, apenas adicionou um arquivo chamado hacked contendo apenas:

another showcase of rails apps vunlerability.
Github pwned. again :(
will you pay me for security audit?

Sim, o typo vunlerability é dele, não meu. Tanto é que surgiram comentários do tipo: "Can hack but can he spell?". Discussões e mais discussões vieram depois sobre as atitudes do menino. Uma dia depois de commitar no master do repositório do rails, o github postou no seu blog que a falha havia sido corrigida.

O Homakov foi banido e parece que foi desbanido depois. Talvez seja verdade quando dizem que o amor prevalece sempre. Sim, este é Homakov. Ele tem uma tatuagem com o logotipo do GitHub.

E onde entra o CakePHP?

Os métodos attr_accessible e attr_protected do rails permite e restringe, respectivamente, que certas propriedades sejam salvas por mass assignments. Mass assignment, no contexto do rails, pode ser exemplificado por:

class PublicKeyController < ApplicationController
  before_filter :authorize_user
  # ...
  def update
    @current_key = PublicKey.find_by_id params[:key]['id']
    @current_key.update_attributes(params[:key])
  end
end

que no caso do CakePHP, poderia ser traduzido para:

class PublicKeyController extends AppController {
  public function beforeFilter() {
    parent::beforeFilter();
    $this->authorizeUser();
  }
  public function update($id) {
    $data = $this->data;
    $data['PublicKey']['id'] = $id;
    $this->PublicKey->saveAll($this->data);
  }
}

Vejam que o uso do saveAll passando direto o conteúdo enviado via POST é uma prática bem comum, mesmo em linguagens diferentes. O próprio manual do CakePHP utiliza códigos desta forma, veja você mesmo.

O próprio autor comentou lá:

But only rails apps got this kind of bug. Yeah, phpists never got so fast prototyping tools so most of them cannot even dream about update_attributes {} and form generation too.

Bom, há 2 erros ali no que ele disse: não são somente aplicações rails que têm este bug e os referenciados update_attributes e form generation estão presentes no CakePHP, que inclusive, foi inspirado no rails. Talvez seja só ironia, mas este problema também está presente no cake.

Infelizmente não há uma maneira tão simples no CakePHP de se defender disto, quanto há no rails. Para se proteger no rails você pode conferir o gist do petenixey falando sobre como o Homakov hackeou o GitHub.

Para se proteger em CakePHP, você pode usar duas formas: a feia ou a chata.

Vamos primeiro para a feia. Vejam que o problema foi a sobrescrita do user_id, que permitia, então, adicionar novas chaves para qualquer usuário. A feia consistem em apagar todos os campos que não podem ser mass assigned, simulando o attr_protected.

public function update($id) {
   $data = $this->data['PublicKey'];
   $data['id'] = $id;
   unset($data['user_id']);
   $this->PublicKey->saveAll($data);
}

Já a chata, seria ao contrário, dizendo quais campos podem ser atualizados:

public function update() {
  $data = $this->data['PublicKey'];
  $data['id'] = $id;
  $this->PublicKey->saveAll(
    $data, 
    array('fieldList' => array('title', 'key'))
  );
}

Na chata, nós simulamos o comportamento do attr_accessible, onde é dito apenas os campos do banco de dados que devem ser atualizados. Digo que ela é chata, porque pode ser massante ter que especificar uma whitelist muito grande. Aí nestes casos, de repente é mais simples utilizar a feia, onde apenas se é especificado uma blacklist.

Conclusões

Frameworks facilitam o trabalho do desenvolvedor, mas ainda, sim, um bom desenvolvedor deve mostrar que está no comando e que domina o framework de trabalho. Na minha opinião, este incidente mostrou como é importante um programador ser poliglota e conhecer todas as camadas em que trabalha, desde o TCP/IP, passando pelo HTTP e também o core/funcionamento do seu framework de trabalho.

Memes, para amenizar

Para finalizar, gostaria de por algumas imagens de memes que surgiram na thread do Homakov. Quem não gostou do post, pode pelo menos dar umas risadas =).

I dont always push my code, but when I do, I do it into origins Hacked github? Made a bunny cry I dont always hack, but when I do I make sure I have my victims tatoo on my arm I found an exploit using attr_accessible, better commit to rails master Build it with rails, they said. Your data will be afe, they said

Quem gostou do post pode dar um +1 ou comentar abaixo: Você concorda com o Homakov que isto é problema do framework ou é problema do desenvolvedor?

2 comentários

Parabéns pelo blog e muito

Yuri Wayne Ferreira (não verificado)

Parabéns pelo blog e muito obrigado pela dica de correção. Isso mostra claramente como devemos dar atenção para as modificações e correções que são encontradas nos frameworks que usamos, para não sermos pegos desprecavidos. Muitas vezes é um desafio mantermos por anos um projeto com a saúde técnica necessária.

Respondendo a ultima

Thiago F Macedo (não verificado)

Respondendo a ultima pergunta: com certeza bug do framework, afinal não tratar dados do post é coisa feia tem tempo (oxi maria).
A sua opção chata certamente é a mais segura. Aliás, é assim que o Yii trabalha (eu prefiro).

Bom blog. Abraços;

Tema por Kiwi Themes.