Elmord's Magic Valley

Computers, languages, and computer languages. Às vezes em Português, sometimes in English.

NSA operation ORCHESTRA, e alguns pensamentos sobre o estado atual da computação

2014-04-17 01:29 -0300. Tags: comp, prog, security, politics, ramble, em-portugues

No FOSDEM (Free and Open Source Developers' European Meeting) deste ano, Poul-Henning Kamp deu uma palestra muito interessante intitulada "NSA operation ORCHESTRA - Annual Status Report" (video, slides). A palestra, apresentada na forma de um report de um programa fictício da NSA, explora a idéia de o que a NSA poderia estar fazendo para coletar o máximo de informação com o menor custo possível. Possibilidades incluem:

Como diz nos slides da palestra, "A intenção foi fazer as pessoas rirem e pensarem, mas eu desafio qualquer um a provar que não é verdade."

A única coisa que não me agrada nessa palestra é a conclusão de que "this is a political problem" e de que é inútil fazer qualquer coisa do ponto de vista técnico. As ações da NSA são um problema político, mas: (1) isso não quer dizer que não possamos ou devamos buscar eliminar fontes de vulnerabilidades no software existente; (2) esse tipo de sabotagem poderia vir igualmente de uma organização privada com recursos suficientes, então o problema não é puramente político, no sentido de que mesmo nos livrando de governos maliciosos, o problema não desapareceu.

Ação política é importante, mas como desenvolvedores de software podemos tomar atitudes para mitigar o efeito de ações maliciosas sobre software. Para começar, podemos parar de usar ferramentas da idade da pedra, que facilitam a introdução (acidental ou deliberada) de falhas de segurança, como C/C++. O potencial de insegurança no C/C++ não é o mero descuido na hora de calcular os índices e o tamanho de um vetor (que por si só já é uma eterna fonte de vulnerabilidades). Em C/C++ existe o conceito de comportamento indefinido (undefined behavior), e cada versão nova do GCC/Clang/[insira seu compilador C/C++ favorito aqui] sai "melhor" em explorar comportamento indefinido para fins de otimização do que a anterior. A idéia básica é que se um programa realiza certas ações "proibidas" pelo standard da linguagem (e.g., acessar um elemento além do final de um vetor), o comportamento resultante do programa não é especificado pelo standard, então o compilador é livre para gerar um programa que faz qualquer coisa nesses casos. Por exemplo, suponha que você escreve algo como:

void foo(struct pessoa_t *pessoa) {
    int idade = pessoa->idade;

    if (pessoa == NULL)
        printf("Oops, pessoa inválida!\n");
    else
        printf("A idade da pessoa é %d.\n", idade);
}

pessoa é um ponteiro (potencialmente nulo) para uma estrutura de dados. Acessar o conteúdo apontado por um ponteiro nulo é um comportamento indefinido: um programa que faz isso é um programa incorreto. Logo, o compilador é livre para gerar código que não funciona no caso de o ponteiro ser nulo. Logo, o compilador pode assumir que pessoa não é um ponteiro nulo: se a hipótese do compilador for verdade, o programa estará correto, e se não for, tanto faz se o programa está correto. Mas se o ponteiro não é nulo (por hipótese), então o if na função é redundante (pois a condição é sempre falsa): o compilador pode descartar as linhas cor-de-burro-quando-foge do código resultante, como uma otimização. Foi exatamente uma otimização desse tipo que transformou um erro de programação no kernel do Linux em uma falha de segurança alguns anos atrás. Outras situações em que o compilador pode conspirar contra o programador incluem: remover verificações de overflow em operações aritméticas, pois signed overflow é indefinido em C/C++; reordenar acessos à memória, ignorando que outras threads podem depender do acesso em uma certa seqüência, se o programador não tomar o cuidado de forçar a ordem das operações; e inúmeras outras situações. Dado o grau de exploitação de comportamento indefinido nos compiladores C/C++ modernos, seja por avanços tecnológicos em análise estática, seja por influência da NSA/agentes soviéticos/illuminati/maçonaria, eu me sinto fortemente propenso a encarar o compilador C/C++ como um agente malicioso, e a idéia de minimizar o uso dessas linguagens parece cada vez mais appealing.

Outra medida técnica para reduzir a propensão dos sistemas computacionais a falhas de segurança é adotar modelos de segurança baseados em capabilities, em que o default é os processos não terem acesso a nada que não lhes seja explicitamente concedido, ao contrário dos modelos baseados em usuários, como o do Unix, em que é difícil ter certeza absoluta de quais são os poderes que um processo tem, e a grande maioria dos processos roda com mais permissões do que precisa (e.g., seu browser tem o poder de ler todos os seus arquivos pessoais, o tempo inteiro).

Falar é mais fácil do que fazer. Hoje em dia há uma falta de soluções práticas que nos permitam livrarmo-nos do C/C++ sem perder performance ou outras conveniências, ou sistemas baseados em capabilities que não sejam sistemas acadêmicos cuja adoção no mundo real é inviável. Estes são problemas nos quais eu pretendo trabalhar durante a minha existência neste mundo [aquela história de crowdfunding era só parcialmente brincadeira :)]; mais sobre isso em um post futuro, talvez. O ponto é que definitivamente há (muito) o que fazer do ponto de vista técnico para mitigar os efeitos de ações da NSA e outros agentes maliciosos.

