paint-brush
Analisando vulnerabilidades comuns introduzidas pela IA geradora de códigopor@natanjfrog
4,968 leituras
4,968 leituras

Analisando vulnerabilidades comuns introduzidas pela IA geradora de código

por Natan Nehorai11m2024/02/04
Read on Terminal Reader

Muito longo; Para ler

O código gerado automaticamente não pode ser cegamente confiável e ainda requer uma revisão de segurança para evitar a introdução de vulnerabilidades de software.
featured image - Analisando vulnerabilidades comuns introduzidas pela IA geradora de código
Natan Nehorai HackerNoon profile picture
0-item
1-item

Ferramentas de Inteligência Artificial como Bard, ChatGPT e Bing Chat são os grandes nomes atuais na categoria Large Language Model (LLM) , que está em ascensão.


Os LLMs são treinados em vastos conjuntos de dados para serem capazes de se comunicar usando a linguagem humana cotidiana como um prompt de bate-papo. Dada a flexibilidade e o potencial dos LLMs, as empresas estão a integrá-los em muitos fluxos de trabalho dentro da indústria tecnológica para tornar as nossas vidas melhores e mais fáceis. Uma das principais integrações de fluxo de trabalho de IA é um plug-in de código de preenchimento automático, que fornece a capacidade de prever e gerar padrões de código correspondentes para codificar de forma mais rápida e eficiente.


No mercado atual de ferramentas geradoras de código de IA, existem muitos participantes, incluindo GitHub Copilot , Amazon CodeWhisperer , Google Cloud Code (Duet AI) , Blackbox AI Code Generation e muito mais. Esta postagem do blog descreve exemplos de armadilhas de segurança comuns que os desenvolvedores enfrentam ao usar essas ferramentas.


O objetivo deste artigo é conscientizar e enfatizar que o código gerado automaticamente não pode ser cegamente confiável e ainda requer uma revisão de segurança para evitar a introdução de vulnerabilidades de software.


Observação: alguns fornecedores sugerem que suas ferramentas de preenchimento automático podem conter trechos de código inseguros.


Muitos artigos já enfatizaram que as ferramentas de preenchimento automático geram vulnerabilidades conhecidas, como IDOR , injeção de SQL e XSS. Esta postagem destacará outros tipos de vulnerabilidade que dependem mais do contexto do código, onde há uma grande probabilidade de o preenchimento automático da IA inserir bugs em seu código.


Ferramentas de segurança e geração de código de IA Os modelos de IA treinados para código geralmente são treinados em grandes bases de código com a missão principal de produzir código funcional.


Muitos problemas de segurança têm uma forma conhecida e definida de mitigação sem comprometer o lado do desempenho (por exemplo, injeções de SQL poderiam ser evitadas não concatenando entradas/parâmetros do usuário diretamente na consulta) - e, portanto, podem ser erradicadas, pois não há razão para escrever o código de maneira insegura.


No entanto, muitos problemas de segurança dependem do contexto e podem ser perfeitamente seguros quando implementados como funcionalidade interna, ao mesmo tempo que, quando confrontados com informações externas de um cliente, tornam-se uma vulnerabilidade e são, portanto, difíceis de distinguir através do conjunto de dados de aprendizagem da IA.


Nessas vulnerabilidades, o uso específico do código geralmente depende de outras partes do código e da finalidade geral do aplicativo.


Aqui estão alguns casos de uso comuns e exemplos que testamos, mostrando alguns desses tipos de vulnerabilidades:

  1. Caso de uso: Buscando arquivos da entrada do usuário - leitura arbitrária de arquivos
  2. Caso de uso: comparação de tokens secretos - malabarismo/coerção de tipo
  3. Caso de uso: Mecanismo de senha esquecida - Colisões de mapeamento de casos Unicode
  4. Caso de uso: Gerando arquivo de configuração - práticas de segurança inadequadas
  5. Caso de uso: Objetos de configuração – Inconsistências que levam à desserialização insegura


Depois de ver como as ferramentas de geração de código de IA lidam com os casos acima, tentamos testar sua capacidade de criar filtros de segurança adequados e outros mecanismos de mitigação.


Aqui estão alguns casos de uso comuns e exemplos que testamos solicitando deliberadamente código seguro:

  1. Caso de uso de solicitação de código seguro: leitura de arquivos - evitando passagem de diretório
  2. Solicitação de código seguro - Upload de arquivo - Lista de extensões restritas


  1. Caso de uso: Buscando arquivos da entrada do usuário - leitura arbitrária de arquivos

Muitos aplicativos incluem código que retorna um arquivo ao usuário com base em um nome de arquivo. Usar a passagem de diretório para ler arquivos arbitrários no servidor é um método comum usado por malfeitores para obter informações não autorizadas.


