TL;DR
Os modelos de raciocínio não são necessários para a maioria SAST , mas para casos extremos, como traversal de caminho em JavaScript, eles detectam o dobro de falsos positivos.
Os modelos de raciocínio são apenas exagero?
Os «modelos de raciocínio» estão em alta. Os grandes laboratórios de IA estão envolvidos numa batalha pela liderança, ultrapassando os limites do tamanho e desempenho dos modelos através de leis de escalabilidade, pré-treinamento mais inteligente e ajustes com RLHF (Reinforcement Learning from Human Feedback, ou Aprendizagem por Reforço a partir do Feedback Humano). Eles também estão a adicionar camadas de cadeias de pensamento para fazer com que os modelos “pensem em voz alta” durante a inferência. Isso permite que eles dominem os sistemas não baseados em IA em tarefas lógicas e, enquanto isso, liderem os rankings.
Isso é impressionante. Mas será que são realmente úteis na prática? Depende.
No caso do AutoTriage*, muito depende da complexidade da regra SAST.
*Resumo rápido - AutoTriage é um recurso que Aikido para filtrar SAST falsos positivos SAST .
**SAST é uma vulnerabilidade potencial descoberta no código-fonte, conforme relatado por um detetor de padrões codificado.
Excessivamente caro para a maioria SAST
O AutoTriage funciona em duas etapas: primeiro, tentamos descartar a possibilidade de explorabilidade. Se isso for possível, podemos filtrar um falso positivo. Isso requer um raciocínio preto no branco sobre a acessibilidade das variáveis controladas pelo utilizador nas vulnerabilidades. Basicamente, o modelo verifica se a variável é realmente controlada pelo utilizador e se há sanitização/validação/conversão em vigor. E, se houver alguma forma de mitigação, ele verifica se ela é realmente eficaz.
A segunda etapa só ocorre quando não podemos descartar a possibilidade de exploração na primeira etapa. Em seguida, vamos nos concentrar na priorização. A prioridade é definida pela probabilidade de que algo possa dar errado e pela gravidade, caso isso ocorra. Essa segunda etapa é menos clara, mas também depende de estimativas subjetivas — por exemplo, uma variável controlada pelo utilizador quando não temos o contexto completo.
Para a maioria das regras, podemos resumir como fazer isso num número razoável de «regras práticas», tornando os modelos de raciocínio exagerados: eles tendem a ter uma precisão semelhante, mas têm um preço significativamente mais alto.
Por que os modelos de raciocínio simples funcionam
Algumas regras são surpreendentemente complexas e os modelos não racionais têm dificuldade em compreendê-las. Imagine usar exatamente o mesmo espaço mental para cada palavra que você diz: inicialmente, alguém lhe pergunta: «quanto é 1 + 1?» Em seguida, essa mesma pessoa lhe pergunta «quanto é 26248 + 346237?» Enquanto os modelos normais têm dificuldade com níveis variados de complexidade, os modelos racionais conseguem lidar com eles simplesmente usando mais palavras para entradas complexas e dividindo problemas maiores em subproblemas menores e mais gerenciáveis.
Infelizmente, como consomem mais tokens, geralmente também são mais caros. No entanto, os modelos estruturados como modelos de raciocínio sofrem menos com a redução do modelo do que os modelos sem raciocínio. Há duas razões para se optar por modelos maiores: (1) eles têm mais capacidade para armazenar mais conhecimento (mas ter muito conhecimento não é realmente necessário no caso da triagem de vulnerabilidades). (2) Modelos maiores tendem a ser um pouco mais precisos por palavra. No entanto, os modelos de raciocínio podem recuperar-se de erros, graças à sua estrutura de raciocínio. Portanto, apesar de consumirem mais tokens, é viável na prática trabalhar com modelos menores com um custo por token mais baixo para compensar o maior uso de tokens.
Percurso em Javascript
O percurso de trajetória é uma regra em que os modelos de raciocínio podem realmente se destacar, pois são surpreendentemente complexos de classificar. O percurso de trajetória é uma vulnerabilidade em que os utilizadores finais podem ler ou gravar ficheiros fora de um diretório pretendido. Por exemplo, imagine que o Google Drive tivesse uma pasta dedicada a cada utilizador separadamente num sistema de ficheiros como este:
GoogleDrive/userId1/
Google Drive/userId2/…Da próxima vez que quiser fazer o download de um dos seus ficheiros, envie uma solicitação GET do seu navegador para o Google Drive, por exemplo, com o nome do ficheiro o meu cão a comer sapatos.jpg. Se esse ficheiro existir, o download começará imediatamente. Mas e se você tentar o seguinte nome de ficheiro: ../userId2/as minhas senhas.txt. Se o Google Drive não tivesse protegido o seu back-end contra o path traversal, então seria possível descarregar um «minhaspalavras-passe.txt' de outro utilizador, se esse ficheiro existir.
Diferentes ataques de traversal de caminho
Para classificar SAST de traversal de caminho, precisamos entender os diferentes casos em que algo é vulnerável ou não. Vamos começar com os casos mais simples e, gradualmente, aumentar a complexidade.
Padrão 1: «../»
O elefante na sala aqui é o padrão «../». Se ler ou escrever num caminho de ficheiro com «../», poderá escape diretório pretendido e ler/escrever em algum lugar que não pretendia. Portanto, se não houver verificação para «../» no caminho do ficheiro e o ficheiro for especificado no lado do cliente, terá uma vulnerabilidade real. Nos casos realmente graves, os hackers poderão ler ficheiros contendo credenciais no seu sistema.
Padrão 2: ‘..\\’
Imagine que verificou «../», mas o código está a ser executado num sistema Windows. Teria novamente um problema, uma vez que a travessia de caminhos ainda é possível com padrões «..\\». Até agora tudo bem, duas regras práticas para verificar ainda são fáceis de gerir, certo?
Padrão 3: «..»
Para obter caminhos bonitos e limpos, sem perder barras, muitas pessoas usam funções como path.resolve() ou path.join(). É aqui que começa a diversão. Imagine algo assim:
if (userControlledSubPath.includes(‘../’) || userControlledSubPath.includes(‘..\\’)|| filename.includes(‘../’) || filename.includes(‘..\\’))
{
throw new Error(‘Path traversal attempt detected);
}
const filepath = path.join(HARDCODED_BASE_PATH, userControlledSubPath, filename);
return fs.readFileSync(filepath);Acontece que isso ainda é vulnerável: se um invasor usar userControlledSubPath === '..', o path.join continuará a interpretá-lo como subir um diretório.
No entanto, o último argumento em path.join() é imune a esse ataque. Se um invasor especificar o '..' no último argumento, o path.join() A função retornaria um diretório em vez de um caminho de ficheiro, o que resultaria numa operação de leitura/gravação inválida.
Padrão 4: ‘/*’
Num novo exemplo, tivemos novamente um teste como este:
if (filename.includes(‘..’))
{
throw new Error(‘Path traversal attempt detected);
}
const filepath = path.resolve(HARDCODED_BASE_PATH, filename);
return fs.readFileSync(filepath);Parece seguro, certo? A verificação cobre os casos «..», «../» e «..\\» — é elegante! Agora vem a surpreendente maneira como isso ainda é vulnerável. Rufem os tambores... trrrrrrrrr... Quando um argumento em path.resolve() começa com uma barra, ele ignora todos os argumentos anteriores. Portanto, quando um invasor faz algo como filename = /etc/passwd, então path.resolve() irá ignorar o caminho base codificado e resolver para /etc/passwd. Assustador, não é? Devíamos ter verificado também se havia uma barra final. Observe que usar path.join() teria tornado seguro.
Apreciando a complexidade
Charlie Chaplin disse uma vez: «A simplicidade não é uma coisa simples». Isso também se aplica aqui: existe uma correção simples e eficaz, mas primeiro é necessário compreender a gama de possíveis vetores de ataque. A correção mais simples e robusta contra o path traversal é primeiro resolver o caminho e verificar se ele ainda começa com o caminho base pretendido. Não há como escapar dessa verificação.
No entanto, a equipa do AutoTriage não tem o luxo de poder escolher a correção. Precisamos de poder marcar soluções alternativas como seguras para não sobrecarregar desnecessariamente os clientes com falsos positivos. Já vimos quatro vetores de ataque diferentes de traversal de caminho e todos eles vêm com verificações específicas. Para cada um desses vetores de ataque, o LLM precisa verificar se é possível que ocorra com todos os requisitos para realizar um ataque bem-sucedido ou para descartar qualquer possibilidade de ataque.
Apesar de os modelos de raciocínio não serem o padrão para a maioria das regras, eles são capazes de filtrar com segurança o dobro de falsos positivos em comparação com modelos sem raciocínio para percorrer caminhos em JavaScript. Isso é uma grande mudança para redução de ruído.
Proteja seu software agora



.avif)