Comentários / Comments (5)

Bug, 2014-04-17 08:52:09 -0300 #

Eu gosto desse tipo de discussão, e provavelmente a gente vai ter que sentar e discutir isso no gtalk quando tivermos tempo :P Mas vamos lá:

a) Não acho que C/C++ seja o problema, mas entendo que comportamento indefinido ou simplesmente o compilador ser fdp pode ser um problema (e sério). E quem disse que o compilador não insere uma falha de segurança? Em uma palestra (fraca, inclusive) que vi esses tempos sobre trojans em hardware, o apresentador comentou sobre tu poder simplesmente injetar um trojan em hardware (ou uma falha de segurança qualquer/backdoor) simplesmente injetando código na hora de sintetizar o teu circuito (que, se não estou errando a terminologia, significa que tu vai converter teu HDL para uma Netlist - gates, basicamente). E parece que isso já foi feito em algumas pesquisas usando FPGAs. Ou seja, é viável.

b) Bloquear o que um aplicativo pode ou não fazer é o modelo usado no Android, não? Isso é sandboxing, e eu não vejo isso como algo bom. Sim, eu entendo perfeitamente o lado da segurança e concordo que ajuda, mas tu tem 3 problemas diretos: 1) o single point of failure causado pelo fato de que teu sandboxing precisa ser extremamente bem feito, 2) overhead do capeta, e 3) retrocompatibilidade. Vai dizer que o flash não pode acessar o hardware pra ver o que acontece :P Algo menos bloqueante é o que a gente faz com o user nobody no Linux, que é basicamente baixar as permissões do processo pra um usuário normal. Still, como todo mundo sabe, isso não é suficiente (at all), já que tu pode muitas vezes usar o nobody pra pegar root de inúmeras formas (como exploit direto do kernel, por exemplo).

c) Inserção de falhas deliberadas para monitoramento é uma das coisas mais absurdas que eu já vi por aí. Além de ser totalmente viável e ser com certeza realizado hoje em dia, é uma puta falta de respeito com os usuários. Essa do OpenSSL foi ridícula, e reza a lenda que a NSA explorava essa merda há tempos. Tipo, tu não pode confiar mais em software algum, exceto no teu (e olhe lá, porque se ele roda em cima do Linux e usa OpenSSL tu já tá fudido :P). Complicado. O que fazer?

d) Tu não tinha te formado? Como tu tá com esse .inf ae? PKOAEOPKAEAPKOEKOAPE


[]s


Vítor De Araújo, 2014-04-17 20:45:11 -0300 #

Apareça no Gtalk quando quiser. Eu deixo meu status quase sempre em "away", então não leve ele muito a sério. :P

a) C/C++ definitivamente é um problema. :P No sentido de que o default é ser inseguro, e tu tem que fazer um esforço (muito sujeito a descuidos) para tornar um programa seguro. O ideal seria o contrário: seguro por default, e no caso de performance ser estritamente necessária em um trecho do programa, informar ao compilador que checks podem ser desativados naquele trecho.