Pode parecer óbvio limpar a entrada do nome/caminho do arquivo proveniente do usuário antes de recuperar o arquivo, mas assumimos que é uma tarefa difícil para um modelo de IA treinado em bases de código genéricas - isso ocorre porque alguns dos conjuntos de dados não precisam usam caminhos higienizados como parte de sua funcionalidade (pode até diminuir o desempenho ou prejudicar a funcionalidade) ou não são prejudicados por caminhos não higienizados, pois destinam-se apenas para uso interno.


Sugestão de ferramenta AI (Google Cloud Code - Duet AI): Vamos tentar gerar uma função básica de busca de arquivos a partir da entrada do usuário, usando o preenchimento automático do Google Cloud Code - Duet AI. Permitiremos que o Duet AI complete automaticamente nosso código, com base em nossos nomes de funções e rotas -



Como podemos ver em cinza, a sugestão de preenchimento automático é usar a função “ send_file ” do Flask, que é a função básica de manipulação de arquivos do Flask.


Mas mesmo a documentação do Flask afirma “ Nunca passe caminhos de arquivo fornecidos por um usuário ”, o que significa que ao inserir a entrada do usuário diretamente na função “send_file”, o usuário terá a capacidade de ler qualquer arquivo no sistema de arquivos, por exemplo, usando caracteres de passagem de diretório como “../” no nome do arquivo.


Implementação adequada:


Substituir a função “send_file” pela função segura “send_from_directory” do Flask mitigará o risco de passagem de diretório. Ele só permitirá a busca de arquivos de um diretório específico codificado (“exportações” neste caso).


  1. Caso de uso: comparação de tokens secretos - malabarismo/coerção de tipos

Em algumas linguagens de programação, como PHP e NodeJS, é possível fazer comparações entre dois tipos diferentes de variáveis e o programa fará uma conversão de tipo nos bastidores para completar a ação.


Comparações vagas não verificam o tipo da variável nos bastidores e, portanto, são mais propensas a ataques de malabarismo de tipo. No entanto, o uso de comparações vagas não é considerado uma prática de codificação insegura por si só - depende do contexto do código. E, novamente, é difícil para os modelos de IA distinguir os casos.


Sugestão de ferramenta de IA (Amazon CodeWhisperer): O exemplo mais comum de malabarismo de tipo PHP são comparações soltas de tokens/hashes secretos onde a entrada do usuário que é lida por meio de uma solicitação JSON (que permite o controle do tipo de entrada) atinge uma comparação vaga (“ ” ) em vez de estrito (“ =”).


Ao que parece, o CodeWhisperer gera condições de comparação vagas em sugestões de preenchimento automático:

A comparação frouxa do secret_token permite que um adversário ignore a comparação $data[“secret_token”] == “secret_token”. Ao enviar um objeto JSON com o valor “secret_token” sendo True (booleano), o resultado da comparação flexível também é True (mesmo em versões mais recentes do PHP).



Mesmo ao “sugerir” a ferramenta (por meio de um comentário) para escrever o cheque “com segurança”, ela não gerou um trecho de código contendo uma comparação estrita -

Implementação adequada:

Somente ao especificar uma comparação estrita (“===”) estamos protegidos contra ataques de malabarismo de tipo.


  1. Caso de uso: Mecanismo de senha esquecida - Colisões de mapeamento de casos Unicode

Vulnerabilidades no mecanismo “Esqueci minha senha” são muito comuns em aplicativos SaaS e foram a causa raiz de CVEs como CVE-2017-8295 (WordPress), CVE-2019-19844 (Django) e CVE-2023-7028 (GitLab).


Especificamente, uma vulnerabilidade de colisão de mapeamento de caso Unicode ocorre quando a entrada do usuário está em letras maiúsculas ou minúsculas em uma expressão de comparação, enquanto seu valor original também é usado no código. Alguns caracteres diferentes resultarão no mesmo código de caracteres quando transformados. Por exemplo, “ß” e “SS” serão transformados em “0x00DF” quando maiúsculos.


Esses casos são geralmente usados para contornar/enganar algumas verificações de segurança, enganando a verificação de comparação e posteriormente usando o valor original que é diferente do esperado. Esses casos são bastante difíceis de compreender, especialmente quando muitas implementações são possíveis.


Sugestão de ferramenta de IA (copiloto do GitHub): um mecanismo particularmente suscetível a esse tipo de vulnerabilidade é o mecanismo de esquecimento de senha . É comum normalizar a entrada do usuário em letras maiúsculas ou minúsculas (em endereços de e-mail ou nomes de domínio) ao realizar a verificação para evitar incompatibilidades indesejadas.


