Bypass de Autenticação
admin' OR '1'='1Tenta burlar o login tornando a condição WHERE sempre verdadeira
Valores fornecidos pelo usuário nunca são interpolados no texto SQL — os valores são vinculados via placeholders de parâmetros (dependente do dialeto, ex.: $1 ou ?)
SQL injection continua sendo uma das vulnerabilidades mais críticas em aplicações web
SQL injection ocorre quando dados não confiáveis são incorporados no texto SQL sem vinculação segura de parâmetros. Atacantes podem manipular o significado da consulta para acessar dados não autorizados, modificar registros ou disparar operações não intencionais.
admin' OR '1'='1Tenta burlar o login tornando a condição WHERE sempre verdadeira
' UNION SELECT password FROM users--Usa UNION para tentar extrair dados de outras tabelas
'; DROP TABLE users; --Tenta anexar instruções extras para apagar dados
Exploit: se placeholders e params estiverem desalinhados, verificações de autorização podem usar os valores erradosExemplo: WHERE userId=$1 AND isAdmin=$2, mas params=[true, 123] em vez de [123, true] pode conceder acesso de admin indevidamente
Cada consulta passa por múltiplas camadas de validação de segurança
Todos os valores fornecidos pelo usuário são convertidos em parâmetros vinculados. Os valores não entram no texto SQL como literais.
Cada nome de campo é validado contra os metadados do schema Prisma antes da geração da consulta.
Nomes de tabelas, nomes de colunas e aliases são validados e colocados entre aspas/escapados com segurança quando necessário.
Apenas operadores conhecidos e seguros são permitidos para cada tipo de campo.
Ordenação estrita garante que cada placeholder mapeie para exatamente um parâmetro na ordem de inserção, evitando desalinhamentos.
Esboços de prova da resistência a injection sob as premissas de design declaradas (vinculação de parâmetros, sem bypass via SQL bruto, uso correto do driver)
Para todo valor V fornecido pelo usuário, não existe caminho de execução em que V apareça como texto SQL na string da consulta gerada.
Por construção, todos os caminhos de código que tratam valores do usuário chamam addParameter(value), que: 1. Armazena o valor em um array params separado 2. Retorna um token placeholder para o texto SQL 3. Apenas o token placeholder é anexado à string SQL 4. O driver do banco recebe o texto SQL e os valores dos parâmetros separadamente ∴ Valores do usuário não são interpretados como sintaxe SQL
const addParameter = (params: any[], value: any) => {
params.push(value)
const index = params.length
return `$${index}`
}O conjunto de nomes de campo F em qualquer consulta gerada é um subconjunto dos campos definidos no schema S, ou seja, F ⊆ S.
Para cada referência de campo: 1. O nome do campo é extraído do objeto da consulta 2. É feita uma busca nos metadados do schema 3. Se o campo não existe no schema, um erro é lançado 4. Apenas campos validados chegam à geração de SQL ∴ Nomes de campo arbitrários não podem aparecer no SQL
const validateField = (field: string, model: Model) => {
if (!model.fields.has(field)) {
throw new Error(`Field ${field} does not exist`)
}
return model.fields.get(field)
}Para qualquer operador O aplicado ao campo F, O pertence aos operadores permitidos para o tipo T de F.
Algoritmo de validação de operadores: 1. Extrair o tipo de campo T do schema 2. Definir allowed_ops(T) = { operadores válidos para o tipo T } 3. Para o operador O na consulta: - Se O ∉ allowed_ops(T), lançar erro - Senão, aplicar o operador usando vinculação de parâmetros ∴ Apenas operadores adequados ao tipo podem ser usados
const ALLOWED_OPS: Record<string, string[]> = {
String: ['contains', 'startsWith', 'endsWith'],
Int: ['lt', 'lte', 'gt', 'gte']
}
if (!ALLOWED_OPS[fieldType]?.includes(operator)) {
throw new Error('Invalid operator')
}Todos os identificadores SQL I são validados para impedir caracteres de controle e garantir aspas/escape seguros por dialeto.
Processamento de identificadores: 1. Rejeitar se contiver caracteres de controle 2. Escapar aspas internas conforme as regras do dialeto 3. Colocar identificadores entre aspas quando necessário (palavras reservadas ou caracteres especiais) 4. Impor limites de tamanho de identificador por dialeto ∴ Identificadores não podem quebrar a sintaxe SQL nem introduzir tokens injetados
const quoteIdentifier = (id: string) => {
if (/[\x00-\x1F]/.test(id)) {
throw new Error('Invalid characters')
}
const escaped = id.replace(/"/g, '""')
const needsQuoting = /[^a-z0-9_]/i.test(id) || isReservedKeyword(id)
return needsQuoting ? `"${escaped}"` : id
}Para cada posição de placeholder N emitida no texto SQL, params[N-1] contém exatamente o valor destinado a essa posição, sem reordenação nem desalinhamento.
Garantia de ordenação dos parâmetros: 1. Um único tracker mantém a ordem de inserção 2. Cada chamada add() anexa o valor a params e emite imediatamente o próximo token placeholder 3. A construção do texto SQL e a dos params avançam em sincronia 4. Não ocorrem operações intermediárias de reordenação ∴ A correspondência um-para-um é mantida do início ao fim
class ParameterTracker {
private params: any[] = []
add(value: any): string {
this.params.push(value)
return `$${this.params.length}`
}
getParams(): any[] {
return this.params
}
}
const tracker = new ParameterTracker()
const sql = `WHERE email = ${tracker.add(email)} AND age > ${tracker.add(age)}`
const params = tracker.getParams()A suíte atual valida a proteção contra vetores comuns de SQL injection e casos de borda
const email = req.body.email
const sql = "SELECT * FROM users WHERE email = '" + email + "'"const email = req.body.email
const { sql, params } = toSQL('User', 'findMany', {
where: { email }
})
const result = { sql, params }O rastreamento centralizado de parâmetros garante que cada valor fornecido pelo usuário seja vinculado como parâmetro, com ordem estável
class ParameterTracker {
private params: any[] = []
add(value: any): string {
this.params.push(value)
return `$${this.params.length}`
}
getParams(): any[] {
return this.params
}
}Validação baseada em schema impede acesso arbitrário a campos
function validateField(
fieldName: string,
model: ModelInfo
): FieldInfo {
const field = model.fields.get(fieldName)
if (!field) {
throw new Error(
`Field "${fieldName}" does not exist`
)
}
return field
}A validação de identificadores e o uso de aspas conforme o dialeto impedem injection de sintaxe via identificadores
function quoteIdentifier(identifier: string): string {
if (/[\x00-\x1F]/.test(identifier)) {
throw new Error('Invalid control characters')
}
const escaped = identifier.replace(/"/g, '""')
const needsQuoting = /[^a-z0-9_]/i.test(identifier) || isReservedKeyword(identifier)
if (needsQuoting) {
return `"${escaped}"`
}
return identifier
}Não contorne a validação de schema nem a checagem de campos. São camadas críticas de segurança.
Garanta que seu schema Prisma reflita com precisão a estrutura do banco.
TypeScript oferece segurança em tempo de compilação, mas validação em runtime adiciona defesa em profundidade.
Registre e monitore SQL gerado e parâmetros em produção para detectar anomalias e mau uso.
A proteção contra SQL injection está embutida em cada camada desta biblioteca. Revise os testes de segurança e detalhes de implementação diretamente.