b) Pelo que eu entendo, o modelo do Android é o mesmo do Unix, só que cada aplicativo roda com um usuário diferente. Já é um avanço, mas tem inconveniências (e.g., se um app vai acessar o sdcard, então ele tem o direito de acessar _tudo_ no sdcard). Capabilities não é bem sandboxing. Acho que eu vou escrever um post sobre isso nos próximos dias, mas imagina que tu tem funções que não têm acesso a variáveis globais: cada função só tem acesso ao que recebe como argumento. Substitua "função" por processo e "argumento" por capability, e é essencialmente isso. Um vez adquirida a capability (que pode ser para um arquivo, um filesystem, um recurso de hardware, etc), o processo pode manipular o recurso sem necessariamente ter overhead. Bom, eu vou escrever um post sobre isso (acho). :P

c) O que fazer é tornar difícil inserir falhas que sejam difíceis de detectar. Easier said than done, mas né. :P

d) Arranjei um mestrado, né. Preciso continuar tendo hospedagem de graça pro blog. :P


Marcus Aurelius, 2014-04-18 03:07:42 -0300 #

O Poul-Henning Kamp é o mesmo cara que chamou o comitê da linguagem C de “pile of morons”, a linguagem até que poderia ser consertada, mas realmente só fazem o mínimo necessário para mantê-la... E os implementadores (de diversos compiladores, tanto open- quanto closed-source) parece que também não dão muita atenção ao C, já que os recursos novíssimos e complicadíssimos do C++ chegam relativamente rápido (oooh, shiny), enquanto que uma faxina geral no C, que talvez nem custasse tanto em termos de implementação demora mais ou nunca acontece.

Sei lá, pra mim é tão óbvio que código “precavido” (bounds-checking et al.) vale mais a pena e “se paga” em pouco tempo: bugs são revelados no momento em que acontecem (e não tempos depois quando o corrompimento da memória se torna crítico), o desenvolvimento se torna mais rápido e os milissegundos perdidos compensam os minutos/horas/dias de caça aos bugs.

E mesmo assim tem gente que gosta de andar de uniciclo com propulsores a jato* sem capacete para mostrar sua “bravura”...

* Uniciclo com propulsores a jato = linguagem C
* Uniciclo com propulsores a jato, teletransporte, comunicador intergaláctico, pia da cozinha, com freios ABS e airbags opcionais = linguagem C++


Marcus Aurelius, 2014-04-18 03:10:09 -0300 #

Aliás, a reimplementação do universo vai ser em qual linguagem? :-)


Vítor De Araújo, 2014-04-18 18:59:29 -0300 #

As respostas para esta e outras perguntas serão reveladas nos próximos capítulos de "Elmord's Magic Valley: Advancing the state-of-the-art to what it was in the 1980s". :P


Deixe um comentário / Leave a comment

Main menu

Recent posts

Recent comments

Tags

em-portugues (213) comp (148) prog (71) in-english (62) life (49) unix (38) pldesign (37) lang (32) random (28) about (28) mind (26) lisp (25) fenius (22) mundane (22) web (20) ramble (18) img (13) rant (12) hel (12) scheme (10) privacy (10) freedom (8) esperanto (7) music (7) lash (7) bash (7) academia (7) copyright (7) home (6) mestrado (6) shell (6) android (5) conlang (5) misc (5) emacs (5) latex (4) editor (4) etymology (4) php (4) worldly (4) book (4) politics (4) network (3) c (3) tour-de-scheme (3) security (3) kbd (3) film (3) wrong (3) cook (2) treta (2) poem (2) physics (2) x11 (2) audio (2) comic (2) lows (2) llvm (2) wm (2) philosophy (2) perl (1) wayland (1) ai (1) german (1) en-esperanto (1) golang (1) translation (1) kindle (1) pointless (1) old-chinese (1)

Elsewhere

Quod vide


Copyright © 2010-2024 Vítor De Araújo
O conteúdo deste blog, a menos que de outra forma especificado, pode ser utilizado segundo os termos da licença Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International.

Powered by Blognir.