Python 2 vs. 3: diferenças de segurança
10 de outubro de 2017
0 minutos de leituraPython 3 e Python 2 têm várias diferenças funcionais. Por si só, elas não são necessariamente melhores nem piores (embora seja possível argumentar que Python 3 deveria ser uma melhoria), mas qualquer mudança pode introduzir riscos. Este post destaca e explica algumas diferenças entre as versões que têm implicações de segurança. Tenha esses pontos em mente ao migrar aplicações Python entre as versões.
O estado atual das versões 2 e 3 do Python
Python 3 foi lançado pela primeira vez em 2008. Um dos problemas que afetaram sua adoção foi a falta de compatibilidade com versões anteriores. Muitos desenvolvedores continuaram trabalhando com versões anteriores, principalmente a versão 2, para evitar o esforço de refatorar suas aplicações. Por esse motivo, o suporte ao Python 2 continuou, e a versão permaneceu em desenvolvimento ativo após o lançamento da versão 3.0.
A versão 2.7 é a versão atual e final do Python 2, ainda usada ativamente por muitos desenvolvedores. A 2.7 foi disponibilizada em 2010, e a Python Foundation anunciou que ela chegaria ao fim da vida útil em 2020.
Python 3.6 é a versão atual do Python e foi disponibilizada pela primeira vez em 2016. Python 3 é considerado o futuro da linguagem de programação Python.
Python 2 vs. 3: principais diferenças na linguagem de programação e suas implicações de segurança
As principais diferenças entre Python 2 e Python 3 são destacadas abaixo. Embora nenhuma das mudanças tenha foco em segurança, todas têm implicações de segurança que você deve ter em mente.
Encadeamento de exceções
Python 3 introduziu o encadeamento de exceções por padrão. No Python 2, quando ocorre uma exceção em uma biblioteca referenciada pelo programa Python principal, os detalhes dessa exceção não são exibidos por padrão no rastreamento da pilha. No Python 3, o rastreamento da pilha mostra todos os detalhes das exceções, tanto as geradas pelo programa principal quanto pelas bibliotecas referenciadas.
Qualquer aspecto interno do seu código ou do comportamento em tempo de execução, quando exposto ao usuário, representa um risco de segurança. Embora seja conveniente para depuração, o encadeamento de exceções expõe muito mais informações a um possível invasor, que pode executar um rastreamento da pilha para ver como o software reage a erros.
É possível desativar exceções encadeadas no Python 3 se você acreditar que os riscos superam os benefícios.
Método de entrada de dados
Python 2 oferece a função eval(), conhecida por não ser segura porque avalia o texto passado como parâmetro e aceita um segundo argumento opcional para os valores globais usados durante a avaliação. No Python 2, a função input() funciona de forma semelhante a eval() e também não é segura.
Os invasores podem explorar essas funções fornecendo nomes de variáveis como entrada e obtendo os valores dessas variáveis, que podem conter informações confidenciais.
Por isso, uma prática recomendada de segurança no Python 2 é não usar eval() nem input(); em vez disso, use raw_input(), que não avalia variáveis no texto de entrada.
O que mudou no Python 3? raw_input() foi removida, e sua funcionalidade foi transferida para uma nova função chamada input(). A nova função input() funciona exatamente como raw_input() funcionava no Python 2, então agora é seguro usá-la.
Mas, se você executar um programa Python 3 no Python 2, todas as entradas poderão ser exploradas.
Em resumo:
Função | Python 2.x | Python 3.x |
|---|---|---|
| Avalia variáveis — não é seguro | Avalia variáveis — não é seguro |
| Avalia variáveis — não é seguro | Não avalia variáveis, como |
| Não avalia variáveis | Removida |
Divisão de inteiros
Python 2 trata a divisão de inteiros de forma diferente e menos intuitiva que Python 3. No Python 2, a divisão de inteiros sempre retorna um valor inteiro, que é o maior inteiro menor ou igual ao resultado. Esse método de divisão é conhecido como divisão por piso:
A divisão de inteiros faz mais sentido no Python 3, porque a linguagem não aplica automaticamente a divisão inteira com arredondamento para baixo ao dividir inteiros:
Essa forma mais intuitiva de lidar com a divisão de inteiros não é compatível com versões anteriores do Python 2. Por isso, é especialmente perigoso executar código Python 3 no Python 2, pois o comportamento da divisão de inteiros não gera um erro de sintaxe.
Isso também cria um possível problema de segurança. Por exemplo, imagine um aplicativo que solicita um PIN numérico e depois divide o PIN armazenado no sistema pelo informado pelo usuário para indicar sucesso. No Python 3, isso funcionaria bem, mas executar o mesmo aplicativo no Python 2 permitiria muitas tentativas incorretas.
Embora esse exemplo não seja realista, ele ilustra como depender da divisão de inteiros para arredondar números (no Python 2) ou não arredondá-los (no Python 3) provavelmente mudará o comportamento, o que poderia ser explorado por invasores para contornar mecanismos de proteção.
Suporte a Unicode
As duas versões do Python tratam strings (sequências de caracteres) de formas diferentes.
Python 2 usa o padrão de codificação ASCII por padrão. O ASCII é limitado à representação de 256 caracteres. Isso limita a flexibilidade do Python para codificar caracteres, especialmente os não padronizados.
Usar Unicode no Python 2 exige sintaxe adicional. Por exemplo, ao usar print, é preciso envolver o texto de entrada na função unicode() para lidar com caracteres especiais.
O padrão Unicode é muito mais versátil: oferece suporte a mais de 128.000 caracteres. No Python 3, Unicode é o padrão. Não é preciso usar sintaxe adicional para definir valores Unicode: eles são impressos automaticamente como strings utf-8.
Embora poderosa, essa mudança pode abrir caminho para um risco considerável de phishing. Por exemplo, é possível direcionar usuários a um URL em que os caracteres do nome de domínio foram substituídos por letras de alfabetos não latinos. Uma publicação de Xudong Zheng mostrou como isso apareceria para os usuários:

