Files
avz-site/src/components/ferramentas/dnsApi.ts
T
2026-06-02 22:34:39 -03:00

374 lines
15 KiB
TypeScript

// ============================================================================
// DNS/SSL API - Consultas via APIs publicas
// ============================================================================
// Usa APIs gratuitas para consultar registros DNS, SSL, MX, SPF, DKIM, DMARC
// ============================================================================
import type { ResultadoFerramenta } from './FerramentaLayout';
// Helper para chamar a API do Google DNS
async function queryDNS(name: string, type: string): Promise<any> {
try {
const res = await fetch(`https://dns.google/resolve?name=${encodeURIComponent(name)}&type=${type}`);
return await res.json();
} catch {
return { Status: 2, Answer: [] };
}
}
// Verificar SPF
export async function checkSPF(dominio: string): Promise<ResultadoFerramenta> {
const data = await queryDNS(dominio, 'TXT');
const records = (data.Answer || []).filter((r: any) => r.data && r.data.includes('v=spf1'));
if (records.length === 0) {
return {
status: 'error',
titulo: 'SPF Nao Encontrado',
mensagem: `O dominio ${dominio} nao possui um registro SPF configurado. Sem SPF, qualquer pessoa pode enviar e-mails em nome do seu dominio, o que facilita ataques de phishing e spoofing.`,
detalhes: { Dominio: dominio, Registro: 'Nao encontrado' },
recomendacoes: [
'Crie um registro TXT no DNS com a politica SPF',
'Exemplo: v=spf1 include:_spf.google.com ~all (para Microsoft 365/Google Workspace)',
'Use o mecanismo ~all (softfail) inicialmente para testes',
'Monitore os logs antes de usar -all (hardfail)',
'Consulte a Avanzato para configuracao personalizada',
],
score: 0,
};
}
const spf = records[0].data.replace(/"/g, '');
const hasAll = spf.includes('-all') || spf.includes('~all') || spf.includes('?all');
const isHardFail = spf.includes('-all');
const includes = spf.match(/include:[^\s]+/g) || [];
const ip4 = spf.match(/ip4:[^\s]+/g) || [];
const ip6 = spf.match(/ip6:[^\s]+/g) || [];
let status: 'success' | 'warning' | 'error' = 'warning';
let score = 60;
let recomendacoes: string[] = [];
if (isHardFail) { status = 'success'; score = 90; }
else if (spf.includes('~all')) { status = 'warning'; score = 70; recomendacoes.push('Considere mudar ~all para -all para protecao mais forte'); }
else if (!hasAll) { status = 'error'; score = 30; recomendacoes.push('Adicione ~all ou -all ao final do seu SPF'); }
if (includes.length > 10) { score -= 20; recomendacoes.push('Voce tem muitos includes. Mais de 10 lookups pode quebrar o SPF'); }
return {
status,
titulo: status === 'success' ? 'SPF Configurado Corretamente' : 'SPF Configurado com Ressalvas',
mensagem: `O dominio ${dominio} possui um registro SPF ${isHardFail ? 'com protecao rigida (-all)' : hasAll ? 'mas sem protecao rigida' : 'mas sem mecanismo de falha'}.`,
detalhes: {
Dominio: dominio,
Registro: spf.substring(0, 100),
Mecanismo: isHardFail ? '-all (hardfail)' : spf.includes('~all') ? '~all (softfail)' : 'Sem all',
Includes: String(includes.length),
'IPs v4': String(ip4.length),
'IPs v6': String(ip6.length),
},
recomendacoes: recomendacoes.length > 0 ? recomendacoes : ['Seu SPF esta bem configurado!', 'Monitore regularmente para garantir que todos os servidores legitimos estejam incluidos'],
score,
};
}
// Verificar DKIM
export async function checkDKIM(dominio: string): Promise<ResultadoFerramenta> {
const selectors = ['default', 'google', 'selector1', 'selector2', 'mail', 'dkim'];
let found = false;
let validSelector = '';
let dkimRecord = '';
for (const selector of selectors) {
const data = await queryDNS(`${selector}._domainkey.${dominio}`, 'TXT');
if (data.Answer && data.Answer.length > 0) {
const record = data.Answer[0].data;
if (record.includes('v=DKIM1') || record.includes('k=rsa')) {
found = true;
validSelector = selector;
dkimRecord = record.replace(/"/g, '').substring(0, 80);
break;
}
}
}
if (!found) {
return {
status: 'warning',
titulo: 'DKIM Nao Encontrado',
mensagem: `Nao foi possivel encontrar um registro DKIM valido para ${dominio} com os selectors comuns. Isso nao significa que nao existe, mas pode indicar que nao esta configurado ou usa um selector personalizado.`,
detalhes: { Dominio: dominio, 'Selectors testados': selectors.join(', ') },
recomendacoes: [
'Verifique qual selector seu servidor de e-mail usa',
'Para Microsoft 365: selector1 e selector2',
'Para Google Workspace: google',
'Entre em contato com seu administrador de e-mail',
'A Avanzato pode configurar DKIM para seu dominio',
],
score: 30,
};
}
return {
status: 'success',
titulo: 'DKIM Configurado',
mensagem: `DKIM encontrado com o selector "${validSelector}". Seu dominio possui assinatura digital para autenticacao de e-mails.`,
detalhes: {
Dominio: dominio,
Selector: validSelector,
Registro: dkimRecord + '...',
},
recomendacoes: [
'DKIM esta configurado corretamente!',
'Certifique-se de que todos os servidores de e-mail assinam com DKIM',
'Renove as chaves DKIM periodicamente',
],
score: 85,
};
}
// Verificar DMARC
export async function checkDMARC(dominio: string): Promise<ResultadoFerramenta> {
const data = await queryDNS(`_dmarc.${dominio}`, 'TXT');
const records = (data.Answer || []).filter((r: any) => r.data && r.data.includes('v=DMARC1'));
if (records.length === 0) {
return {
status: 'error',
titulo: 'DMARC Nao Encontrado',
mensagem: `O dominio ${dominio} nao possui DMARC configurado. Sem DMARC, voce nao recebe relatorios sobre falhas de autenticacao e nao pode instruir provedores a rejeitar e-mails falsos.`,
detalhes: { Dominio: dominio, Registro: 'Nao encontrado' },
recomendacoes: [
'Crie um registro TXT em _dmarc.seudominio.com',
'Exemplo: v=DMARC1; p=quarantine; rua=mailto:dmarc@seudominio.com',
'Comece com p=none para monitorar, depois mude para quarantine ou reject',
'Configure rua para receber relatorios agregados',
'Consulte a Avanzato para configuracao completa',
],
score: 0,
};
}
const dmarc = records[0].data.replace(/"/g, '');
const policy = dmarc.match(/p=(\w+)/)?.[1] || 'none';
const rua = dmarc.match(/rua=([^;]+)/)?.[1] || 'Nao configurado';
let status: 'success' | 'warning' | 'error' = 'warning';
let score = 50;
if (policy === 'reject') { status = 'success'; score = 100; }
else if (policy === 'quarantine') { status = 'warning'; score = 75; }
else { status = 'error'; score = 40; }
return {
status,
titulo: policy === 'reject' ? 'DMARC com Protecao Maxima' : 'DMARC Configurado',
mensagem: `DMARC encontrado com politica "${policy}". ${policy === 'reject' ? 'E-mails que falham na autenticacao serao rejeitados.' : policy === 'quarantine' ? 'E-mails suspeitos vao para spam.' : 'Apenas monitorando, sem acao sobre e-mails falsos.'}`,
detalhes: {
Dominio: dominio,
Politica: policy,
'Relatorios (rua)': rua,
Registro: dmarc.substring(0, 80) + '...',
},
recomendacoes: policy === 'reject'
? ['Excelente! Sua protecao DMARC esta no nivel maximo']
: [`Considere mudar p=${policy} para p=reject para protecao total`, 'Monitore os relatorios rua antes de mudar para reject'],
score,
};
}
// MX Lookup
export async function checkMX(dominio: string): Promise<ResultadoFerramenta> {
const data = await queryDNS(dominio, 'MX');
const records = (data.Answer || []).sort((a: any, b: any) => a.preference - b.preference);
if (records.length === 0) {
return {
status: 'error',
titulo: 'Registros MX Nao Encontrados',
mensagem: `O dominio ${dominio} nao possui registros MX. Sem MX, o dominio nao pode receber e-mails.`,
detalhes: { Dominio: dominio, Status: 'Sem MX' },
recomendacoes: [
'Configure registros MX no DNS do seu dominio',
'Para Microsoft 365: siga o assistente de configuracao',
'Para Google Workspace: adicione os MX do Google',
],
score: 0,
};
}
const mxProviders = records.map((r: any) => r.data).join(', ');
const isMicrosoft = mxProviders.toLowerCase().includes('microsoft') || mxProviders.toLowerCase().includes('outlook') || mxProviders.toLowerCase().includes('protection');
const isGoogle = mxProviders.toLowerCase().includes('google') || mxProviders.toLowerCase().includes('aspmx');
const provider = isMicrosoft ? 'Microsoft 365 / Exchange Online' : isGoogle ? 'Google Workspace' : 'Outro / Servidor Proprio';
return {
status: 'success',
titulo: `${records.length} Servidor(es) MX Encontrado(s)`,
mensagem: `O dominio ${dominio} esta configurado para receber e-mails via ${provider}.`,
detalhes: {
Dominio: dominio,
'Servidores MX': String(records.length),
Provedor: provider,
'Prioridade mais alta': records[0].data,
},
recomendacoes: [
'Sua infraestrutura de e-mail esta configurada',
`Voce esta usando: ${provider}`,
'Mantenha pelo menos 2 servidores MX para redundancia',
],
score: 90,
};
}
// SSL Checker
export async function checkSSL(dominio: string): Promise<ResultadoFerramenta> {
try {
// Usar a API do crt.sh para verificar certificados
const res = await fetch(`https://crt.sh/?q=${encodeURIComponent(dominio)}&output=json`);
const data = await res.json();
if (!data || data.length === 0) {
return {
status: 'warning',
titulo: 'Certificado SSL Nao Encontrado',
mensagem: `Nao foi possivel encontrar informacoes de certificado SSL para ${dominio} no banco de dados publico. Isso pode significar que o site nao usa HTTPS ou o certificado nao e publico.`,
score: 30,
};
}
const cert = data[0];
const issuer = cert.issuer_name || 'Desconhecido';
const notAfter = cert.not_after ? new Date(cert.not_after) : null;
const diasRestantes = notAfter ? Math.floor((notAfter.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : 0;
const isExpired = diasRestantes < 0;
const isExpiringSoon = diasRestantes < 30 && diasRestantes > 0;
let status: 'success' | 'warning' | 'error' = 'success';
let score = 90;
if (isExpired) { status = 'error'; score = 0; }
else if (isExpiringSoon) { status = 'warning'; score = 50; }
return {
status,
titulo: isExpired ? 'Certificado SSL Expirado!' : isExpiringSoon ? 'Certificado SSL Vence em Breve' : 'Certificado SSL Valido',
mensagem: isExpired
? `O certificado SSL de ${dominio} EXPIROU em ${notAfter?.toLocaleDateString('pt-BR')}. Seu site esta inseguro!`
: `Certificado SSL de ${dominio} emitido por ${issuer.split('=').pop()}. ${isExpiringSoon ? `Vence em ${diasRestantes} dias.` : `Valido ate ${notAfter?.toLocaleDateString('pt-BR')}.`}`,
detalhes: {
Dominio: dominio,
Emissor: issuer.split('=').pop() || issuer,
'Valido ate': notAfter?.toLocaleDateString('pt-BR') || 'Desconhecido',
'Dias restantes': String(diasRestantes),
},
recomendacoes: isExpired
? ['RENOVE IMEDIATAMENTE seu certificado SSL', 'Use Let\'s Encrypt (gratuito) ou contrate um certificado pago', 'Entre em contato com a Avanzato para renovacao urgente']
: isExpiringSoon
? [`Renove seu certificado nos proximos ${diasRestantes} dias`, 'Configure renovacao automatica', 'Considere usar Let\'s Encrypt com auto-renew']
: ['Seu certificado SSL esta valido!', 'Configure renovacao automatica para evitar expiracao', 'Monitore a data de vencimento'],
score,
};
} catch {
return {
status: 'info',
titulo: 'Verificacao de SSL',
mensagem: `Nao foi possivel verificar o SSL de ${dominio} externamente. Verifique diretamente no navegador clicando no cadeado ao lado da URL.`,
score: 50,
};
}
}
// Email Security Score
export async function checkEmailSecurity(dominio: string): Promise<ResultadoFerramenta> {
const [spf, dkim, dmarc, mx] = await Promise.all([
checkSPF(dominio),
checkDKIM(dominio),
checkDMARC(dominio),
checkMX(dominio),
]);
const scores = [spf.score || 0, dkim.score || 0, dmarc.score || 0, mx.score || 0];
const media = Math.floor(scores.reduce((a, b) => a + b, 0) / scores.length);
let status: 'success' | 'warning' | 'error' = 'success';
if (media < 50) status = 'error';
else if (media < 80) status = 'warning';
return {
status,
titulo: `Email Security Score: ${media}/100`,
mensagem: media >= 80
? `A seguranca de e-mail de ${dominio} esta boa! Score: ${media}/100.`
: media >= 50
? `A seguranca de e-mail de ${dominio} precisa de atencao. Score: ${media}/100.`
: `A seguranca de e-mail de ${dominio} esta critica! Score: ${media}/100.`,
detalhes: {
Dominio: dominio,
'Score Total': `${media}/100`,
'SPF': `${spf.score}/100 (${spf.status === 'success' ? 'OK' : 'Problema'})`,
'DKIM': `${dkim.score}/100 (${dkim.status === 'success' ? 'OK' : 'Atenção'})`,
'DMARC': `${dmarc.score}/100 (${dmarc.status === 'success' ? 'OK' : 'Problema'})`,
'MX': `${mx.score}/100 (${mx.status === 'success' ? 'OK' : 'Problema'})`,
},
recomendacoes: [
...spf.recomendacoes || [],
...dmarc.recomendacoes || [],
...(dkim.score && dkim.score < 80 ? dkim.recomendacoes || [] : []),
].filter((v, i, a) => a.indexOf(v) === i).slice(0, 6),
score: media,
};
}
// Microsoft 365 Analyzer
export async function checkMicrosoft365(dominio: string): Promise<ResultadoFerramenta> {
const [mx, spf, dmarc, dkim] = await Promise.all([
checkMX(dominio),
checkSPF(dominio),
checkDMARC(dominio),
checkDKIM(dominio),
]);
const isMicrosoft365 = mx.detalhes?.Provedor?.includes('Microsoft');
const m365Score = isMicrosoft365 ? 40 : 0;
const spfScore = spf.score || 0;
const dmarcScore = dmarc.score || 0;
const dkimScore = dkim.score || 0;
const total = Math.min(100, m365Score + Math.floor((spfScore + dmarcScore + dkimScore) / 3 * 0.6));
let status: 'success' | 'warning' | 'error' = 'success';
if (total < 40) status = 'error';
else if (total < 70) status = 'warning';
return {
status,
titulo: `Microsoft 365 Analyzer: ${total}/100`,
mensagem: isMicrosoft365
? `O dominio ${dominio} esta configurado com Microsoft 365. Score de seguranca: ${total}/100.`
: `O dominio ${dominio} nao parece usar Microsoft 365 como servidor de e-mail principal. Score de seguranca: ${total}/100.`,
detalhes: {
Dominio: dominio,
'Usa Microsoft 365': isMicrosoft365 ? 'Sim' : 'Nao detectado',
'Score Total': `${total}/100`,
'SPF': `${spfScore}/100`,
'DKIM': `${dkimScore}/100`,
'DMARC': `${dmarcScore}/100`,
},
recomendacoes: isMicrosoft365
? [
...(total < 80 ? ['Configure DKIM corretamente para Microsoft 365'] : []),
...(dmarcScore < 80 ? ['Fortaleca a politica DMARC'] : []),
'Habilite ATP (Advanced Threat Protection) no Microsoft 365',
'Configure regras de anti-spam e anti-malware',
'Ative a autenticacao de dois fatores para todos os usuarios',
]
: [
'Se voce usa Microsoft 365, verifique se os registros MX estao corretos',
'Configure SPF com include:_spf.microsoft.com',
'Ative DKIM no portal do Microsoft 365',
'Configure DMARC para monitoramento',
],
score: total,
};
}