
Em 7 de agosto, o Facebook apresentou o Pysa, um analisador estático de código aberto voltado para a segurança que ajuda você a trabalhar com milhões de strings do Instagram. As limitações são divulgadas, as decisões de design são abordadas e, claro, os meios para ajudar a evitar falsos positivos. A situação é mostrada quando Pysa é mais útil e o código em que o analisador não é aplicável. Detalhes do blog de engenharia do Facebook sob o corte.
No ano passado, escrevemos sobre como construímos o Zoncolan , uma ferramenta de análise estática que analisa mais de 100 milhões de linhas de código de hack e ajuda os engenheiros a prevenir milhares de possíveis problemas de segurança. O sucesso inspirou o Pysa - Python Static Analyzer. O analisador é construído em cima do Pyre, a ferramenta de verificação de tipo Python do Facebook. Pysa trabalha com fluxo de dados em código. A análise do fluxo de dados é útil porque muitas vezes os problemas de segurança e privacidade são modelados como dados indo para onde não deveriam.
Pysa ajuda a identificar muitos tipos de problemas. O analisador verifica se o código usa corretamente certas estruturas internas para impedir o acesso ou divulgação de dados do usuário com base em políticas técnicas de privacidade. Além disso, o analisador detecta problemas comuns de segurança de aplicativos da web, como injeção de XSS e SQL. Como Zoncolan, a nova ferramenta ajudou a aumentar os esforços de segurança de aplicativos Python. Isso é especialmente verdadeiro para o Instagram.
Pysa no Instagram
O maior repositório Python no Facebook tem milhões de linhas em servidores Instagram. Quando o Pysa é executado em uma alteração de código sugerida pelo desenvolvedor, ele fornece resultados em cerca de uma hora, em vez das semanas ou meses que pode levar para verificar manualmente. Isso ajuda você a encontrar e prevenir o problema com rapidez suficiente para que ele não entre em sua base de código. Os resultados das verificações são enviados diretamente para o desenvolvedor ou engenheiros de segurança, dependendo do tipo de problema e da relação sinal-ruído na situação particular.
Pysa e código aberto
O código-fonte do Pysa e muitas definições de problemas estão abertos para outros desenvolvedores analisarem o código de seus projetos. Trabalhamos com estruturas do lado do servidor de código aberto, como Django e Tornado , portanto, desde o primeiro lançamento dentro do Facebook, Pysa encontra problemas de segurança em projetos que usam essas estruturas. Usar Pysa para estruturas que ainda não têm cobertura é geralmente tão fácil quanto adicionar algumas linhas de configuração. Você só precisa dizer ao analisador de onde vêm os dados para o servidor.
Pysa tem sido usado para detectar problemas como CVE-2019-19775 em projetos Python de código aberto. Também trabalhamos com o projeto Zulip e incluiu Pysa em sua base de código.
Como funciona?
Pysa foi projetado com lições aprendidas com Zoncolan. Ele usa os mesmos algoritmos para realizar análises estáticas e até mesmo compartilha código com Zoncolan. Como Zoncolan, Pysa monitora o fluxo de dados em um programa. O usuário define as fontes de dados importantes e os coletores de onde os dados vêm. Em aplicações de segurança, os tipos mais comuns de fontes são os pontos onde os dados controlados pelo usuário entram na aplicação, como o dicionário HttpRequest.GET no Django. Os receptores geralmente são muito mais variados e podem incluir a execução de APIs. Por exemplo,
eval
ouos.open
... Pysa executa iterativamente rodadas de análise para construir resumos para determinar quais funções estão retornando dados da origem e quais têm parâmetros chegando ao destino. Quando o analisador detecta que a fonte está eventualmente se conectando ao receptor, ele relata o problema. A visualização desse processo é uma árvore com um problema no topo e origens e fluxos nas folhas:

Para realizar análise de procedimento cruzado - para seguir o fluxo de dados entre chamadas de função - você precisa ser capaz de mapear chamadas de função para suas implementações. Para fazer isso, você precisa usar todas as informações disponíveis no código, incluindo tipos estáticos opcionais, se houver. Trabalhamos com Pyre para descobrir essas informações. Embora o Pysa dependa muito do Pyre e ambas as ferramentas compartilhem o mesmo repositório, é importante observar que esses são produtos separados com aplicativos separados.
Falso-positivo
Os engenheiros de segurança são os principais usuários do Pysa no Facebook. Como qualquer engenheiro que trabalha com ferramentas automatizadas de detecção de erros, tivemos que descobrir como lidar com falsos positivos (sem problema, sem sinal) e negativos (sem problema, sem sinal).
O design de Pysa visa evitar negligenciar problemas e detectar tantos problemas reais quanto possível. No entanto, reduzir o número de alarmes falsos pode exigir compensações que aumentam o número de alarmes desnecessários. Muitos falsos positivos causam fadiga de ansiedade e o risco de problemas reais serem ignorados no ruído. Pysa tem duas ferramentas para remover sinais indesejados: sanitizantes e sinais.
DesinfetanteÉ uma ferramenta simples. Diz ao analisador para não seguir o fluxo de dados depois que o fluxo passou pela função ou atributo. Os higienizadores permitem que você codifique o conhecimento da transformação do domínio que sempre apresenta os dados de maneira segura e confidencial.
Os sinais são mais sutis: eles são pequenos pedaços de metadados que Pysa anexa aos fluxos de dados enquanto os rastreia. Ao contrário dos desinfetantes, os sinais não removem os problemas dos resultados da análise. Atributos e outros metadados podem ser usados para filtrar os resultados após a análise. Os filtros são geralmente escritos para um par de origem-destino específico para ignorar problemas quando os dados já foram processados para um tipo específico (mas não todos os tipos) de um destino.
Para entender em quais situações Pysa é mais útil, imagine que o seguinte código seja executado para carregar um perfil de usuário:
# views/user.py
async def get_profile(request: HttpRequest) -> HttpResponse:
profile = load_profile(request.GET['user_id'])
...
# controller/user.py
async def load_profile(user_id: str):
user = load_user(user_id) # Loads a user safely; no SQL injection
pictures = load_pictures(user.id)
...
# model/media.py
async def load_pictures(user_id: str):
query = f"""
SELECT *
FROM pictures
WHERE user_id = {user_id}
"""
result = run_query(query)
...
# model/shared.py
async def run_query(query: str):
connection = create_sql_connection()
result = await connection.execute(query)
...
Este é o local onde a injeção de SQL potencial na load_pictures não pode ser explorada: esta função sempre fica válida
user_id
a partir de função load_user
no load_profile
. Se configurado corretamente, Pysa provavelmente não relatará um problema. Agora imagine que um engenheiro empreendedor escrevendo código no nível do controlador percebe que buscar dados do usuário e uma imagem ao mesmo tempo retorna resultados mais rápido:
# controller/user.py
async def load_profile(user_id: str):
user, pictures = await asyncio.gather(
load_user(user_id),
load_pictures(user_id) # no longer 'user.id'!
)
...
A mudança pode parecer inofensiva, mas na verdade acaba mesclando a string controlada pelo usuário
user_id
com o problema de injeção de SQL em load_pictures
. Em uma aplicação com muitas camadas entre o ponto de entrada e as consultas do banco de dados, o engenheiro pode não perceber que os dados são totalmente controlados pelo usuário ou que o problema de injeção está oculto na função chamada. Esta é exatamente a situação para a qual o analisador foi escrito. Quando um engenheiro propõe uma mudança semelhante no Instagram, Pysa descobre que os dados estão indo de uma entrada orientada pelo usuário para uma consulta SQL e relata o problema.
Limitações do analisador
É impossível escrever um analisador estático perfeito . Pysa tem limitações de escopo, fluxo de dados e decisões de design, comprometendo o desempenho para precisão e exatidão. Python, como uma linguagem dinâmica, possui características exclusivas que fundamentam algumas dessas decisões de design.
Espaço do problema
O Pysa foi criado para detectar apenas problemas de segurança relacionados a fluxos de dados. Nem todas as questões de segurança ou privacidade são modeladas como fluxos de dados. Veja um exemplo:
def admin_operation(request: HttpRequest):
if not user_is_admin():
return Http404
delete_user(request.GET["user_to_delete"])
Pysa não é a ferramenta certa para garantir que uma verificação de autorização seja
user_is_admin
executada antes de uma operação privilegiada delete_user
. O analisador pode detectar dados request.GET
direcionados para delete_user
, mas esses dados nunca passam pela validação user_is_admin
. O código pode ser reescrito para tornar o problema modelado por Pysa ou você pode criar a verificação de permissão em uma operação administrativa delete_user
. Mas este código, antes de mais nada, mostra quais problemas Pysa não resolve.
Limites de recursos
Tomamos uma decisão de design sobre as restrições para que Pysa possa concluir a análise antes que as alterações propostas pelos desenvolvedores entrem na base de código. Quando o analisador monitora fluxos de dados em muitos atributos de um objeto, às vezes é necessário simplificar e tratar o objeto inteiro como exatamente contendo esses dados. Isso pode levar a falsos positivos.
Outra limitação é o tempo de desenvolvimento. Isso forçou um compromisso sobre quais recursos Python são suportados. Pysa ainda não inclui decoradores no gráfico de chamadas ao chamar funções e, portanto, ignora problemas dentro de decoradores.
Python como uma linguagem dinâmica
A flexibilidade do Python torna a análise estática difícil. É difícil controlar os fluxos de dados por meio de chamadas de método sem informações de tipo. No código abaixo, é impossível determinar qual das implementações
fly
é chamada:
class Bird:
def fly(self): ...
class Airplane:
def fly(self): ...
def take_off(x):
x.fly() # Which function does this call?
O analisador funciona em projetos completamente não tipados. Mas é preciso pouco esforço para cobrir os tipos importantes.
A natureza dinâmica do Python impõe outra limitação. Ver abaixo:
def secret_eval(request: HttpRequest):
os = importlib.import_module("os")
# Pysa won't know what 'os' is, and thus won't
# catch this remote code execution issue
os.system(request.GET["command"])
A vulnerabilidade de execução é claramente visível aqui, mas o analisador irá ignorá-la. O módulo é
os
importado dinamicamente. Pysa não entende que a variável local os representa exatamente o módulo os
. Python permite que você importe dinamicamente quase qualquer código a qualquer momento. Além disso, a linguagem pode alterar o comportamento de uma chamada de função para quase qualquer objeto. Pysa pode aprender a analisar o sistema operacional e detectar o problema. Mas o dinamismo do Python significa que existem inúmeros exemplos de fluxos de dados patológicos que o analisador não verá.
resultados
No primeiro semestre de 2020, Pysa foi responsável por 44 por cento de todos os problemas detectados no Instagram. Entre todos os tipos de vulnerabilidade, 330 problemas exclusivos foram encontrados nas alterações de código propostas. 49 (15%) problemas revelaram-se significativos, 131 dos problemas (40%) eram reais, mas tinham circunstâncias atenuantes. Falsos negativos foram registrados em 150 (45%) casos.
Revisamos regularmente os problemas relatados de outras maneiras. Por exemplo, por meio do programa Bug Bounty. É assim que nos certificamos de corrigir todos os sinais negativos falsos. A detecção de cada tipo de vulnerabilidade é configurável. Por meio do constante refinamento, os engenheiros de segurança mudaram para tipos melhores para relatar problemas reais 100 por cento do tempo.
No geral, estamos satisfeitos com as compensações que fizemos para ajudar os engenheiros de segurança a crescer. Mas sempre há espaço para desenvolvimento. Criamos o Pysa para melhorar continuamente a qualidade do código por meio de uma estreita colaboração entre engenheiros de segurança e programadores. Isso nos permitiu iterar rapidamente e criar uma ferramenta que atenda às nossas necessidades melhor do que qualquer solução pronta para usar. A colaboração dos engenheiros levou a adições e refinamentos aos movimentos Pysa. Por exemplo, a maneira como você vê o rastreamento do problema mudou. É mais fácil ver falsos negativos agora.
Documentação e tutorial do analisador Pysa .
Descubra os detalhes de como obter uma profissão de alto perfil do zero ou Subir de nível em habilidades e salários, fazendo cursos online SkillFactory:
- «Python -» (9 )
- «Python » (2 )
- Python (10 )
E
- Machine Learning (12 )
- «Machine Learning Pro + Deep Learning» (20 )
- « Machine Learning Data Science» (20 )
- Data Science (12 )
- - (8 )
- (9 )
- DevOps (12 )
- Java- (18 )
- JavaScript (12 )