À primeira vista, o URL parece correto, mas nem tudo é o que parece. Embora o URL do exemplo de Xudong pareça ser “apple.com”, na verdade é “xn-pple-43d.com”. Usando, por exemplo, o “a” cirílico (U+0430) em vez do “a” ASCII (U+0061), é possível fazer com que alguns navegadores ocultem involuntariamente o domínio real. Assim, embora o site pareça seguro, o usuário é direcionado a outro domínio, que pode apresentar riscos de segurança. Isso é conhecido como ataque homográfico.
Esse ataque pode ser relevante no Python se tiver como alvo um caminho de diretório analisado pelo aplicativo Python ou um parâmetro de consulta. Por exemplo, se a aplicação expõe um caminho http://domain.com/Acme, um invasor pode substituir as letras de “Acme” por caracteres de outros alfabetos, levando os usuários a outra conta.
Ao migrar um aplicativo do Python 2 para o 3, considere que todas as entradas serão renderizadas como Unicode, o que pode abrir caminho para que invasores enganem seus usuários.
Vulnerabilidades e correções de segurança no Python 3.6 vs. 2.7
Versões antigas do Python 2 e do 3 tinham vulnerabilidades graves e devem ser evitadas (consulte CVE Details para ver as versões do Python).
No entanto, nas versões mais recentes, 2.7.x e 3.6.x, a maioria das vulnerabilidades foi corrigida. Python 2 e Python 3 recebem manutenção ativa atualmente e, desde que você atualize para a versão mais recente, estará protegido contra as principais vulnerabilidades. Ainda assim, é provável que a Python Software Foundation dedique menos atenção ao Python 2 e que ele receba atualizações de segurança com menos frequência que o Python 3.
Mais importante: o Python 2 chegará ao fim da vida útil em 2020. A partir daí, a linguagem não receberá manutenção e não haverá mais correções de segurança. Se você continuar usando Python 2, tenha em mente que, a partir de 2020, a linguagem se tornará insegura. Por isso, comece a planejar sua migração para Python 3.
Grafos de dependências diferentes, bibliotecas vulneráveis diferentes
A falta de compatibilidade com versões anteriores no Python 3 fez com que o pip, principal gerenciador de pacotes do Python, também precisasse criar fluxos para as versões 2.x e 3.x e registrar quais pacotes são compatíveis com cada versão. Como resultado, migrar uma aplicação muitas vezes exige mudar as bibliotecas usadas por ela ou, pelo menos, suas versões.
Essas bibliotecas, como qualquer software, não são perfeitas e, de tempos em tempos, são descobertas vulnerabilidades. Depois que elas são divulgadas, os responsáveis pelas aplicações precisam correr para corrigi-las antes que os invasores as explorem — uma corrida que, por exemplo, a Equifax perdeu feio.
As correções de vulnerabilidades muitas vezes não são portadas para todas as versões possíveis. Por isso, é fundamental incluir testes para vulnerabilidades conhecidas como parte do processo de migração. Se você continuar usando Python 2 por qualquer motivo, o risco de vulnerabilidades será ainda maior, já que cada vez mais bibliotecas estão deixando de oferecer suporte à versão antiga, inclusive para correções de vulnerabilidades.
Independentemente da sua decisão sobre qual versão do Python usar, você pode testar sua aplicação em busca de vulnerabilidades conhecidas com o Snyk usando nossa integração com o GitHub, nossa interface de linha de comando ou outras plataformas integradas.