Baixe o app para aproveitar ainda mais
Prévia do material em texto
FACULDADE ANHANGUERA – UNIDADE CAMPINAS I TECNOLOGIA EM ANÁLISE E DESENVOLVIMENTO DE SISTEMAS DESENVOLVIMENTO DE SOFTWARE SEGURO – ETAPA 02 ALUNO RA QUEMUEL SANTOS DE AQUINO 8309737322 PAULO VICTOR DE MENEZES 2848233367 SIDMAR PORFÍRIO 7989711257 LEONARDO CUENCAS 7700620660 ATIVIDADE PRÁTICA SUPERVISIONADA (ATPS) TUTOR: PROF. RICARDO AUGUSTO DA SILVA CAMPINAS, 27 DE MAIO DE 2015. SUMÁRIO INTRODUÇÃO .........................................................................................................................................3 CRIPTOGRAFIA NOS DIAS DE HOJE ......................................................................................................15 USANDO CRIPTOGRAFIA BASE64 NAS LINGUAGENS JAVA E ASP .........................................................18 RELATÓRIO 01 – UTILIZANDO CRIPTOGRAFIA .....................................................................................30 REFERÊNCIAS BIBLIOGRÁFICAS ............................................................................................................47 2 INTRODUÇÃO A necessidade de troca de informações entre os seres humanos, sem perigo de interceptação, existe desde os tempos de Roma antiga. Foi lá que surgiu o código de César, que consistia numa forma de embaralhar as letras de uma mensagem. Com o aparecimento dos computadores, novas formas de codificar a informação foram criadas, de modo que os usuários pudessem se comunicar, com proteção. Entretanto, essa tecnologia estava restrita a governos e organizações militares. O desejo de enviar mensagens de forma segura sem que ninguém mais, a não ser o destinatário, consiga ler, é tão antigo quanto a invenção da escrita. Abaixo estremos descrevendo de forma clara e objetiva informações sobre procedimentos de segurança usados em sistemas e em soluções web. INTRODUÇÃO À CRIPTOGRAFIA Em guerras primitivas tem-se relato de generais que raspavam a cabeça de um escravo e sobre a cabeça escreviam mensagens secretas de guerra. Após o cabelo crescer, o emissário era enviado (esta técnica de esconder mensagens é chamada de esteganografia e sua versão moderna consiste em esconder mensagens em imagens ou músicas). Basicamente existem duas formas primárias de obscurecer uma mensagem (criptografar), que pode ser pelo uso de substituição ou transposição. Na substituição troca-se alguma coisa por outra, de acordo com algum critério. Os passatempos encontrados em bancas de revistas exploram a substituição, onde o leitor é levado a descobrir que onde tem triângulo deve ser considerado a letra "A", por exemplo. Já na transposição apenas troca-se as coisas de lugar. Usando transposição simples, pode-se escrever a palavra "teste" como "ettse", transpondo uma letra com a sua vizinha. 3 O método matemático de substituição para inviabilizar a leitura mais antigo que se tem registro é a cifra de César. Elaborada por Júlio César, esta cifra inaugurou as chamadas cifras de substituição monoalfabéticas. Na infantil Cifra de César, cada letra do alfabeto é substituída pela sua terceira vizinha, ou seja, o "A" é transformado em "D", em um alfabeto circular, onde o "Z" vira "C" (...VWXYZABC...), conforme ilustrado na figura. Nota-se aí a existência de duas operações distintas: a) cifrar: para cifrar uma mensagem, deve-se substituir cada letra pela terceira vizinha subsequente. Em C usando string isto poderia ser feito com: strCIF[i] = strTXT[i] + 3; No entanto, um programador experiente verá erro na fórmula acima: ela só funciona até o "W" que somado com 3 virará "Z". Para as letras "X", "Y" e "Z" a fórmula é falha! Aí entraria em ação a operação de módulo, muito utilizada em criptografia (fica como dever de casa). b) decifrar: já para recuperar a mensagem outro método matemático precisa ser usado. Ao invés de trocar pela terceira subsequente, deve-se trocar pela terceira anterior ("D" deve virar "A"). Percebe-se nitidamente duas operações distintas para cifrar ou decifrar. Uma variação interessante da cifra de césar é a Rot13, onde se troca pela décima terceira vizinha. Ela é interessante pois como o alfabeto tem exatos 26 letras, o processo de cifrar e decifrar é o mesmo! Para cifrar basta achar a vizinha 13 subsequente e para decifrar também (a vizinha de "A" é "M" e a vizinha de "M" é o "A"). Ninguém irá cifrar nada sério usando César. Serve apenas como fundamento didático para explicar alguns conceitos, como o de chave, por exemplo. Atualmente a criptografia consiste em uma série de fórmulas matemáticas, em que se utiliza um segredo (chamado de chave) para cifrar e decifrar a informação. Este segredo pode ser o 4 mesmo para as duas operações (criptografia simétrica), ou pode haver segredos diferentes, um para cifrá-la e outro para decifrá-la, ou vice-versa (criptografia assimétrica). Por meio de um software especial e um par de chaves, um usuário pode então utilizar essa tecnologia, para proteger suas informações pessoais. O par de chaves possui as seguintes características: Chave pública - disponível para qualquer usuário que queira se comunicar de forma segura; Chave privada - chave secreta, que somente o dono do par de chaves conhece. Através de software e um par de chaves, um usuário pode: - Assinar digitalmente um documento (garantir que o documento não foi alterado após a assinatura e que o mesmo foi assinado pelo dono do par de chaves que foi usado); - Cifrar um documento, de modo que só o dono do par de chaves destino seja capaz de ler a informação cifrada; - Assinar e cifrar um documento. Infelizmente, o uso do software apenas garante que o dono do par de chaves realizou as operações, e não que o par de chaves em questão pertence realmente a uma pessoa. Não existe nenhum mecanismo que impeça a um usuário gerar um par de chaves com o nome de outro usuário. Para resolver esse problema, utilizam-se técnicas como certificação digital (versão eletrônica para os já conhecidos cartórios) e web-of-trust, que consiste em criar uma cadeia de confiança entre diferentes chaves. Este conceito será visto, adiante, com mais detalhes. - PGP e GnuPG O PGP surgiu inicialmente como um produto gratuito, disponível para diversas plataformas. Entretanto, a partir de 2004 o PGP deixou de ser gratuito, o que fez com que muitos usuários de tecnologia migrassem para outros produtos, como o GnuPG (gpg), que é uma versão software livre do PGP, disponível para diversas plataformas. O gpg e o pgp são compatíveis, de modo que, a partir de um dos programas, é possível cifrar, decifrar, assinar e verificar assinaturas entre eles. 5 - Teia de confiança (Web of Trust) Conforme foi dito, anteriormente, existe um problema em se certificar que uma determinada chave realmente pertence a um usuário, site ou empresa. Essa dificuldade não é relativa apenas ao PGP, mas a qualquer sistema que use criptografia. Uma das soluções mais comuns consiste na compra de um certificado de uma entidade certificadora, que funciona como um cartório digital, atestando que uma determinada chave realmente pertence a um usuário específico. Como no PGP, a idéia é disponibilizar criptografia para o público em geral, sem custos. Para isso, foi criado um método alternativo para se verificar se uma chave pertence a uma determinada pessoa, chamado web-of-trust. Neste método, a confiança vai sendo estabelecida através de uma rede de transitividade, onde se A confia em B e B confia em C, então A confia em C. Essa rede é construída por meio de uma relação pessoal entre dois indivíduos, constatação da identidade da chave, e assinatura da chave pública de um usuáriopelo outro. Essas etapas acabam por gerar um laço de confiança. Essas relações de confiança convertem- se, então, em uma rede de confiança, como pode ser visto abaixo. Após a verificação da identidade do par de chaves, o próximo passo é a publicação desta chave num servidor de chaves, de modo que qualquer um que queira se comunicar possa obter facilmente a chave pública deste usuário. Quando uma chave é publicada, as assinaturas das pessoas que confiaram nesta chave também são publicadas, de modo que basta ao usuário confiar em uma das assinaturas da chave para que passe a confiar na chave. - Servidor de Chaves Um servidor de chaves consiste em um software especial que possui uma interface via web, ou via e-mail, capaz de cadastrar, buscar e invalidar chaves públicas de usuários em qualquer parte do mundo. Estes servidores normalmente formam uma rede, de forma que, se um usuário publicar uma chave em um servidor na Espanha, por exemplo, esta atualização seja propagada para todos os servidores participantes da rede. Você provavelmente chegou neste texto, por causa do servidor de chaves do CAIS/RNP, que pode ser encontrado no endereço: http://www.rnp.br/keyserver. 6 O servidor do CAIS pertence à rede mundial, e, através dele, é possível consultar, adicionar e cancelar chaves na rede mundial de servidores. Para mais informações sobre o funcionamento específico do servidor de chaves, deve-se consultar a documentação incluída no endereço acima. Para outras informações a respeito de PGP e GPG, as referências podem ser consultadas na margem direita da página http://www.rnp.br/keyserver.php - Conceito de chave A Cifra de César não tem chave. Toda a "segurança" está na descoberta do método matemático. Se alguém descobrir que deve-se somar 3, acabou o algoritmo. Outro problema da Cifra de César é que se eu compartilho o método com 10 pessoas, todas as 10 estarão aptas a ler todas as mensagens. Mas se eu quisesse, usando esta cifra, trocar uma mensagem cifrada com a pessoa A, mas de forma que a pessoa B não possa ler, mesmo sabendo o método? Uma variação da Cifra de César consiste em trocar a letra pela vizinha k ao invés da 3. Se eu usar k como 3, terei que o "A" troca por "D". Se eu usar k=6, terei que o "A" troca por "G", e assim por diante. A ideia deste algoritmo é que todos conheçam o método (trocar por uma vizinha), mas ninguém sabe por qual vizinha é. Neste caso eu poderia combinar com a pessoa A que sempre que enviasse uma mensagem para ela eu usaria k como 3. Já com a pessoa B eu combino k=6. Todos conhecem o método, mas a pessoa B não saberia como ler mensagens que enviei a pessoa A pois ela não sabe que com A eu usei k=3. Eis o conceito de chave. O método é o mesmo, mas a chave (k) varia. Considerando que a pessoa B queira muito ler a mensagem que eu enviei para a pessoa A, tudo que ela precisa é descobrir qual valor de k eu usei para o A, já que o método é o mesmo. A ideia é que não exista segredo algum no método de criptografia (muito embora alguns fabricantes insistem em basear sua segurança nisto). 7 E como pode ser feita esta descoberta? Uma das forma é através da força bruta. - Ataque por força bruta Neste tipo de ataque testa-se todas as possibilidades de chave possíveis, até encontrar uma que sirva. Voltando ao infantil método de César com a modificação e presença do k, se B quisesse muito saber o que escrevi para A, ele poderia tentar usar os possíveis valores de k até que a mensagem faça sentido. Neste caso k pode assumir uma variedade limitada de valores. Ele pode ser 0 (onde "A" seria trocado pelo mesmo "A". Ridículo, mas matematicamente é uma chave viável), pode ser 1, 2, 3, ... 25, sendo que neste último o "A" seria trocado pelo "Z". Veja que existem apenas 26 possíveis valores para k neste método. Um ataque por força bruta não exigiria mais que 26 tentativas. Matematicamente se usa a estatística aqui, sendo que ninguém é tão sortudo para acertar na primeira tentativa e nem tão azarado para acertar na última! Na média se considera que o ataque terá sucesso em 13 tentativas. Logo, neste caso, um ataque por força bruta terá um esforço médio de 13 tentativas. Se eu usar papel e caneta (como devia ser no tempo de César) e conseguir testar no ritmo de uma tentativa por minuto, levaria 13 minutos em média para quebrar o algoritmo. Este seria o esforço de um ataque de força bruta à Cifra de César, o que o torna, como disse, infantil. Mas se eu mudasse a cifra monoalfabética um pouco. Se ao invés de ter que trocar uma letra por uma k vizinha eu usasse uma tabela de trocas: Letra A B C D E F G H I J K L M ... troca X J M A F W C N O K B E T ... Será que um ataque de força bruta seria possível com este novo algoritmo? 8 Um ataque de força bruta sempre é possível, a questão é quanto tempo em média ele levaria. O tempo é de acordo com as possíveis combinações de chave. Neste caso de um algoritmo monoalfabético com uma tabela de troca, cada letra pode ser uma das outras 25, sem poder repetir, ou seja, se já decidi que "A" troca pelo "X", ninguém mais poderá ser trocado pelo "X". A matemática nos ensina que neste caso haverão 26! possibilidades (fatorial de 26) para a chave. As aparências enganam, pois fatorial de 26 resulta em 403.291.461.126.605.635.584.000.000 possíveis valores para chave e isto é muito, mas muito mesmo. Como calcular este fatorial no Linux? Usando bc, uma alternativa bem criativa é com o comando: $ seq -s '*' 1 26|bc O comando seq irá gerar a sequência: 1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19*20*21*22*23*24*25*26 que será avaliada pelo bc. Se eu tivesse um computador capaz de realizar 1 bilhão de tentativas por segundo (1.000.000.000) e tiver 1 bilhão destes computadores para uso, o ataque de força bruta ainda levaria 201.645.730 segundos, ou aproximadamente 6 anos de processamento! Verifique você mesmo no bash: $ echo "403291461126605635584000000 / 2 / 1000000000 / 1000000000 / 60 / 60 / 24 / 365" | bc Divido o número de tentativas por 2, pois na média se acerta na metade das tentativas, divido pela quantidade de quebras por segundo de um computador, que é um bilhão, divido pelo número de computadores que tenho. Depois divido por 60 para transformar segundos em 9 minutos, por 60 novamente para transformar em horas, por 24 tem-se dias e dividindo por 365 tem em anos. Logo, por força bruta, este algoritmo de substituição monoalfabética é infalível e inquebrável, apenas com o desconforto de ter que memorizar uma k que é uma tabela. Costumo instigar meus alunos neste momento perguntando-lhes se está aí o algoritmo perfeito e inquebrável! Então? Por força bruta, SIM, eis um algoritmo ideal. Só que existem outros meios de se quebrar um algoritmo de cifra que é através da criptoanálise. - Ataque por criptoanálise O ataque por força bruta é possível em todos os casos. Ainda não existem algoritmos imunes a este ataque, ou seja, ele sempre poderá ser tentado. A questão é o tempo que levará. Deseja-se um algoritmo com tantas possibilidades de chave que o ataque levaria anos ou mesmo milhares de anos, de sorte que ninguém sequer irá tentar este ataque por ser inviável (em tempo: estudos sobre computação quântica sugerem a possibilidade da construção de algoritmos de criptografia imunes ao força bruta). A cifra de substituição monoalfabética com uma tabela de trocas, citada no capítulo anterior, é um caso de um algoritmo para o qual demonstrou-se que o foça bruta é inviável. Mas existe a criptoanálise. Simon (ver referências) define a criptografia como a eterna guerra entre criptógrafos e criptoanalistas.Um constrói para que o outro descubra como destruir! No caso mais específico da substituição monoalfabética ela é frágil e sucumbe ao ataque de "Análise de Frequência". Notoriamente todos começam tentando a letra "A" ou, no máximo, indo pelas vogais. Porque? Ora, pelo simples fato de que em um texto em português a letra "A" é a letra que mais se repete! É improvável que um texto não possua uma boa quantidade de letras "A"s. Isto é análise de frequência. 10 Se estou tentando quebrar uma cifra monoalfabética (onde uma letra trocou por outra), sabendo que o texto é em português a primeira coisa que faço é contar as letras. Se, e ao contar, descubro que a mais repetida é o "W" é provável que o "W" está substituindo a letra "A". Onde tem "W" coloco "A" e vou brincando até o texto fazer algum sentido para mim. Todo o algoritmo de substituição monoalfabético é vulnerável ao ataque de análise de frequência. Se o texto for em inglês, sabe-se que a letra que mais se repete é o "E". Mas entenda que substituição monoalfabética não quer dizer apenas letras assim como análise de frequência não se aplica somente a letras que mais se repetem! Posso cifrar o arquivo binário com uma tabela de 256 trocas, onde o byte 00000000 será trocado por 00110110, por exemplo, e assim por diante. Ainda é considerado substituição monoalfabética. No caso, se era um executável, posso realizar análise de frequência nele, descobrir quais os bytes mais presentes em um EXE típico e usar o mesmo princípio! Fabricantes de processadores fazem isto para descobrir as instruções mais usadas em programas a fim de otimizá-las. Logo, foi para o brejo nosso algoritmo perfeito: ele perece pelo ataque de "Análise de Freqüência". Alternativas interessantes ao longo da história foram usadas para melhorar a cifra monoalfabética como variar a troca para os mais frequêntes. Como exemplo, a letra "A", sendo a que mais se repete, pode ser trocada por "X", por "#" ou por "*". Isto, se bem usado, evita a análise de freqüência, mas impõe alguns problemas, como o fato do alfabeto de troca ter que possuir mais símbolos que o original. Enfim, a cifra de Viginère resolveu de forma criativa este problema e ficou inquebrável por séculos até que Babage a quebrou usando, pasmem, Análise de freqüência (Simon). Ainda existem outras técnicas de criptoanálise como por exemplo as colas. Chama-se de cola quando um atacante tem uma vaga ideia de trechos da mensagem. 11 Na segunda guerra mundial os alemães pisaram na bola neste sentido, pois toda a comunicação tinha a saudação nazista, logo ao menos um trecho da mensagem era previsível (Simon). Hoje os modernos algoritmos de cifras devem ser imunes as colas, ou seja, mesmo que eu lhe dê centenas de textos cifrados e as suas centenas de textos originais correspondentes, ainda assim você não deve ser capaz de descobrir qual chave eu usei (esta propriedade é muito interessante e parece utó[*****], mas não é). - Simétricos ou assimétricos? método: o algoritmo matemático que faz a cifra e a decifragem. Não deve ser segredo. chave: único segredo. Uma informação que alimenta o algoritmo para cifrar ou decifrar. Quanto mais possibilidades, mais inviável é o força bruta. Assim como uma porta de uma casa com sua fechadura que só pode ser aberta por quem tiver a chave. Considerando que a fechadura é a prova de criptoanálise, ou seja, a prova de "chaveiros", a única forma de abrir a porta sem a chave correta é se um chaveiro viesse com uma sacola com todas as possíveis chaves para aquela fechadura e ficasse tentando até uma servir. Se forem bilhões de chaves possíveis, torna inviável (isto seria um força bruta). Mas veja que neste exemplo a mesma chave que fecha também abre. Existe apenas uma chave e ela serve para trancar a porta e também para destrancá-la! Neste caso tem-se o que se chama de algoritmo simétrico, onde existe apenas uma única chave e ela serve para cifrar e decifrar. Um problema crônico existe com os algoritmos simétricos e ele atormentou os cientistas durante muito tempo: como pode ocorrer de forma segura a troca das chaves? Se eu quero trocar uma mensagem com a pessoa B como conto ao B que usarei k=10 para cifrar a mensagem? Se tiver a oportunidade de me encontrar pessoalmente com ele, ok, converso de forma reservada e lhe conto a chave. Mas e se ele estiver distante, sem a possibilidade de um encontro pessoal? Telefono para ele? E se alguém estiver com uma 12 escuta telefônica? Envio um email? Veja, não tem como realizar esta comunicação de forma segura. Este problema atormentou muita gente. Na segunda guerra o livro dos códigos da Enigma, com chaves diárias de um mês inteiro, era entregue em mãos aos operadores de rádio. Este tinha ordens para, sob ataque, imediatamente destruir a máquina e o livro. Se os aliados tivessem acesso a este livro seriam capazes de quebrar as comunicações de um mês inteiro (o filme "U-571 - A Batalha do Atlântico" explora este fato, onde os aliados disfarçam um submarino como sendo alemão a fim de tentar capturar a Enigma). Simon conta detalhes deste fato histórico e revela que uma das primeiras formas que os aliados acharam para ler as mensagens foi subornar um oficial alemão (depois eles quebraram com colas e depois com máquinas implementadas por Alan Turing chamadas de "bombas")! Seria possível a troca de chaves de forma segura usando um meio de comunicação inseguro? Isto se consagrou como matematicamente impossível, exceto para os "loucos" Marin Hellman e Whitfield Diffie. Loucos pois aparentemente estavam pesquisando algo impossível, para o qual já se havia desistido: um algoritmo de troca de chaves seguro em um meio inseguro. Este artigo ficaria muito longo se começasse a contar a história do famoso algoritmo Diffie- Hellman e o quanto ele revolucionou a ciência da criptografia. Cabe apenas ressaltar o raciocínio que embasou estes "loucos". Eles pensaram facilmente em um protocolo de troca de informações sigilosas via correio. É interessante para ilustrar. Imagine que quero enviar documentos sigilosos pelo correio para "P" e que eu não confio no carteiro. Colocar os documentos em uma caixa com cadeado não serve, pois como enviaria a chave do cadeado para o "P"? Pelo correio? Por teletransporte? Se não confio no meio, não adianta variar os métodos usando este meio de transporte no qual não confio. Estamos falando de criptografia onde se leva as teorias ao extremo. 13 Mas DH pensaram em um método (eu sou o "E" enviando documentos para "P"): - "E" compra um cadeado em uma ferragem; - "E" coloca os documentos em uma caixa e coloca o seu cadeado. Pressupõe-se que tanto a caixa como o cadeado são invioláveis e que a única forma de abrir a caixa é com a chave; - "E" envia a caixa pelo Correio. Veja que ninguém poderá abir, pois "E" não enviou a chave. O carteiro curioso nada pode fazer; - "P" recebe a caixa. Bom, "P" também não a pode abrir pois ele não tem a chave (surpreso?). Mas "P" ao invés de abrir, compra o seu cadeado e coloca ele também na caixa. A caixa agora tem DOIS cadeados: o de "E" e o de "P"; - "P" devolve a caixa pelo correios ao "E"; - Legal, agora nem mesmo "E" consegue abrir sua caixa. Mas ele apenas usa a sua chave para tirar o seu cadeado, mantendo o cadeado de "P"; - "E" devolve a caixa para "P" que agora pode abrí-la. Observe que neste protocolo jamais houve uma troca de chaves. DH pensavam que deveria existir um princípio matemático que reproduzisse este efeito e depois de muita pesquisa o encontraram na aritmética modular usando números primos(mais tarde se soube que eles não foram pioneiros, pois pesquisas secretas neste sentido haviam sido realizadas muitos anos antes). Um algoritmo assimétrico, portanto, é quando se usa uma chave para cifrar, porém, magicamente, esta chave não serve para abrir o que se cifrou. Não tem espaço aqui para explicar isto em termos matemáticos (se alguém quiser, me peça), mas o fato é que tais algoritmos existem. Muitos chamam estes algoritmos de chave pública e privada, mas o nome técnico deles é algoritmos assimétricos. Esta confusão no nome é porque uma das chaves, normalmente a que serve para cifrar, é tornada pública e a outra é mantida sob sigilo conhecida como chave privada (eu tenho que tomar muito cuidado quando falo disto. Sempre "chave privada". 14 Digamos que eu queira enviar um documento para "P". Bastaria isto: - "P" compra um cadeado e me envia ele ABERTO pelo correio. Não envia a chave; - "E" recebe o cadeado aberto de "P" e o usa para fechar a caixa. Uma vez fechada, "E" já não tem mais condições de abrí-la; - "E" envia a caixa com o cadeado fechado de "P"; - "P" a abre pois tem a chave. Nesta analogia a chave pública seria o cadeado aberto: ele só serve para cifrar. Estendendo a analogia, "P" poderia pedir a um fabricante de cadeados que confeccionasse milhares de cadeados com a mesma chave. O cadeado de "P" estaria em todas as agências de correios, por exemplo. Esta é a ideia. Claro que para que isto funcione o cadeado deve ser forte e a chave deve possuir muitas combinações, senão um ataque de força bruta seria viável. Deve também ser imune a criptoanálise, ou seja, nenhum chaveiro do mundo seria capaz de construir uma nova chave apenas analisando o cadeado. Voltando aos termos "informáticos" a ideia do cadeado foi implementada pelo algoritmo RSA baseado na fatoração de números primos. Constitui em duas chaves, uma chamada de Ke (K para "e"ncriptar) e Kd (k para "d"ecriptar). Uma complementa a outra. O que cifra-se usando Ke apenas com a Kd é possível recuperar (não vou descrever o funcionamento do RSA). Um atacante conhece Ke pois a tornei pública. Contudo, mesmo tendo ela, ele está computacionalmente inviabilizado de calcular Kd. RSA garante isto através do uso de números primos gigantes, hoje com tamanhos de 512 bits. CRIPTOGRAFIA NOS DIAS DE HOJE Na informática tudo se resume a bits. Em um algoritmo de criptografia o que se mede é o tamanho da chave em bits. Se uma chave possui 3 bits, ela pode ser uma dentre os seguintes valores: 15 000, 001, 010, 011, 100, 101, 110 e 111 Isto nos dá apenas 8 possibilidades para k. Na matemática, o número de possíveis chaves é de 2 elevando ao número de bits (23 = 8). Assim sendo, se um algoritmo simétrico tiver 128 bits de chave, como é o caso do AES, significa que ele tem 2128 possíveis chaves ou 340282366920938463463374607431768211456. Como nosso computador testa 1 bilhão de possibilidades e dispõe de 1 bilhão destas para usar, com este hardware eu levaria 170141183460469231731 segundos, já considerando a metade das tentativas. Em uma calculadora, dividindo isto por 60 para ter minutos, por 60 novamente para ter horas, por 24 para ter dias e por 365 para anos, chega-se a estrondosa quantia de 5.395.141.535.403 anos! É pura falta de conhecimento quem afirma que algoritmos simétricos de 128 bits são quebráveis por força bruta. Só mesmo Dan Brown em seu livro "Fortaleza Digital" e seu computador quântico de bilhões de bits para quebrar tal algoritmo! Se considerar o AES128, não estou dizendo que ele é inquebrável pois pode ter fraquezas exploráveis por criptoanálise. Se houver ou (a) não se descobriu ou (b) quem descobriu não nos contou! O que estou afirmando é ser inquebrável quanto a força bruta! Literaturas afirmam que um algoritmos simétrico de 256 bits tem segurança eterna, pois nem todo o silício do universo daria para construir uma máquina que o quebrasse (parenteses agora: da maneira como se constrói computadores hoje em dia. Alguns cientistas vem falando da computação quântica e de como ela mudaria a maneira de fazer as coisas. Computação quântica é cercada de mistério e de exageros. O fato é que já existe uma máquina com sete bits quânticos disponível). Agora o que muitos realmente confundem é quanto a força bruta dos algoritmos assimétricos! É muito, mas muito diferente! 16 Em um simétrico, se eu tenho 8 bits de chave, tenho 256 combinações possíveis. Se pegar o caso do RSA que é um assimétrico e se tiver uma chave de 8 bits, significa que o valor do N é de 256 bits. Sem querer descrever o RSA aqui, posso dizer que N é o resultado da multiplicação de dois números primos de 4 bits. Quebrar a chave significa achar um destes números. E não é de 2 elevado na 4 tentativas, pois os valores 2, 4, 6, 8, 9, 10 não precisam ser testados pois não são primos! No universo de 4 bits só existem estes primos: 2, 3, 5, 7, 11,13. Só! Mata-se em seis tentativas no máximo! Para algoritmos assimétricos, como no caso do RSA, o cálculo do esforço matemático não é simplesmente de 2NumeroDeBits. É muito, mas muito menos que isto. Exatamente por isto é que os algoritmos assimétricos precisam de uma chave muito maior, hoje perto dos 1024 bits. No caso do RSA, dizer que ele é de 1024 bits, significa que o valor de N é de 1024 bits, logo quebrar significa encontrar um número primo de até 512 bits que sirva. - Criptografia com Base64 A função base64_encode() retorna um dado condificado com base64. Esta codificação é designada para que dados binários durem no transporte sobre camadas de transorte que não são 8-bit clean, como mensagens de e-mail. O método Base64 oferece maneira de descriptografar o código e torná-lo igual ao o que era antes da ação. Exemplo: <?php // PARA ENCRIPTAR $palavra = "palavra, valor ou texto a ser encriptado."; echo base64_encode($palavra); // RESULTADO: cGFsYXZyYSwgdmFsb3Igb3UgdGV4dG8gYSBzZXIgZW5jcmlwdGFkby4= 17 // PARA DESFAZER A OPERAÇÃO: $palavra = "cGFsYXZyYSwgdmFsb3Igb3UgdGV4dG8gYSBzZXIgZW5jcmlwdGFkby4="; echo base64_decode($palavra); // RESULTADO: palavra, valor ou texto a ser encriptado. ?> Com esses recursos é possível deixar a aplicação bem mais segura e, por que não, organizada. USANDO CRIPTOGRAFIA BASE64 NAS LINGUAGENS JAVA E ASP O envio e recebimento de informações sigilosas é uma necessidade desde tempos atrás. Com o avanço da tecnologia, a chegada da internet e a facilidade de transmitir dados de maneira extremamente rápida, a criptografia tornou-se uma ferramenta fundamental nos dias de hoje para o envio e recebimento desse tipo de informação, onde apenas o emissor e o receptor tenham acesso a essas informações. A transmissão de dados e o armazenamento de informações têm que possuir segurança e para isso ocorrer são utilizadas criptografias que ajudam a embaralhar as informações, evitando que pessoas não autorizadas vejam. Como essa Etapa trata da parte prática, estaremos falando um pouco sobre este tipo de solução nas linguagens e dando exemplos práticos. - Criptografia Base64 em ASP.NET O namespace System.Security.Cryptography é principal namespace usado para a criptografia, de modo que você normalmente necessidade de adicionar uma declaração para este namespace no topo do seu código para usar as classes contidas nele. A criptografia trabalha com bits e bytes, de forma que em geral você deve converter os dados para o formato binário antes de realizar a criptografia. Depois de descriptografar, você deve converter os bytes descriptografados para o tipo original. 18 Na codificação, usando o mecanismo de codificação base64 por exemplo,não é preciso usar uma chave de codificação ou decodificação. O mecanismo de codificação Base64 é usando quando você precisa representar dados binários em um ambiente baseado em texto. Dessa forma o Simple Mail Transfer Protocol (SMTP) é um protocolo baseado em texto para o envio de e-mail. E para enviar um anexo binário você tem que codificar o anexo usando o mecanismo base64. Para converter para e a partir de base64 podemos usar a classe Convert. Veja exemplo abaixo para VB .NET e C#: Dim binario = New Byte() {1, 2, 3, 4, 5} Dim codificacao = Convert.ToBase64String(binario) Dim retornaBinario = Convert.FromBase64String(codificacao) var binario = new Byte[] { 1, 2, 3, 4, 5 }; var codificacao = Convert.ToBase64String(binario); var retornaBinario = Convert.FromBase64String(codificacao); - Criptografia Simétrica A Criptografia simétrica fornece o tipo mais rápido e mais básico de criptografia. Neste tipo de criptografia, você usa a mesma chave secreta para criptografar e descriptografar dados. A criptografia simétrica remonta ao Egito antigo, e a cifra de César foi uma maneira bem conhecida de realizar a criptografia simétrica, que remonta a Júlio César. Você já ouviu falar do anel decodificador secreto; você usa o mesmo anel decodificador secreto para criptografar e descriptografar mensagens. Um anel decodificador secreto (ou decodificador secreto) é um dispositivo que permite descodificar uma cifra de substituição simples, ou criptografar uma mensagem, trabalhando no sentido oposto. 19 A Criptografia simétrica é rápida e existem muitos algoritmos simétricos; sendo tais algoritmos complexos e eficientes, razão pela qual ainda é o núcleo de muitos protocolos de comunicação criptografada. A Criptografia simétrica usa uma chave secreta para criptografar e descriptografar uma mensagem. Uma chave secreta, que pode ser um número, uma palavra ou apenas uma seqüência de letras aleatórias, é aplicada ao texto de uma mensagem para alterar o conteúdo de uma maneira específica. Isso pode ser tão simples quanto Deslocar cada letra por um número de locais no alfabeto. Desde que tanto o remetente quanto destinatário saibam a chave secreta, eles podem criptografar e descriptografar todas as mensagens que usam essa chave. As chaves usadas são relativamente pequenas, geralmente chaves menores que 256 bits de forma a não onerar o processo. E o que isso significa ? Significa que quanto maior a chave mais forte é criptografia e mais recursos você terá que usar tanto para criptografar como para decriptografar. Abaixo temos uma tabela que mostra o tempo médio de busca, através do método de força bruta para a quebra da chave de criptografia simétrica. 20 Se você resolver usar uma chave com 1 megabyte de tamanho você vai criar uma criptografia muito forte mas vai gastar muito mais recursos (em tempo de processamento) para criptografar e descriptografar uma mensagem. Os algoritmos de chave simétrica ou criptografia de chave única são uma classe de algoritmos para a criptografia que usam chaves criptográficas relacionadas para as operações de cifragem ou decifragem. A chave de cifragem pode ser idêntica à de decifragem ou poderá existir uma transformação simples entre as duas chaves. As chaves representam um segredo, partilhado entre duas ou mais partes, que podem ser usadas para manter um canal confidencial de informação. Usa-se uma única chave, partilhada por ambos os interlocutores, na premissa de que esta é conhecida apenas por eles. A grande desvantagem da criptografia simétrica é o gerenciamento de chaves e a sua distribuição. Se você usa a mesma chave para criptografar e descriptografar os dados, como você vai transferir com segurança a chave para alguém descriptografar os dados da mensagem ? 21 Se um usuário malicioso intercepta a chave, ele pode decodificar suas mensagens. (Uma forma de atenuar este problema é adicionar camadas de segurança na criptografia simétrica.) Abaixo apresentamos alguns exemplos de algoritmos de criptografia simétrica: 1- O DES ("Data Encryption Standard") - O DES é um mecanismo de cifragem tradicional (simétrico) desenvolvido nos anos setenta, utiliza uma chave de 56 bits que é aplicada a blocos de dados com 64 bits, o objetivo destes algoritmos é que seja muito difícil calcular a chave K, mesmo conhecendo o algoritmo DES, uma mensagem cifrada C e uma mensagem original M: C = DES(K,M); 2- Rivest Ciphers (RC2; RC4 e RC5) - Esta é uma sucessão de algoritmos bastante usados que possuem maior flexibilidade e possibilitam maior segurança do que o DES simples. Todos são algoritmos simétricos (a mesma chave é usada para cifrar e para decifrar).; 3- International Data Encryption Algorithm (IDEA) - É mais uma técnica de cifragem simétrica em bloco que é usada nos dias atuais. Usa blocos fixos com 64 bits (8 bytes) e usa chaves com 128 bits (16 bytes). É considerada segura, mas ao contrário dos algoritmos RC usa chaves de comprimento fixo que podem comprometer segurança; 4-BLOWFISH - É um algoritmo simétrico conhecido pela sua velocidade, sendo mais rápido do que o RC2 e o IDEA. Usa blocos fixos com 64 bits (8 bytes), mas as chaves podem ter qualquer comprimento (128 bits é o mais usado); 5- ARCFOUR - É um algoritmo considerado equivalente ao RC4, tal como o RC4 não usa blocos de entrada mas sim um fluxo contínuo de bytes e as chaves podem ter comprimento variável (128 bits é o mais usado); O algoritmo simétrico mais usado atualmente é o AES (Advanced Encryption Standard). O Advanced Encryption Standard (AES, ou Padrão de Criptografia Avançada), também conhecido por Rijndael, é uma cifra de bloco adotada como padrão de criptografia pelo governo dos Estados Unidos. 22 O AES foi anunciado pelo NIST (Instituto Nacional de Padrões e Tecnologia dos EUA) como U.S. FIPS PUB (FIPS 197) em 26 de Novembro de 2001, depois de 5 anos de um processo de padronização. Tornou-se um padrão efetivo em 26 de Maio de 2002. A plataforma .NET contém o algoritmo Rijndael, que você deve usar para todos os novos projetos que exigem a criptografia simétrica. Assim você pode usar a classe RijndaelManaged para realizar a criptografia e descriptografia simétrica. Esta classe exige que você forneça uma chave e um vetor de inicialização (VI). O VI ajuda a garantir que ao criptografar a mesma mensagem várias vezes produz-se textos cifrados diferentes. O mensagem criptografada é comumente conhecido como texto cifrado. Para descriptografar os dados, você deve usar a mesma chave e o VI usado para criptografar os dados. A chave deve ser composta de bytes de dados que compõem o comprimento total da chave. Por exemplo, uma chave de 128 bit é composta por 16 bytes. Se a chave é gerada pela aplicação, utilizar bytes para a chave não é um problema, no entanto, se um ser humano está gerando a chave, é comum querer que a chave seja uma palavra ou frase. Isso pode ser feito usando a classe Rfc2898DeriveBytes, que pode derivar uma chave de uma senha fornecida. - Criando um Sistema de Criptografia A seguir estaremos exemplificando alguns passos para criar um Sistema de Criptografia: Passo 1: Crie um novo projeto do tipo Windows Forms Application com o nome CriptografiaSimetrica; A seguir no formulário form1.vb inclua os controles Label, TextBox e Button disponibilizando: Mensagem a ser criptografada, Senha, Botão para disponibilizar a criptografia e o espaço para apresentação da mensagem criptografada. Passo 2: 23 Declare os namespaces usados no projeto: Imports System.Security.Cryptography Imports System.IO Imports System.TextA seguir declare as seguintes variáveis no início do formulário (conforme exemplo a seguir): Dim textoCifrado As Byte() Dim sal() As Byte = {&H0, &H1, &H2, &H3, &H4, &H5, &H6, &H5, &H4, &H3, &H2, &H1, &H0} Dim senha As String = "" Dim mensagem As String = "" Agora no evento Click do botão de comando inclua o código abaixo: Private Sub btnCriptografar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCriptografar.Click senha = txtSenha.Text mensagem = txtMensagem.Text Dim chave As New Rfc2898DeriveBytes(senha, sal) ' criptografa os dados Dim algoritmo = New RijndaelManaged() algoritmo.Key = chave.GetBytes(16) algoritmo.IV = chave.GetBytes(16) Dim fonteBytes() As Byte = New System.Text.UnicodeEncoding().GetBytes(mensagem) Using StreamFonte = New MemoryStream(fonteBytes) Using StreamDestino As New MemoryStream() Using crypto As New CryptoStream(StreamFonte, algoritmo.CreateEncryptor(), CryptoStreamMode.Read) moveBytes(crypto, StreamDestino) textoCifrado = StreamDestino.ToArray() End Using 24 End Using End Using 'MessageBox.Show(String.Format("Mensagem : {0}{1} Criptografada : {2}", mensagem, Environment.NewLine, Convert.ToBase64String(textoCifrado))) txtResultado.Text = txtResultado.Text + Convert.ToBase64String(textoCifrado) + " :: senha => " + txtSenha.Text + Environment.NewLine End Sub Digite a seguir o código da rotina moveBytes() abaixo: Private Sub moveBytes(ByVal fonte As Stream, ByVal destino As Stream) Dim bytes(2048) As Byte Dim contador = fonte.Read(bytes, 0, bytes.Length - 1) While (0 <> contador) destino.Write(bytes, 0, contador) contador = fonte.Read(bytes, 0, bytes.Length - 1) End While End Sub Entendendo o código: A mensagem a ser criptografada é informada na caixa de texto txtMensagem.Text e atribuída a variável mensagem e o resultado da criptografia é armazenado na variável textoCifrado; - Estamos usando uma senha e um variável (sal) para criar uma chave usando a classe Rfc2898DeriveBytes : Dim chave As New Rfc2898DeriveBytes(senha, sal) Para descriptografar a mensagem você deve usar o mesmo valor de sal. - Em seguida criamos uma instância da classe RijndaelManaged : Dim algoritmo = New RijndaelManaged() 25 - Preenchemos os valores para a chave e para o Vetor de Inicialização (VI): algoritmo.Key = chave.GetBytes(16) algoritmo.IV = chave.GetBytes(16) - Cada vez que você chama chave.GetBytes(16) obtemos bytes diferentes , logo para descriptografar temos que executar a mesma sequência; Primeiro chamar Key A seguir chamar GetBytes para preencher Key E finalmente, chamar key.GetBytes para preencher o VI A seguir convertemos os dados binários para que ele possa ser criptografado usando a classe UnicodeEncoding(); New System.Text.UnicodeEncoding().GetBytes(mensagem) - Criamos 3 Streams para mover os dados: O primeiro Stream é um objeto MemoryStream e representa a fonte que é preenchida com os bytes dos dados; O segundo Stream é um objeto MemoryStream e representa o destino; O próximo Stream é um objeto CryptoStream e é aqui que o serviço é feito Usamos a rotina auxiliar moveBytes que copia os bytes de um fluxo de origem para um fluxo de destino. O objeto de Stream de criptografia é passado para moveBytes como a fonte, e o objeto StreamDestino objeto é passado para moveBytes como destino. Depois da chamada a moveBytes o objeto StreamDestino contém os dados criptografados. Finalmente, a mensagem criptografada é exibida na caixa de texto : 26 txtResultado.Text = txtResultado.Text + Convert.ToBase64String(textoCifrado) + " :: senha => " + txtSenha.Text + Environment.NewLine Para descriptografar a mensagem, precisamos apenas realizar o processo inverso (conforme código a seguir): Private Sub btnDescriptografar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDescriptografar.Click If (textoCifrado Is Nothing) Then MessageBox.Show("Os dados não estão criptografados!") Return End If Dim chave As New Rfc2898DeriveBytes(senha, sal) Dim algoritmo = New RijndaelManaged() algoritmo.Key = chave.GetBytes(16) algoritmo.IV = chave.GetBytes(16) Using StreamFonte = New MemoryStream(textoCifrado) Using StreamDestino As New MemoryStream() Using crypto As New CryptoStream(StreamFonte, algoritmo.CreateDecryptor(), CryptoStreamMode.Read) moveBytes(crypto, StreamDestino) Dim bytesDescriptografados() As Byte = StreamDestino.ToArray() Dim mensagemDescriptografada = New UnicodeEncoding().GetString(bytesDescriptografados) txtMensagemDescriptografada.Text = mensagemDescriptografada End Using End Using End Using End Sub 27 - Criptografia Base64 em Java Muitas vezes o desenvolvedor Java precisa transformar uma String em um array de bytes convertido em uma forma meta-criptografada. Para isso, você pode usar um algoritmo bem simples chamado de Base64. Existem diversas implementações do Base64 para Java e Android. O próprio Android já possui uma implementação bem interessante do Base64. Para codificar uma String você utiliza um método encode e para decodificar a String você utiliza um método decode. Apesar de existirem diversas implementações do Base64 disponíveis, neste documento estaremos sugerindo a implementação OstermillerUtils. Esta implementação é bem interessante e fácil de usar. Depois de utilizar esta implementação em diversos aplicativos, não encontrei problemas de incompatibilidade com outras linguagens nem problemas de conflitos com outras implementações em diferentes jars. Para utilizar o OstermillerUtils, basta fazer o download do jar e acrescentá-lo em seu classpath. 28 Exemplo: String string = Base64.decode(s); String string = Base64.encode(s); Lembre-se que Base64 não possui criptografia através de senha. Para isso você pode utilizar diversas implementações existem do Cipher. Abaixo damos um exemplo mais completo usando a criptografia Base64: import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import java.io.IOException; public class TesteBase64{ public static void main(String args[]){ BASE64Encoder encoder = new BASE64Encoder(); String code = encoder.encodeBuffer("Teste Base64".getBytes()); //Vai imprimir "Teste Base64 -(codificado)- VGVzdGUgQmFzZTY0" System.out.println("Teste Base64 -(codificado)- " + code); BASE64Decoder decoder = new BASE64Decoder(); try{ byte[] decoded = decoder.decodeBuffer("VGVzdGUgQmFzZTY0"); //Vai imprimir "VGVzdGUgQmFzZTY0 -(decodificado)- Teste Base64" System.out.println("VGVzdGUgQmFzZTY0 -(decodificado)- " + new String(decoded)); }catch(IOException ex){ } } } 29 RELATÓRIO 01 - UTILIZANDO CRIPTOGRAFIA O tema abordado é muito amplo e o conteúdo apresentado é apenas uma introdução para, quem sabe, ser continuado em trabalhos futuros. Muita coisa ficou de fora, como os tipos de cifras (de bloco, de fluxo), algoritmos de HASH e até mesmo a descrição dos algoritmos. Entender o funcionamento de um RSA por exemplo é muito gratificante, embora não interesse a maioria. Para nós o mais importante é saber como empregá-lose não os seus princípios matemáticos. Se os matemáticos dizem que que é inviável, que seja. A gente acredita, implementa e usa! Deve-se dizer que os algoritmos assimétricos são extremamente onerosos, ou seja, consomem muita CPU. Seu uso deve ser evitado ou, pelo menos, minimizado pois o processador agradece. Como os simétricos são absolutamente seguros e rápidos, sendo seu único problema a troca de chave, é comum usar um assimétricos apenas para trocar a chave convergindo após para um simétrico. O protocolo SSL é exatamente assim (quem está por trás do SSH e o HTTPS, dentre outros). - Criando uma criptografia segura no PHP Se você éum desenvolvedor web profissional, segurança é um aspecto importante do seu trabalho. Se você estiver planejando armazenar dados críticos ou sensíveis em sua aplicação web, como senhas, cartões de credito etc, você deveria usar uma criptografia segura (strong cryptography) para proteger esses dados. O que é criptografia segura? Criptografia segura é o uso de sistemas ou componentes que são considerados altamente resistentes à criptoanálise, o estudo de métodos para decifrar códigos. Teoricamente falando, se criptografarmos e armazenarmos dados confidenciais em um banco de dados, arquivo, ou qualquer outra coisa, um atacante malicioso não será capaz de descriptografá-los sem saber a chave, uma sequência de números usada para criptografar ou 30 para descriptografar dados. Como podemos provar que o atacante não será capaz de descriptografar os dados? Infelizmente, a resposta correta é que não podemos ter certeza. Podemos apenas ter um bom nível de segurança usando algoritmos bem testados de criptografia (criptografia segura). Por exemplo, a cifra ENIGMA, usada na Segunda guerra Mundial como um sistema para criptografar a comunicação entre soldados alemães, hoje não é considerada criptograficamente forte. O DES, um algoritmo padrão do FIST em 1976, que infelizmente ainda é utilizado em muitos sistemas, não é considerado criptograficamente forte mais. Em 1998, a Electronic Frontier Foundation (EFF) construiu uma máquina, o EFF DES cracker, para executar uma pesquisa por força bruta pela key space (o conjunto das possíveis senhas) da encriptação cipher – ou seja, para descriptografar uma mensagem criptografada tentando cada chave possível. O objetivo é provar que a chave DES não é mais segura. Essa máquina era capaz de encontrar a chave de uma mensagem criptografada em menos de um dia, e estamos falando de 1998! Atualmente, alguns dos algoritmos que podem ser considerados criptograficamente fortes sao: Blowfish, Twofish, Advanced Encryption Standard (AES, Rijndael), 3DES, Serpent, RSA etc. É importante dizer que a segurança de qualquer algoritmo é relacionada à força e ao tamanho da chave, e iremos falar desse ponto a seguir. Por que devemos usar criptografia segura? Muitos desenvolvedores, quando têm a necessidade de criptografar alguma informação, tentam implementar suas cifras pessoas usando diferentes abordagens. Na internet, você consegue encontrar várias implementações de cifras domésticas no PHP. Eu desencorajo fortemente o uso dessas cifras, e vou dizer por quê. Criar uma cifra segura é algo bastante complicado e, se você não for um expert, eu diria que é uma tarefa praticamente impossível. Além disso, estamos falando de criptografia segura, o que significa que mesmo se você for capaz de criar uma nova cifra, você precisa de tempo para provar que ela é segura, compartilhando a implementação do algoritmo na comunidade open source. 31 O PHP oferece diferentes implementações dos algoritmos de criptografia mais importantes. Em particular, o PHP tem as extensões de criptografia a seguir: Hash, mcrypt e OpenSSL A extensão Hash não precisa de bibliotecas externas e é acionada por padrão no PHP 5.1.2. Essa extensão substitui a antiga extensão mhash. Com essa extensão, você pode gerar hashes ou HMAC (Hash-based Message Authentication Code). Ela suporta os algoritmos de geração hash mais comuns usados na criptografia segura. Se você quiser saber quais algoritmos são suportados pelo seu ambiente PHP, pode utilizar a função hash_algos(), que te dá uma lista de todos os algoritmos suportados. A extensão mcrypt é uma interface para a biblioteca mcrypt, que suporta uma grande variedade de blocos de algoritmos, como DES, 3DES, Blowfish (default), 3-WAY, SAFER- SK64, SAFER-SK128, TWOFISH, TEA, RC2 e GOST nos métodos de encriptação CBC, OFB, CFB e ECB. Essa extensão é a mais utilizada para criptografar dados usando cifras simétricas. A extensão OpenSSL usa as funções do OpenSSL project para geração e verificação de assinaturas e para lacrar (criptografar) e abrir (descriptografar) dados. Você pode utilizar o OpenSSL para proteger dados usando a criptografia pública de chaves com o algoritmo RSA. - Melhores práticas no PHP Até agora,discutimos os aspectos gerais da criptografia segura. Agora eu gostaria de te falar quais são as melhores práticas em como usar a criptografia segura no PHP. Aqui você pode encontrar algumas das minhas recomendações: Use algoritmos padrão. Sempre use um algoritmo padrão para criptografar seus dados. Não tente implementar sua cifra doméstica, você irá gastar muito tempo e energia sem conseguir obter uma segurança real. Minha sugestão é usar os melhores algoritmos disponíveis. Exemplos de algoritmos criptograficamente sólidos são: - algoritmos de chave simétrica: AES, que é um padrão FIST 197 desde 2001; 32 – algoritmos de chaves públicas: RSA, um algoritmo padrão da industria utilizado em muitos produtos; – Funções Hash: SHA-x, em que x pode ser 1,256,384, e 512. SHA é um padrão NIST. - Espaço da chave Quando falamos em segurança de uma cifra, o espaço chave é um dos parâmetros mais importantes. Se nenhum design sólido explícito é dado pela cifra, a força do design é igual ao tamanho da chave. Por exemplo, a cifra DES usa uma chave de 56 bits, o que significa que o espaço da chave é de 2^56. Estes números parecem imensos, mas computadores modernos podem suportá-los, e o EFF prova isso com o cracker DES. Então o espaço da chave é muito importante. Para cifras simétricas, eu diria que 128 bits é o tamanho mínimo para uma chave de criptografia forte. Com relação à criptografia de chaves públicas, os experts recomendam um tamanho mínimo de 2048 bits se quisermos proteger nossos dados por 20 anos. - Princípio de Kerchoof Auguste Kerckhoffs foi um linguista alemão e criptógrafo do Higher Commercial Studies em Paris no final do século 19. Ele escreveu, em um artigo famoso do “le Journal des Sciences Militaires”, a seguinte frase, que é considerada o máximo na criptografia moderna: Um sistema de criptografia deve ser seguro mesmo se tudo que disser respeito ao sistema, exceto a chave, seja de conhecimento público. Usando a interpretação da Shannon, “O inimigo conhece o sistema”. Na minha opinião, no mundo do software, você somente tem segurança com o uso de algoritmos open source. Se o código-fonte foi testado por milhares de pessoas ao redor do mundo, a probabilidade de encontrar um bug de segurança e consequentemente de consertá-lo é mais alta usando um software open source do que comparada à chance do uso de um software proprietário. Não use rand() ou mt_rand() Até para este ponto, não tente implementar seu gerador aleatório. Você não pode implementar um gerador de números aleatório seguro usando a função rand() no PHP. Essa função usa a biblioteca libc para gerar números pesudoaleatórios que não são seguros para aplicativos de 33 criptografia. Ela gera números aleatórios usando um métodoaditivo linear de feedback, com um período curto, que é previsível. Até a função mt_rand() não é segura do ponto de vista da criptografia. Ela uso o algoritmo Mersenne Twister para gerar números pseudoaleatórios. Essa função é melhor que a rand(), porque é mais rápida e produz números pseudoaleatórios com um período maior, mas ainda é um algoritmo determinista, então também é previsível. Para gerar um número aleatório criptograficamente forte no PHP, você tem que usar a função openssl_random_pseudo_bytes() da biblioteca OpenSSL. Essa função está disponível a partir do PHP 5.3. Se você estiver usando uma versão antiga do PHP, pode usar esta implementação: function secure_rand($length) { if(function_exists('openssl_random_pseudo_bytes')) { $rnd = openssl_random_pseudo_bytes($length, $strong); if($strong === TRUE) return $rnd; } for ($i=0;$i<$length;$i++) { $sha= sha1(mt_rand()); $char= mt_rand(0,30); $rnd.= chr(hexdec($sha[$char].$sha[$char+1])); } return $rnd; } Nessa implementação, eu encriptei os outputs da mt_rand() para hash. Esse método aumenta a segurança do Mersenne Twister, mas não tem o mesmo nível de segurança da implementação OpenSSL. Use um valor salt em funções hash Se você estiver usando uma função hash para proteger os dados, como por exemplo uma senha, concatene os dados com um valor randômico (salt) antes de gerar o hash. Um salt randômico irá proteger seus dados de Dictionary attacks. 34 - Tamanho e força de senhas Não dê a possibilidade para os usuários da sua aplicação web de escolherem senhas bobas e pequenas. Você deve sempre usar uma senha com no mínimo 8 caracteres misturados com números e letras. Você pode usar a biblioteca CrackLib para testar a “força” de uma senha. Não use senhas com texto puro como chave para encriptar Uma boa prática na criptografia, usando cifras simétricas, é usar um valor hashed como chave de uma cifra. Esse método aumenta a segurança dos dados criptografados ao adicionar maior aleatoriedade. Isso significa que se você quiser gerar uma boa chave para a cifra, você deve usar o hash da senha e usá-lo como a chave do seu algoritmo de criptografia. Use Base64 para codificar dados criptografados Se você precisar migrar dados criptografados entre sistemas diferentes, como transmitir dados pela internet, é recomendado codificar os dados em Base64. No PHP, você pode usar as funções base64_encode() e base64_decode(). Essa codificação irá garantir que seus dados sejam armazenados corretamente, independentemente do sistema de codificação utilizado no seu ambiente. É claro que essas são apenas sugestões gerais, e os pontos relacionados ao uso correto da criptografia são mais do isso. - Partindo para a prática Apesar de haver muita coisa escrita sobre a conversão de arquivos para Base64, resolvi engrossar o caldo com a minha visão. Além de falar um pouco sobre a história do Base64, o foco aqui é mostrar dois códigos que podem ser úteis aos que desejam compreender como funciona a codificação. Inicialmente, o Base64 foi desenvolvido para permitir o envio de dados binários através de protocolos que suportavam apenas conteúdo escrito em ASCII. Tudo isso porque, como os 35 meios de transmissão eram extremamente falhos, convencionou-se por usar um dos oito bits de um bytes como "bit de paridade" a fim de facilitar a detecção de erros em cada byte transmitido. A ideia é transformar todo o conteúdo original numa sequência de caracteres ASCII. Para isso foram usadas as 26 letras do alfabeto em suas formas minúscula e maiúscula, os 10 números, o '+' e a '\', totalizando 64 caracteres. Como toda grande mudança é onerosa e pode gerar um monte de problemas imprevistos, todas as novidades que foram chegando precisavam tiveram que se adaptar ao que já existia. Assim, mesmo não sendo mais tão necessária essa correção byte a byte, o Base64 continua sendo usado da mesma forma que antes e em outro monte de a?licações. Por exemplo, tudo o que anexamos a um e-mail é codificado pelo Base64 antes de ser enviado. Outro bom exemplo são os arquivos que precisam guardar dados binários e cuja visualização seja interessante, como é o caso dos arquivos de chaves públicas e privadas que são às vezes guardados em Base64. Falando mais especificamente da codificação, a RFC 4648 diz que a conversão deve ser feita com base nas seguintes regras: - cria-se um vetor com os caracteres válidos, na seguinte ordem: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ os bytes a serem convertidos são agrupados em blocos de até 3 bytes que são divididos em 4 conjuntos de 6 bits; - Cada um desses conjuntos representa um número entre 0 e 63 e, assim, um elemento do vetor anterior; - Se o bloco de entrada não tiver 3 bytes, o bloco de saída é completado com o caracter '='; por fim, cada linha do arquivo codificado deve ter 64 bytes, seguidos dos caracteres que representam uma nova linha '\r\n'. Vejam um exemplo de conversor que fiz: #include <stdio.h> #include <stdlib.h> #define safe_free(x) if (x) { free(x); x = NULL; } 36 /* * A codificacao consiste em pegar um bloco com ate 3 bytes e transformar * em quatro bytes nos padroes do Base64. * * +-----------------------------+ * |123456 78|1234 5678|12 345678| * |123456|12 3456|1234 56|123456| * +-----------------------------+ * * * cbEntrada: vetor com ate 3 posicoes * cbSaida : vetor sempre com 4 posicoes * cbTamanho: quantidade de bytes em cbEntrada */ void CodificaBloco(unsigned char *cbEntrada, unsigned char *cbSaida, unsigned char cbTamanho) { char cbBase[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; unsigned char cbConta = 0; unsigned char cbX; for(cbX = 0; cbX < cbTamanho; cbX++) { if (cbConta == 0) { cbSaida[cbConta++] = cbBase[(unsigned char)(cbEntrada[0] >> 2)]; } else if (cbConta == 1) { cbSaida[cbConta++] = cbBase[(unsigned char)(((cbEntrada[0] & 3) << 4) | (cbEntrada[1] >> 4))]; } else if (cbConta == 2) { 37 cbSaida[cbConta++] = cbBase[(unsigned char)(((cbEntrada[1] & 15) << 2) | ((cbEntrada[2] & 192) >> 6))]; } } // acerta "sobra" de bits cbX--; if (cbX == 0) { cbSaida[cbConta++] = cbBase[(unsigned char)((cbEntrada[0] & 3) << 4)]; } else if (cbX == 1) { cbSaida[cbConta++] = cbBase[(unsigned char)((cbEntrada[1] & 15) << 2)]; } else if (cbX == 2) { cbSaida[cbConta++] = cbBase[(unsigned char)(cbEntrada[2] & 63)]; } // completa saida com '=' while (cbConta < 4) cbSaida[cbConta++] = (unsigned char) '='; } /* * Codifica entradas de 48 bytes ou menos e retorna linhas * de ate 64 caracteres base64, finalizadas com '\r\n' * * clEntrada: vetor com ate 48 bytes * clSaida : vetor com ate 67 bytes * clTamanho: quantidade de bytes em clEntrada * * Retorno: quantidade de bytes em clSaida */ int CodificaLinha(unsigned char *clEntrada, 38 unsigned char *clSaida, unsigned char clTamanho) { unsigned char *clBloco64, *clBloco256, clX, clY, clZ, clTamanhoSaida; clBloco256 = (unsigned char*) malloc(sizeof(char) * 3); clBloco64 = (unsigned char*) malloc(sizeof(char) * 4); // limpa as variaveis for (clZ = 0; clZ < 67; clZ++) { if (clZ < 4) { clBloco64[clZ] = '\0'; if (clZ < 3) clBloco256[clZ] = '\0'; } clSaida[clZ] = '\0'; } clY = 0; clTamanhoSaida= 0; // codifica a linha for (clX = 0; clX <= clTamanho; clX++) { // codifica o bloco if ((clY == 3) || ((clX == clTamanho) && (clY))) { clX--; CodificaBloco(clBloco256, clBloco64, clY); // copia bloco para a saida e limpa variaveis for (clZ = 0; clZ < 4; clZ++) { clSaida[clTamanhoSaida++] = clBloco64[clZ]; clBloco64[clZ] = '\0'; if (clZ < 3) clBloco256[clZ] = '\0'; } 39 clY = 0; } else { clBloco256[clY++] = clEntrada[clX]; } } // finaliza a linha com CR + LF if (clTamanhoSaida) { clSaida[clTamanhoSaida++] = '\r'; clSaida[clTamanhoSaida++] = '\n'; clSaida[clTamanhoSaida] = '\0'; } safe_free(clBloco256); safe_free(clBloco64); return clTamanhoSaida; } /* * Codifica um arquivo binario para base64 * * caEntrada: arquivo binario a ser codificado * caSaida : arquivo base64 a ser gravado */ void CodificaArquivo(FILE *caEntrada, FILE *caSaida) { unsigned char *caLinha256, *caLinha64, caX; caLinha256 = (unsigned char*) malloc(sizeof(unsigned char) * 48); caLinha64 = (unsigned char*) malloc(sizeof(unsigned char) * 67); caX = 0; while(!feof(caEntrada)) { caLinha256[caX++] = (unsigned char)getc(caEntrada); if (feof(caEntrada) || (caX == 48)) 40 { // para desconsiderar o ultimo caracter if (feof(caEntrada)) caX--; if (!caX) break; caX = CodificaLinha(caLinha256, caLinha64, caX); fprintf(caSaida, "%s", caLinha64); caX = 0; } } safe_free(caLinha256); safe_free(caLinha64); } void ImprimeUso() { printf("+----------------------------------------------+\n"); printf("| Para usar o encode digite: |\n"); printf("| encode ArquivoEntrada ArquivoSaida |\n"); printf("| O ArquivoEntrada sera convertido para Base64 |\n"); printf("| e salvo no ArquivoSaida |\n"); printf("+----------------------------------------------+\n"); } int main(int argc, char **argv) { unsigned char *Linha256, *Linha64, *Bloco256, *Bloco64; unsigned char X = 0; FILE *ArquivoEntrada, *ArquivoSaida; if (argc != 3) { ImprimeUso(); return 0; } else 41 { ArquivoEntrada = fopen(argv[1], "rb"); ArquivoSaida = fopen(argv[2], "wb"); } CodificaArquivo(ArquivoEntrada, ArquivoSaida); fclose(ArquivoEntrada); fclose(ArquivoSaida); return 0; } Já a decodificação, seguindo a lógica inversa, seria: #include <stdio.h> #include <stdlib.h> #define safe_free(x) if (x) { free(x); } x = NULL; /* * A decodificacao consiste em separar o texto codificado * em blocos de 4 caracteres, identificar o numero * correspondente a sua pposicao no vetor de codificacao * e reagrupar os bits de forma a remontar os bytes originais * +-----------------------------+ * |123456|12 3456|1234 56|123456| * |123456 78|1234 5678|12 345678| * +-----------------------------+ * * * Decodifica um bloco em base64 para binario. * dcEntrada: vetor com 4 bytes * dcSaida : vetor com 3 bytes * * Retorno: tamanho do bloco decodificado 42 */ int DecodificaBloco(unsigned char *dcEntrada, unsigned char *dcSaida) { unsigned char dcTamanho, dcX, dcConta, dcPosicao; // limpa saida for (dcX = 0; dcX < 3; dcX++) dcSaida[dcX] = '\0'; // acha o tamanho original do bloco dcTamanho = 4; while((dcEntrada[--dcTamanho] == '=') && (dcTamanho)); // inicia decodificacao dcConta = 0; for (dcX = 0; dcX < (dcTamanho + 1); dcX++) { // define posicao do caracter na tabela if (DEBUGANDO) printf("dcX = %d\n", dcX); if ((dcEntrada[dcX] >= 'A') && dcEntrada[dcX] <= 'Z') dcPosicao = (dcEntrada[dcX] - 65); if ((dcEntrada[dcX] >= 'a') && dcEntrada[dcX] <= 'z') dcPosicao = (dcEntrada[dcX] - 71); if ((dcEntrada[dcX] >= '0') && dcEntrada[dcX] <= '9') dcPosicao = (dcEntrada[dcX] + 4); if (dcEntrada[dcX] == '+') dcPosicao = 62; if (dcEntrada[dcX] == '/') dcPosicao = 63; // recompoe os bits if (dcX == 0) { dcSaida[dcConta] = (unsigned char)(dcPosicao << 2); } else if (dcX == 1) { dcSaida[dcConta++] = (unsigned char) (dcSaida[0] | ((dcPosicao & 48) >> 4)); 43 if ((dcPosicao & 15) << 4) dcSaida[dcConta] = (unsigned char)((dcPosicao & 15) << 4); } else if (dcX == 2) { dcSaida[dcConta++] = (unsigned char) (dcSaida[1] | ((dcPosicao & 60) >> 2)); if ((dcPosicao & 3) << 6) dcSaida[dcConta] = (unsigned char)((dcPosicao & 3) << 6); } else if (dcX == 3) { dcSaida[dcConta] = (unsigned char)(dcSaida[2] | dcPosicao); } } return --dcTamanho; } /* * Decodifica uma linha em base64 para binario, retornando o tamanho * da linha decodificada. * * dlEntrada: vetor com ate 64 bytes * dlSaida : vetor com ate 48 bytes * * Retorno: tamanho da linha decodificada */ int DecodificaLinha(unsigned char *dlEntrada, unsigned char *dlSaida) { unsigned char *dlBloco256, *dlBloco64, dlConta, dlX, dlTamanhoBloco, dlTamanhoLinha; dlBloco256 = (unsigned char*) malloc(sizeof(unsigned char) * 3); dlBloco64 = (unsigned char*) malloc(sizeof(unsigned char) * 4); dlConta = 0; 44 dlTamanhoLinha = 0; // limpa a variavel de saida for (dlX = 0; dlX < 49; dlSaida[dlX++] = '\0'); // decodifica linha for (dlX = 0; (dlEntrada[dlX] != '\0'); dlX++) { dlBloco64[dlConta++] = dlEntrada[dlX]; // decodifica bloco if (dlConta == 4) { dlTamanhoBloco = DecodificaBloco(dlBloco64, dlBloco256); // copia bloco decodificado para a saida for (dlConta = 0; dlConta <= dlTamanhoBloco; dlSaida[dlTamanhoLinha++] = dlBloco256[dlConta++]); dlConta = 0; } } safe_free(dlBloco256); safe_free(dlBloco64); return dlTamanhoLinha; } /* * Decodifica um arquivo em base64 para binario. * * daEntrada: arquivo base64 a ser decodificado * daSaida : arquivo binario a ser gerado. */ void DecodificaArquivo(FILE *daEntrada, FILE *daSaida) { unsigned char *daLinha256, *daLinha64, daX; // porque o tamanho pode ser negativo char daTamanho; 45 daLinha256 = (unsigned char*) malloc(sizeof(unsigned char) * 49); daLinha64 = (unsigned char*) malloc(sizeof(unsigned char) * 67); daX = 0; while(!feof(daEntrada)) { daLinha64[daX++] = (unsigned char)getc(daEntrada); // ignora /r e /n if ((daLinha64[daX - 1] == '\n') || (daLinha64[daX - 1] == '\r')) { daLinha64[--daX] = '\0'; continue; } if (feof(daEntrada) || (daX == 64) || (daLinha64[daX - 1] == '\0')) { if (daLinha64[daX - 1] != '\0') daLinha64[daX++] = '\0'; daTamanho = DecodificaLinha(daLinha64, daLinha256); for (daX = 0; (daX < daTamanho); daX++) { putc((char)daLinha256[daX], daSaida); } daX = 0; } } safe_free(daLinha256); safe_free(daLinha64);return; } void ImprimeUso() { printf("+-----------------------------------------------+\n"); printf("| Para usar o decode digite: |\n"); printf("| decode ArquivoEntrada ArquivoSaida |\n"); printf("| O ArquivoEntrada sera convertido para Base256 |\n"); printf("| e salvo no ArquivoSaida |\n"); printf("+-----------------------------------------------+\n"); } 46 int main(int argc, char **argv) { FILE *ArquivoEntrada, *ArquivoSaida; if (argc != 3) { ImprimeUso(); return 0; } else { ArquivoEntrada = fopen(argv[1], "rb"); ArquivoSaida = fopen(argv[2], "wb"); } DecodificaArquivo(ArquivoEntrada, ArquivoSaida); fclose(ArquivoEntrada); fclose(ArquivoSaida); return 0; } REFERÊNCIAS BIBLIOGRÁFICAS [1] BEAL, Adriana. Segurança da Informação: princípios e melhores práticas para a proteção dos ativos de informação nas organizações – São Paulo: Atlas, 2005. [2] BSI BRASIL. ISO/IEC 27001 Segurança da Informação. Disponível em: <http://www.bsibrasil.com.br/certificacao/sistemas_gestao/normas/iso_iec27001/>. Acesso em: 12 fev. 2013. [3] HOLANDA, Roosevelt de. O estado da arte em sistemas de gestão da segurança da Informação: Norma ISO/IEC 27001:2005 – São Paulo: Módulo Security Magazine, 19 jan 2006. Disponível em <http://www.modulo.com.br>. Acesso em: 12 fev. 2013. [4] ISO 17799. ABNT NBR ISO/IEC 17799:2005 – Tecnologia da Informação – Técnicas de segurança. Código de prática para a gestão da segurança da informação. Associação Brasileira de Normas Técnicas – Rio de Janeiro: ABNT, 2005. 47 [5] LYRA, Maurício Rocha. Segurança e auditoria em sistemas de informação. Rio de Janeiro: Ciência Moderna, 2008. 253 p. [6] OLIVA, Rodrigo Polydoro; OLIVEIRA, Mírian. Elaboração, Implantação e Manutenção de Política de Segurança por Empresas no Rio Grande do Sul em relação às recomendações da NBR/ISO17799 – ENANPAD. 2003. [7] PARRA, Gislaine. Metodologia para análise de segurança aplicada em uma infraestrutura de chave pública. 2002. 99f. Dissertação (Mestrado em Ciência da Computação) – Programa de Pós-Graduação em Ciência da Computação da Universidade Federal de Santa Catarina, Florianópolis. [8] SÊMOLA, Marcos. Gestão da Segurança da Informação: uma visão executiva – Rio de Janeiro: Campus, 2003. [9] THE ISO 27000 DIRECTORY. An Introduction to ISO 27001, ISO 27002….ISO 27008. Disponível em <http://www.27000.org/index.htm>. Acesso em: 12 fev. 2013. 48 SUMÁRIO INTRODUÇÃO À CRIPTOGRAFIA
Compartilhar