Por exemplo, dar uma olhada no código gerado pelo Copilot para a função de esquecimento de senha (abaixo) mostra que ele é vulnerável a esse problema.


O código do Copilot primeiro procura o endereço de e-mail em letras minúsculas:

Esqueci a senha - Sugestão para e-mail minúsculo na consulta


Então, ao prosseguir com o resto do processo, sugere o seguinte:

Esqueci a senha - Sugestão para usar o endereço de e-mail original (sem letras minúsculas) em send_email


Como podemos ver, a sugestão de preenchimento automático gera uma senha aleatória e a envia para o e-mail do usuário. No entanto, inicialmente ele usa uma versão em letras minúsculas do e-mail do usuário para buscar a conta do usuário e, em seguida, continua a usar o e-mail do usuário original "no estado em que se encontra" (sem uma conversão em letras minúsculas) como destino do e-mail de recuperação.


Por exemplo, digamos que o invasor seja dono da caixa de entrada de “[email protected]” (o “K” é um caractere unicode do sinal Kelvin ) e use este e-mail para o mecanismo “Esqueci a senha”. Os e-mails “[email protected]” e “[email protected]não são equivalentes 'no estado em que se encontram', mas após a conversão para minúsculas serão -


Usar o endereço de e-mail “[email protected]” irá buscar as informações da conta de “[email protected]”, mas a senha redefinida será enviada para a caixa de entrada do invasor (“[email protected]”), permitindo o controle do “ conta [email protected]”.


Implementação adequada: O endereço de e-mail usado para buscar a conta do usuário deve ser exatamente igual ao endereço de e-mail de recuperação (ou seja, o parâmetro send_email também usará email.lower()).


Além disso, por precaução, recomenda-se utilizar o valor que já estava armazenado no servidor ao invés daquele fornecido pelo usuário.


  1. Caso de uso: Gerando arquivo de configuração - práticas de segurança inadequadas

Outro tópico que pode confundir as ferramentas de preenchimento automático é a gravação de arquivos de configuração, especialmente para infraestruturas complicadas em nuvem.


A principal razão é que de facto existem directrizes de melhores práticas de segurança, mas nem todos as seguem e, em alguns casos, é necessário ir contra elas. Esses exemplos de código podem chegar ao conjunto de dados de treinamento de IA. Como resultado, algumas saídas de preenchimento automático violarão as diretrizes de segurança recomendadas.


No exemplo a seguir, criaremos um arquivo de configuração para um Pod do Kubernetes , a menor unidade de execução do Kubernetes.


Sugestão de ferramenta AI (geração de código AI Blackbox): Neste exemplo, começamos a criar um arquivo de configuração do Pod.

A sugestão cria a seção de recursos e adiciona um recurso de kernel Linux (SYS_ADMIN) ao contêiner que é essencialmente equivalente a executar o pod como usuário root.


Implementação adequada: então é melhor omitir o securityContext? Não - já que muitos outros recursos serão adicionados por padrão, violando o princípio do menor privilégio .


De acordo com as melhores práticas de segurança do Docker , a configuração mais segura é primeiro descartar todos os recursos do kernel Linux e só depois adicionar aqueles necessários para o seu contêiner.


Para corrigir os problemas mencionados acima, a seção de recursos começa eliminando todos os recursos e, em seguida, adiciona gradualmente os necessários ao nosso contêiner.


  1. Caso de uso: Objetos de configuração – Inconsistências que levam à desserialização insegura

    Muitas vulnerabilidades exigem a definição de um objeto de configuração, que decide como o aplicativo tratará o componente necessário.


O exemplo que escolhemos é a biblioteca de desserialização JSON da Newtonsoft em C#, que pode ser usada de forma segura ou insegura dependendo da configuração do objeto JsonSerializerSettings e, especificamente, do TypeNameHandling .


Enquanto testamos o código de desserialização, percebemos que a definição do objeto de configuração é bastante aleatória e mudanças sutis podem levar à geração de um conjunto de configurações diferente.


Sugestões de ferramentas de IA (Amazon CodeWhisperer): os exemplos a seguir exibem um comportamento inconsistente baseado apenas nos nomes dos métodos usados pelo desenvolvedor:


Podemos ver duas configurações diferentes que resultam em desserialização JSON insegura devido à propriedade TypeNameHandling ser “Auto” e “ALL”. Essas propriedades permitem que o documento JSON defina a classe desserializada – permitindo que invasores desserializem classes arbitrárias. Isso pode ser facilmente aproveitado para execução remota de código, por exemplo, desserializando a classe “System.Diagnostics.Process”. A única diferença no código original do desenvolvedor são os nomes dos métodos.


Implementação adequada:

Por padrão, um objeto JsonSerializerSettings é instanciado com “TypeNameHandling = TypeNameHandling.None”, que é considerado seguro. Assim, usamos o construtor padrão de JsonSerializerSettings que resultará em um valor TypeNameHandling seguro.


  1. Caso de uso de solicitação de código seguro: leitura de arquivos - evitando passagem de diretório Semelhante ao primeiro caso de uso, onde examinamos o código vulnerável produzido pelo Copilot para busca de arquivos, aqui tentamos solicitar uma versão segura de um mecanismo de leitura de arquivos.


Sugestão de ferramenta de IA (copiloto do GitHub):

Podemos ver que a verificação de segurança é realmente ingênua, evitando apenas sequências ponto-ponto-barra que são necessárias para conduzir tentativas de passagem de diretório. O parâmetro path pode ser um caminho absoluto - apontando para qualquer caminho desejado no sistema (por exemplo, /etc/passwd), o que anula o propósito da verificação de segurança.


Implementação adequada:

Aqui, a função secure_file_read obtém um parâmetro de nome de arquivo (relativo) junto com um diretório (safe_dir) onde o nome do arquivo deve residir (e do qual não deve ser escapado). Ele cria um caminho absoluto juntando safe_dir e filename e prossegue para obter sua forma canônica chamando realpath. Em seguida, obtém a forma canônica da pasta safe_dir. Então, o conteúdo do arquivo será retornado somente se o caminho canônico começar com o safe_dir canônico.


  1. Solicitação de código seguro - Upload de arquivo - Lista de extensões restritas É um caso de uso comum aceitar apenas tipos de arquivo específicos para upload com base em sua extensão.


Sugestão de ferramenta de IA (geração de código de IA da Blackbox): No exemplo a seguir, pedimos ao autocompletador de IA para gerar uma função encarregada de filtrar extensões de arquivo perigosas.

O código Python sugerido pega o nome do arquivo, separa a extensão e compara-o com uma lista de extensões perigosas.


À primeira vista, este trecho de código parece seguro. No entanto, as convenções de nomes de arquivos do Windows proíbem nomes de arquivos que terminem com um ponto e, ao fornecer um nome de arquivo que termine com um ponto (“.”) durante a criação do arquivo, o ponto será descartado. Ou seja, o filtro gerado pode ser ignorado no Windows ao enviar um arquivo com uma extensão maliciosa seguida de um ponto (ex. “example.exe.”).


Implementação adequada: Normalmente, a melhor abordagem é usar uma lista de permissões , que verifica a extensão do arquivo apenas em relação às extensões permitidas. No entanto, como as ferramentas adotaram uma abordagem de lista negra (que é necessária em alguns casos), iremos delinear uma implementação de lista negra mais segura:


No trecho a seguir, eliminamos todo o controle que o usuário tinha sobre a entrada, removendo a extensão de todos os caracteres não alfanuméricos e concatenando-a com um nome de arquivo recém-gerado.


Resumindo - use com cuidado Co-programadores automáticos são ótimos para sugerir ideias, completar código automaticamente e acelerar o processo de desenvolvimento de software. Esperamos que essas ferramentas continuem evoluindo com o passar do tempo, mas por enquanto, conforme demonstrado com os casos de uso nesta postagem, as soluções de IA de preenchimento automático têm muitos desafios a serem superados até que possamos relaxar e confiar em seus resultados sem verificar novamente. insetos.


Uma forma possível de reduzir a probabilidade de introdução de vulnerabilidades é solicitar deliberadamente à ferramenta de geração de código de IA para gerar código seguro. Embora isso possa ser útil em alguns casos de uso, não é infalível – a saída “aparentemente segura” ainda deve ser revisada manualmente por alguém proficiente em segurança de software.


Recomendações da equipe de pesquisa de segurança da JFrog Para ajudar a proteger seu software, recomendamos revisar manualmente o código produzido por ferramentas geradoras de IA, bem como incorporar soluções de segurança, como Static Application Security Testing (SAST), que pode descobrir vulnerabilidades com segurança à medida que você avança. Conforme visto no exemplo acima, as ferramentas SAST podem alertar e capturar a colisão de mapeamento de caso Unicode descrita no caso de uso nº 3. Essa abordagem proativa é essencial para garantir a segurança do seu software.


Mantenha-se atualizado com a pesquisa de segurança JFrog As descobertas e pesquisas da equipe de pesquisa de segurança desempenham um papel importante na melhoria dos recursos de segurança de software de aplicação da plataforma JFrog.


Acompanhe as últimas descobertas e atualizações técnicas da equipe de pesquisa da JFrog Security em nosso site de pesquisa e no X @JFrogSecurity .