Nesta página
- Porque é que o Rust merece uma análise de segurança a sério
- Porque é que a maioria do SAST para Rust é um teatro de correspondência de padrões
- Fluxo de dados vs correspondência de padrões, num único exemplo
- O que um ruleset para Rust com consciência de fluxo de dados deteta
- Dentro do loop do agente, não apenas no dashboard
- Perguntas frequentes
- A segurança de memória do Rust é suficiente para prevenir vulnerabilidades?
- Pode acontecer injeção de SQL em Rust com sqlx ou diesel?
- Porque é que as ferramentas de SAST para Rust produzem tantos falsos positivos?
- O que é a análise de fluxo de dados (taint)?
- A análise de fluxo de dados funciona dentro dos agentes de programação com IA?

Hoje, é Rust em produção que faz a internet funcionar, e a maioria das ferramentas de análise estática ainda o trata como uma simples caixa por assinalar. Adicionaram *.rs ao glob de ficheiros, lançaram uma lista de nomes de funções perigosas e chamaram a isso suporte para Rust. O verdadeiro trabalho, seguir a entrada não confiável através de funções assíncronas, de ligações genéricas a bases de dados e de auxiliares derivados de macros até chegar a uma consulta SQL ou a uma resposta HTML, ficou por fazer. É exatamente aí que vivem as vulnerabilidades. Eis porque a correspondência de padrões falha em Rust, e o que a análise de fluxo de dados (dataflow) deteta em alternativa.
Porque é que o Rust merece uma análise de segurança a sério
O Rust deixou de ser uma curiosidade restrita a sistemas há anos. A Cloudflare serve uma grande parte da web através do Pingora. A Discord reescreveu nele serviços críticos em desempenho. A AWS disponibiliza o Firecracker e o Bottlerocket em Rust, a Stripe usa-o em caminhos de pagamento, e a lista (Linkerd, Polkadot, Solana, Tauri) cresce todos os meses. É código que lida com entrada não confiável, com dinheiro e com credenciais em escala.
O mercado de análise estática respondeu, na sua maioria, adicionando Rust a um glob e seguindo em frente. O resultado são ferramentas que ou inundam as equipas de falsos positivos em linhas que são obviamente seguras, ou falham silenciosamente as linhas que não o são. De qualquer forma, o relatório deixa de ser confiável, o scanner é desativado no CI, e a linguagem que era suposto ser mais segura acaba com menos cobertura de segurança do que o serviço em JavaScript ao lado. A segurança de memória não é segurança de aplicação: o borrow checker não o vai impedir de concatenar entrada do utilizador numa string SQL.
Porque é que a maioria do SAST para Rust é um teatro de correspondência de padrões
A análise estática em Rust é genuinamente difícil, por razões que não aparecem no marketing dos fornecedores. Um scanner que apenas faz correspondência de padrões de texto bate em cinco paredes ao mesmo tempo.
Empilhe tudo isto e o Rust torna-se uma linguagem onde a correspondência de padrões é ruidosa onde devia ser silenciosa e silenciosa onde devia ser ruidosa. A solução não são mais padrões. É o fluxo de dados (dataflow).
Fluxo de dados vs correspondência de padrões, num único exemplo
Eis o tipo de bug que separa as duas abordagens. Usa sqlx, o padrão assíncrono moderno, e o seu QueryBuilder.
use sqlx::{PgPool, QueryBuilder, Postgres};
pub async fn search(pool: &PgPool, input: SearchInput) -> Result<Vec<Item>, sqlx::Error> {
let mut q: QueryBuilder<Postgres> =
QueryBuilder::new("SELECT * FROM items WHERE 1 = 1");
if let Some(name) = input.name {
// .push_bind() is safe (parameterized). .push() is concatenation.
// This is direct SQL injection.
q.push(" AND name LIKE '%").push(&name).push("%'");
}
q.build_query_as::<Item>().fetch_all(pool).await
}
Um scanner de padrões à procura de format! junto a sqlx::query não encontra nada aqui. Não há nenhum format!, nenhum operador de concatenação de strings, nenhum destino óbvio. A contaminação flui através de QueryBuilder::push, uma função perfeitamente segura com uma string constante e perigosa com entrada do utilizador. O scanner não consegue distinguir a diferença porque não segue o valor, por isso entrega um relatório limpo num ficheiro com uma injeção de SQL real.
Um motor de fluxo de dados rastreia a contaminação desde input.name (desserializado a partir do pedido por serde e axum) através da chamada .push(&name), reconhece QueryBuilder::push como um destino de texto SQL e reporta-o com o caminho completo. A correção é um método:
// .push_bind() keeps the value out of the SQL text.
q.push(" AND name LIKE ").push_bind(format!("%{}%", name));
Mesma forma, propriedade de segurança completamente diferente. Só uma análise que segue os dados as consegue distinguir.
O que um ruleset para Rust com consciência de fluxo de dados deteta
O ruleset para Rust da CybeDefend é construído sobre este modelo, com modelos de destino (sink) específicos por biblioteca para que a contaminação não se perca quando atravessa uma fronteira de API. Cada deteção é entregue com o caminho completo: localização da origem, cada salto intermédio e o destino.
drivers SQL rastreados: sqlx, diesel, rusqlite, tokio-postgres
frameworks web para XSS: actix-web, axum, rocket
clientes HTTP assíncronos para SSRF: reqwest, ureq, hyper, surf
- Injeção de SQL nos quatro drivers principais, seguindo a entrada do utilizador através de funções assíncronas, de tipos de ligação genéricos e de auxiliares de consulta derivados de macros, não apenas os casos óbvios de
format!. - Cross-site scripting (XSS) nas três frameworks mais usadas, incluindo HTML produzido através dos motores de templates
askamaetera, normalizado num único modelo de destino de resposta. - Má configuração de CORS, como um
Access-Control-Allow-Origincom wildcard combinado com pedidos com credenciais, ou uma origem refletida a partir do pedido sem uma allowlist, detetada no momento da construção. - Server-side request forgery (SSRF) em clientes HTTP assíncronos, com a contaminação seguida através de cada auxiliar de construção de URL e parada quando existe uma allowlist de hosts ou um filtro de IP no caminho.
O objetivo de rastrear os sanitizadores (sanitizers) com o mesmo cuidado que os destinos é que uma deteção só dispara quando o caminho perigoso é de facto alcançável sem nenhuma proteção pelo meio. É isso que mantém o relatório suficientemente confiável para o deixar ativo no CI.
Dentro do loop do agente, não apenas no dashboard
As regras para Rust não são apenas um relatório de CI. São empurradas para o seu agente de programação com IA através do VibeDefend, a camada que corre dentro do Claude Code, do Cursor, do OpenAI Codex, do Windsurf e do VS Code Copilot. Quando o agente gera Rust, o ruleset já está carregado como contexto, por isso um caminho contaminado para um dos destinos acima é reescrito antes de a linha lhe ser sequer sugerida.
Esta é a diferença entre apanhar um bug na revisão do pull request, depois de o agente ter entregado 2.000 linhas que ninguém leu de uma ponta à outra, e nunca o escrever em primeiro lugar. Não paramos o diff na revisão. Paramo-lo no prompt.
Perguntas frequentes
A segurança de memória do Rust é suficiente para prevenir vulnerabilidades?
Não. O borrow checker do Rust previne bugs de corrupção de memória (use-after-free, buffer overflows, data races), que são uma classe grande e valiosa. Não faz nada pelas vulnerabilidades ao nível da aplicação. A injeção de SQL, o XSS, a má configuração de CORS, o SSRF, o controlo de acesso quebrado e a desserialização insegura acontecem todos em Rust seguro, porque têm a ver com a forma como os dados não confiáveis fluem pela sua lógica, não com a memória. Uma linguagem segura em termos de memória continua a precisar de análise de segurança de aplicação.
Pode acontecer injeção de SQL em Rust com sqlx ou diesel?
Sim. Ambas as bibliotecas oferecem APIs seguras e parametrizadas (as macros verificadas em tempo de compilação e o .bind() do sqlx, a DSL tipada do Diesel), mas ambas também expõem escotilhas de fuga: sqlx::query(&str) e QueryBuilder::push recebem strings em tempo de execução, e diesel::sql_query corre SQL em bruto. No momento em que a entrada do utilizador é concatenada numa dessas, tem uma injeção, por mais moderno que seja o driver. O mesmo se aplica a rusqlite e a tokio-postgres.
Porque é que as ferramentas de SAST para Rust produzem tantos falsos positivos?
Porque a maioria delas faz correspondência de padrões em nomes de funções e em formas de strings em vez de seguir os dados. Os lifetimes, as cadeias assíncronas, os genéricos, as macros derivadas e os trait objects do Rust escondem o verdadeiro caminho dos dados, por isso um scanner de padrões sinaliza cada chamada que se parece com um destino (afogando a equipa em ruído) ou ignora a regra por completo (falhando bugs reais). A análise de fluxo de dados corta o ruído reportando apenas caminhos que são de facto alcançáveis a partir de uma origem não confiável sem nenhum sanitizador.
O que é a análise de fluxo de dados (taint)?
É uma análise que modela um programa como um grafo de valores e pergunta, para cada operação perigosa, se a entrada não confiável a consegue alcançar sem ser sanitizada. Os pontos de entrada são "origens", as operações perigosas são "destinos" (sinks), e um caminho limpo entre eles sem nenhuma proteção é uma deteção real e explorável. Ao contrário da correspondência de padrões, compreende que a mesma função pode ser segura ou insegura consoante os dados que a alcançam.
A análise de fluxo de dados funciona dentro dos agentes de programação com IA?
Sim. Através do VibeDefend, o ruleset para Rust é carregado no contexto do agente no momento do prompt, por isso o Claude Code, o Cursor, o Codex e os restantes aplicam-no enquanto escrevem em vez de depois. Se uma alteração gerada fosse introduzir um caminho contaminado num destino SQL, HTML ou HTTP, o agente produz a versão segura, parametrizada ou validada à primeira tentativa, antes de o diff existir. Para o panorama mais amplo de proteger cada agente, veja os nossos guias de segurança do Claude Code, do Cursor, do GitHub Copilot e do OpenAI Codex.


