ã¡ãŒã«ã¢ãã¬ã¹ãæ€èšŒããéã«ãæãå°é£ãªã·ããªãªã®1ã€ããã£ãããªãŒã«ã¡ãŒã«ãµãŒããŒã§ãããããã®ãµãŒããŒã¯ããã¡ã€ã³å ã®ããããã¢ãã¬ã¹å®ãŠã®ã¡ãŒã«ãåä¿¡ãããããæšæºç㪠SMTP æ€èšŒã§ã¯ç¹å®ã®ã¡ãŒã«ããã¯ã¹ãå®éã«ååšãããã©ããã倿ããããšãã§ããŸããããã£ãããªãŒã«ã¡ãŒã«æ€åºãçè§£ããããšã¯ãã¡ãŒã«ãªã¹ãã®å質ãç¶æããå°éçãæå€§åããããã«çå£ã«åãçµããã¹ãŠã®äººã«ãšã£ãŠäžå¯æ¬ ã§ããåºç€æŠå¿µã«ã€ããŠã¯ãã¡ãŒã«æ€èšŒå®å šã¬ã€ããã芧ãã ããã
ãã®å æ¬çãªã¬ã€ãã§ã¯ããã£ãããªãŒã«ã¡ãŒã«ã«ã€ããŠç¥ã£ãŠããã¹ããã¹ãŠãæ¢ããŸã:ããããäœã§ãããããªãååšããã®ããã©ã®ããã«æ€åºãããããããŠæãéèŠãªã®ã¯ãã¡ãŒã«æ€èšŒã¯ãŒã¯ãããŒã§ã©ã®ããã«åŠçãããã§ããã¡ãŒã«æ€èšŒã·ã¹ãã ãæ§ç¯ããéçºè ã§ãããã¡ãŒã«ãªã¹ããã¯ãªãŒã³ã¢ããããããšããããŒã±ã¿ãŒã§ããããã®ã¬ã€ãã¯ãã£ãããªãŒã«ãã¡ã€ã³ã广çã«æ±ãããã«å¿ èŠãªç¥èãšããŒã«ãæäŸããŸãã
å ç¢ãªã¡ãŒã«æ€èšŒæŠç¥ã¯ããã£ãããªãŒã«ãµãŒããŒãèæ ®ããå¿ èŠããããŸããé©åãªæ€åºãšåŠçããªããã°ãæ€èšŒçµæãã¡ãŒã«å°éçã«ã€ããŠèª€ã£ãä¿¡é Œãäžããå¯èœæ§ããããŸããæè¡çãªè©³çްãšå®çšçãªãœãªã¥ãŒã·ã§ã³ãæãäžããŠãããŸãããã
ãã£ãããªãŒã«ã¡ãŒã«ãµãŒããŒãšã¯?
ãã£ãããªãŒã«ã¡ãŒã«ãµãŒããŒ(ã¢ã¯ã»ãããªãŒã«ãµãŒããŒãšãåŒã°ããŸã)ã¯ãç¹å®ã®ã¡ãŒã«ããã¯ã¹ãååšãããã©ããã«é¢ä¿ãªãããã®ãã¡ã€ã³å®ãŠã®ãã¹ãŠã®åä¿¡ã¡ãŒã«ãåãå
¥ããããã«èšå®ãããŠããŸããanyaddress@catchall-domain.com ã«ã¡ãŒã«ãéä¿¡ãããšããanyaddressããšããååã®ã¡ãŒã«ããã¯ã¹ãäœæãããããšããªããŠãããµãŒããŒã¯ããŠã³ã¹ããã«ãããåãå
¥ããŸãã
ãã£ãããªãŒã«èšå®ã®ä»çµã¿
éåžžã®ã¡ãŒã«ãµãŒããŒèšå®ã§ã¯ãååšããªãã¡ãŒã«ããã¯ã¹å®ãŠã®ã¡ãã»ãŒãžãå°çãããšããµãŒããŒã¯ã550 User not foundããŸãã¯é¡äŒŒã®æåŠã¡ãã»ãŒãžã§å¿çããŸãããã®åäœã«ãããã¡ãŒã«æ€èšŒã·ã¹ãã ã¯ãµãŒããŒã®å¿çã確èªããããšã§ã¢ãã¬ã¹ãååšãããã©ããã倿ã§ããŸãã
ãã£ãããªãŒã«ãµãŒããŒã¯ç°ãªãåäœãããŸããåä¿¡è ã¢ãã¬ã¹ã«é¢ä¿ãªãããã¹ãŠã®åä¿¡ã¡ãŒã«ãåãå ¥ããããã«èšå®ãããŠããŸããã¡ãŒã«ã¯ãã®åŸã次ã®ããã«åŠçãããå¯èœæ§ããããŸã:
- æå®ãããã¡ãŒã«ããã¯ã¹ã«ã«ãŒãã£ã³ã° - åäžã®ç®¡çè ããã¹ãŠã®ã¡ãã»ãŒãžãåä¿¡
- äžè¬ãã¥ãŒã«ä¿å - ã¡ãã»ãŒãžã¯åŸã§ä»åãããããã«ä¿æ
- éãã«ç Žæ£ - åãå ¥ãããããé ä¿¡ãããã«åé€
- å¥ã®ã·ã¹ãã ã«è»¢é - åŠçã®ããã«å¥ã®ãµãŒããŒã«éä¿¡
ãããPostfixã¡ãŒã«ãµãŒããŒèšå®ã§ã©ã®ããã«èŠãããã®äŸã以äžã«ç€ºããŸã:
# /etc/postfix/main.cf # æšæºèšå® - æªç¥ã®åä¿¡è ãæåŠ local_recipient_maps = proxy:unix:passwd.byname $alias_maps # ãã£ãããªãŒã«èšå® - ãã¹ãŠã®åä¿¡è ãåãå ¥ãã local_recipient_maps =
çµç¹ããã£ãããªãŒã«ãµãŒããŒã䜿çšããçç±
çµç¹ããã£ãããªãŒã«ã¡ãŒã«ãèšå®ããæ£åœãªçç±ã¯ããã€ããããŸã:
1. ããžãã¹ã³ãã¥ãã±ãŒã·ã§ã³ã®çŽå€±é²æ¢
äžå°äŒæ¥ã¯ãåŸæ¥å¡åã®ã¿ã€ããã¹ãããªãšãŒã·ã§ã³ã®ããã«éèŠãªã¡ãŒã«ãèŠéãããšãå¿é
ããããšããããããŸãã誰ãã john.smith@company.com ã«ã¡ãŒã«ãéã£ãŠããå®éã®ã¢ãã¬ã¹ã jsmith@company.com ã§ããå Žåããã£ãããªãŒã«èšå®ã«ããã¡ãã»ãŒãžã倱ãããªãããšãä¿èšŒãããŸãã
2. æè»ãªã¡ãŒã«ã«ãŒãã£ã³ã°
äžéšã®çµç¹ã¯ãæŽç·Žãããã¡ãŒã«ã«ãŒãã£ã³ã°ã·ã¹ãã ã®äžéšãšããŠãã£ãããªãŒã«ã䜿çšããŸãããã¹ãŠã®åä¿¡ã¡ãŒã«ã¯äžå€®ãã¥ãŒã«éãããããã§ã«ãŒã«ã«åºã¥ããŠèªåçã«ä»åãããé ä¿¡ãããŸãã
3. ã»ãã¥ãªãã£ç£èŠ
ã»ãã¥ãªãã£ããŒã ã¯ãæ»æè ãã¹ãããŒãã©ã®ã¢ãã¬ã¹ãã¿ãŒã²ããã«ããŠããããç£èŠããããã«ãã£ãããªãŒã«ãèšå®ããããšããããŸãããã®ã€ã³ããªãžã§ã³ã¹ã¯ããã£ãã·ã³ã°è©Šè¡ãããŒã¿äŸµå®³ãç¹å®ããã®ã«åœ¹ç«ã¡ãŸãã
4. ã¬ã¬ã·ãŒã·ã¹ãã ã®äºææ§
ããã¡ãŒã«ã·ã¹ãã ããå¥ã®ã·ã¹ãã ã«ç§»è¡ããçµç¹ã¯ãç§»è¡äžã«ã¡ãã»ãŒãžã倱ãããªãããã«ããããã«äžæçã«ãã£ãããªãŒã«ãæå¹ã«ããå ŽåããããŸãã
5. ãã©ã€ãã·ãŒä¿è·
ãã©ã€ãã·ãŒãéèŠããäžéšã®çµç¹ã¯ãç»é²ããåãµãŒãã¹ã«å¯ŸããŠäžæã®ã¡ãŒã«ã¢ãã¬ã¹ãäœæããããã«ãã£ãããªãŒã«ãã¡ã€ã³ã䜿çšããã©ã®äŒæ¥ãããŒã¿ãå ±æãŸãã¯æŒæŽ©ãããã远跡ããããããŸãã
ã¡ãŒã«æ€èšŒã«ãããåé¡
ã¡ãŒã«æ€èšŒã®ç®çã§ã¯ããã£ãããªãŒã«ãµãŒããŒã¯å€§ããªèª²é¡ãæç€ºããŸãããã£ãããªãŒã«ãã¡ã€ã³ã§SMTPæ€èšŒãå®è¡ãããšããµãŒããŒã¯ãã¹ããããã¹ãŠã®ã¢ãã¬ã¹ã«å¯ŸããŠã250 OKãã®åãå ¥ãå¿çãè¿ããŸãâãããæ¬ç©ã§ããããšå®å šã«æ¶ç©ºã®ãã®ã§ããããšé¢ä¿ãããŸããã
ãã®SMTPã»ãã·ã§ã³ã®äŸãèããŠã¿ãŸããã:
> MAIL FROM:<test@verify.local> < 250 OK > RCPT TO:<real.user@catchall-domain.com> < 250 OK > RCPT TO:<completely.fake.address@catchall-domain.com> < 250 OK > RCPT TO:<asdfghjkl12345@catchall-domain.com> < 250 OK
3ã€ã®ã¢ãã¬ã¹ãã¹ãŠãåãè¯å®çãªå¿çãåãåããããSMTPæ€èšŒã ãã§ã¯å®éã®ãŠãŒã¶ãŒãšåœã®ã¢ãã¬ã¹ãåºå¥ããããšãäžå¯èœã§ãã
ãã£ãããªãŒã«ã¡ãŒã«ãµãŒããŒãæ€åºããæ¹æ³
ã¡ãŒã«ãµãŒããŒããã£ãããªãŒã«ãšããŠèšå®ãããŠãããã©ãããæ€åºããã«ã¯ãå·§åŠãªã¢ãããŒããå¿ èŠã§ã:確å®ã«ååšããªãã¯ãã®ã¢ãã¬ã¹ã§ãã¹ããããµãŒããŒã®å¿çã芳å¯ããŸãã
æ€åºã¢ã«ãŽãªãºã
åºæ¬çãªãã£ãããªãŒã«æ€åºã¢ã«ãŽãªãºã ã¯æ¬¡ã®ããã«æ©èœããŸã:
- ã¿ãŒã²ãããã¡ã€ã³ã§ã©ã³ãã ãªååšããªãã¢ãã¬ã¹ãçæ
- ãã®åœã®ã¢ãã¬ã¹ã§SMTPæ€èšŒãå®è¡
- å¿çãåæ:
- ãµãŒããŒãåœã®ã¢ãã¬ã¹ãåãå ¥ããå Žå â ãã£ãããªãŒã«ã®å¯èœæ§ãé«ã
- ãµãŒããŒãåœã®ã¢ãã¬ã¹ãæåŠããå Žå â éåžžã®æ€èšŒãé©çšããã
Node.jsã§ã®å®è£
ãã£ãããªãŒã«æ€åºã®å®å šãªNode.jså®è£ ã¯æ¬¡ã®ãšããã§ã:
const net = require('net');
const dns = require('dns').promises;
const crypto = require('crypto');
class CatchAllDetector {
constructor(options = {}) {
this.timeout = options.timeout || 10000;
this.fromEmail = options.fromEmail || 'verify@verify.local';
this.fromDomain = options.fromDomain || 'verify.local';
}
/**
* Generate a random email address that definitely doesn't exist
*/
generateRandomEmail(domain) {
const randomString = crypto.randomBytes(16).toString('hex');
const timestamp = Date.now();
return `nonexistent-${randomString}-${timestamp}@${domain}`;
}
/**
* Get the primary MX server for a domain
*/
async getMXServer(domain) {
try {
const records = await dns.resolveMx(domain);
if (!records || records.length === 0) {
return null;
}
// Sort by priority and return the primary server
records.sort((a, b) => a.priority - b.priority);
return records[0].exchange;
} catch (error) {
return null;
}
}
/**
* Perform SMTP verification on an email address
*/
async smtpVerify(email, mxServer) {
return new Promise((resolve) => {
const socket = new net.Socket();
let step = 0;
let result = { accepted: false, response: '' };
const commands = [
null, // Wait for greeting
`EHLO ${this.fromDomain}\r\n`,
`MAIL FROM:<${this.fromEmail}>\r\n`,
`RCPT TO:<${email}>\r\n`,
'QUIT\r\n'
];
socket.setTimeout(this.timeout);
socket.on('data', (data) => {
const response = data.toString();
const code = parseInt(response.substring(0, 3));
if (step === 0 && code === 220) {
socket.write(commands[1]);
step++;
} else if (step === 1 && code === 250) {
socket.write(commands[2]);
step++;
} else if (step === 2 && code === 250) {
socket.write(commands[3]);
step++;
} else if (step === 3) {
result.response = response.trim();
result.accepted = code === 250 || code === 251;
socket.write(commands[4]);
socket.destroy();
resolve(result);
} else if (code >= 400) {
result.response = response.trim();
result.accepted = false;
socket.destroy();
resolve(result);
}
});
socket.on('timeout', () => {
result.response = 'Connection timeout';
socket.destroy();
resolve(result);
});
socket.on('error', (error) => {
result.response = `Error: ${error.message}`;
socket.destroy();
resolve(result);
});
socket.connect(25, mxServer);
});
}
/**
* Detect if a domain is configured as catch-all
*/
async detectCatchAll(domain) {
// Get MX server
const mxServer = await this.getMXServer(domain);
if (!mxServer) {
return {
isCatchAll: null,
reason: 'Could not resolve MX records',
domain
};
}
// Generate a random non-existent email
const fakeEmail = this.generateRandomEmail(domain);
// Test the fake email
const result = await this.smtpVerify(fakeEmail, mxServer);
return {
isCatchAll: result.accepted,
reason: result.accepted
? 'Server accepts mail for non-existent addresses'
: 'Server rejects non-existent addresses',
domain,
mxServer,
testEmail: fakeEmail,
serverResponse: result.response
};
}
/**
* Verify an email with catch-all detection
*/
async verifyWithCatchAllDetection(email) {
const domain = email.split('@')[1];
// First, detect if domain is catch-all
const catchAllResult = await this.detectCatchAll(domain);
if (catchAllResult.isCatchAll === null) {
return {
email,
valid: null,
catchAll: null,
reason: catchAllResult.reason
};
}
// Get MX server
const mxServer = await this.getMXServer(domain);
// Verify the actual email
const verifyResult = await this.smtpVerify(email, mxServer);
return {
email,
valid: verifyResult.accepted,
catchAll: catchAllResult.isCatchAll,
reason: catchAllResult.isCatchAll
? 'Address accepted but domain is catch-all (deliverability uncertain)'
: verifyResult.accepted
? 'Address verified successfully'
: 'Address rejected by server',
serverResponse: verifyResult.response
};
}
}
// Usage example
async function main() {
const detector = new CatchAllDetector();
// Test catch-all detection
const domains = ['gmail.com', 'example.com', 'company.com'];
for (const domain of domains) {
console.log(`\nTesting domain: ${domain}`);
const result = await detector.detectCatchAll(domain);
console.log(`Is Catch-All: ${result.isCatchAll}`);
console.log(`Reason: ${result.reason}`);
if (result.serverResponse) {
console.log(`Server Response: ${result.serverResponse}`);
}
}
// Verify specific email with catch-all detection
const emailResult = await detector.verifyWithCatchAllDetection('user@example.com');
console.log('\nEmail Verification Result:');
console.log(JSON.stringify(emailResult, null, 2));
}
main().catch(console.error);
Pythonã§ã®å®è£
åçã®Pythonå®è£ ã¯æ¬¡ã®ãšããã§ã:
import socket
import dns.resolver
import secrets
import time
from dataclasses import dataclass
from typing import Optional
@dataclass
class CatchAllResult:
is_catch_all: Optional[bool]
reason: str
domain: str
mx_server: Optional[str] = None
test_email: Optional[str] = None
server_response: Optional[str] = None
@dataclass
class VerificationResult:
email: str
valid: Optional[bool]
catch_all: Optional[bool]
reason: str
server_response: Optional[str] = None
class CatchAllDetector:
def __init__(self, timeout: int = 10, from_email: str = 'verify@verify.local',
from_domain: str = 'verify.local'):
self.timeout = timeout
self.from_email = from_email
self.from_domain = from_domain
def generate_random_email(self, domain: str) -> str:
"""Generate a random email address that definitely doesn't exist."""
random_string = secrets.token_hex(16)
timestamp = int(time.time() * 1000)
return f"nonexistent-{random_string}-{timestamp}@{domain}"
def get_mx_server(self, domain: str) -> Optional[str]:
"""Get the primary MX server for a domain."""
try:
records = dns.resolver.resolve(domain, 'MX')
mx_records = sorted(
[(r.preference, str(r.exchange).rstrip('.')) for r in records],
key=lambda x: x[0]
)
return mx_records[0][1] if mx_records else None
except Exception:
return None
def smtp_verify(self, email: str, mx_server: str) -> dict:
"""Perform SMTP verification on an email address."""
result = {'accepted': False, 'response': ''}
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(self.timeout)
sock.connect((mx_server, 25))
# Receive greeting
response = sock.recv(1024).decode()
if not response.startswith('220'):
result['response'] = response.strip()
return result
# Send EHLO
sock.send(f'EHLO {self.from_domain}\r\n'.encode())
response = sock.recv(1024).decode()
if not response.startswith('250'):
result['response'] = response.strip()
return result
# Send MAIL FROM
sock.send(f'MAIL FROM:<{self.from_email}>\r\n'.encode())
response = sock.recv(1024).decode()
if not response.startswith('250'):
result['response'] = response.strip()
return result
# Send RCPT TO
sock.send(f'RCPT TO:<{email}>\r\n'.encode())
response = sock.recv(1024).decode()
result['response'] = response.strip()
code = int(response[:3])
result['accepted'] = code in (250, 251)
# Send QUIT
sock.send(b'QUIT\r\n')
sock.close()
except socket.timeout:
result['response'] = 'Connection timeout'
except socket.error as e:
result['response'] = f'Socket error: {str(e)}'
except Exception as e:
result['response'] = f'Error: {str(e)}'
return result
def detect_catch_all(self, domain: str) -> CatchAllResult:
"""Detect if a domain is configured as catch-all."""
# Get MX server
mx_server = self.get_mx_server(domain)
if not mx_server:
return CatchAllResult(
is_catch_all=None,
reason='Could not resolve MX records',
domain=domain
)
# Generate a random non-existent email
fake_email = self.generate_random_email(domain)
# Test the fake email
result = self.smtp_verify(fake_email, mx_server)
return CatchAllResult(
is_catch_all=result['accepted'],
reason='Server accepts mail for non-existent addresses' if result['accepted']
else 'Server rejects non-existent addresses',
domain=domain,
mx_server=mx_server,
test_email=fake_email,
server_response=result['response']
)
def verify_with_catch_all_detection(self, email: str) -> VerificationResult:
"""Verify an email with catch-all detection."""
domain = email.split('@')[1]
# First, detect if domain is catch-all
catch_all_result = self.detect_catch_all(domain)
if catch_all_result.is_catch_all is None:
return VerificationResult(
email=email,
valid=None,
catch_all=None,
reason=catch_all_result.reason
)
# Get MX server
mx_server = self.get_mx_server(domain)
# Verify the actual email
verify_result = self.smtp_verify(email, mx_server)
if catch_all_result.is_catch_all:
reason = 'Address accepted but domain is catch-all (deliverability uncertain)'
elif verify_result['accepted']:
reason = 'Address verified successfully'
else:
reason = 'Address rejected by server'
return VerificationResult(
email=email,
valid=verify_result['accepted'],
catch_all=catch_all_result.is_catch_all,
reason=reason,
server_response=verify_result['response']
)
# Usage example
if __name__ == '__main__':
detector = CatchAllDetector()
# Test catch-all detection
domains = ['gmail.com', 'example.com', 'company.com']
for domain in domains:
print(f"\nTesting domain: {domain}")
result = detector.detect_catch_all(domain)
print(f"Is Catch-All: {result.is_catch_all}")
print(f"Reason: {result.reason}")
if result.server_response:
print(f"Server Response: {result.server_response}")
# Verify specific email with catch-all detection
email_result = detector.verify_with_catch_all_detection('user@example.com')
print("\nEmail Verification Result:")
print(f" Email: {email_result.email}")
print(f" Valid: {email_result.valid}")
print(f" Catch-All: {email_result.catch_all}")
print(f" Reason: {email_result.reason}")
é«åºŠãªæ€åºæè¡
åºæ¬çãªãã£ãããªãŒã«æ€åºã¯ããããã®é«åºŠãªæè¡ã§æ¹åã§ããŸã:
1. è€æ°ãããŒããã¹ã
1ã€ã®åœã¢ãã¬ã¹ã ãã§ãã¹ãããã®ã§ã¯ãªããã©ã³ãã ã«çæãããè€æ°ã®ã¢ãã¬ã¹ã§ãã¹ãããŸããããã¯ãäžè²«æ§ã®ãªãåäœããããµãŒããŒãç¹å®ããã®ã«åœ¹ç«ã¡ãŸã:
async detectCatchAllAdvanced(domain, probeCount = 3) {
const results = [];
for (let i = 0; i < probeCount; i++) {
const fakeEmail = this.generateRandomEmail(domain);
const result = await this.smtpVerify(fakeEmail, await this.getMXServer(domain));
results.push(result.accepted);
// Small delay between probes to avoid rate limiting
await new Promise(resolve => setTimeout(resolve, 500));
}
// Analyze results
const acceptedCount = results.filter(r => r).length;
if (acceptedCount === probeCount) {
return { isCatchAll: true, confidence: 'high' };
} else if (acceptedCount === 0) {
return { isCatchAll: false, confidence: 'high' };
} else {
return { isCatchAll: null, confidence: 'low', note: 'Inconsistent server behavior' };
}
}
2. ãã¿ãŒã³ããŒã¹ã®æ€åº
äžéšã®ãã£ãããªãŒã«ãµãŒããŒã¯ãã¿ãŒã³ã§èšå®ãããŠããŸããç°ãªã圢åŒã®ã¢ãã¬ã¹ããã¹ãããŸã:
const testPatterns = [
`nonexistent${Date.now()}@${domain}`, // Random with timestamp
`zzz-fake-user-zzz@${domain}`, // Obvious fake pattern
`test.${crypto.randomUUID()}@${domain}`, // UUID format
`admin-backup-${Date.now()}@${domain}` // Administrative-looking
];
3. å¿çã³ãŒãåæ
远å ã®æŽå¯ã®ããã«ç¹å®ã®SMTPå¿çã³ãŒããšã¡ãã»ãŒãžãåæããŸã:
function analyzeResponse(response) {
const code = parseInt(response.substring(0, 3));
const message = response.toLowerCase();
if (code === 250) {
if (message.includes('accepted for delivery')) {
return { accepted: true, type: 'explicit_accept' };
}
return { accepted: true, type: 'standard_accept' };
}
if (code === 550) {
if (message.includes('user unknown') || message.includes('no such user')) {
return { accepted: false, type: 'user_not_found' };
}
if (message.includes('rejected') || message.includes('denied')) {
return { accepted: false, type: 'policy_rejection' };
}
}
if (code === 451 || code === 452) {
return { accepted: null, type: 'temporary_failure' };
}
return { accepted: code < 400, type: 'unknown' };
}
ãã£ãããªãŒã«ã¡ãŒã«ã®åŠçã«é¢ãããã¹ããã©ã¯ãã£ã¹
ãã£ãããªãŒã«ãã¡ã€ã³ãæ€åºããããã¡ãŒã«æ€èšŒã¯ãŒã¯ãããŒã§ãããã®ã¢ãã¬ã¹ãåŠçããããã®æŠç¥ãå¿ èŠã§ãã
æŠç¥1:ãªã¹ã¯ããŒã¹ã®åé¡
ç°ãªãä¿¡é ŒåºŠã¬ãã«ãå²ãåœãŠããªã¹ã¯åé¡ã·ã¹ãã ãå®è£ ããŸã:
function classifyEmailRisk(verificationResult) {
const { valid, catchAll, domain } = verificationResult;
if (!valid) {
return { risk: 'high', action: 'reject', reason: 'Invalid email address' };
}
if (!catchAll) {
return { risk: 'low', action: 'accept', reason: 'Verified deliverable' };
}
// Catch-all domain - assess additional risk factors
const riskFactors = [];
// Check domain age and reputation (would need external data)
// Check if domain is a known business domain
// Check email pattern (role-based, random, etc.)
const localPart = verificationResult.email.split('@')[0];
if (isRoleBasedAddress(localPart)) {
riskFactors.push('role_based');
}
if (looksRandomlyGenerated(localPart)) {
riskFactors.push('random_looking');
}
if (riskFactors.length >= 2) {
return { risk: 'high', action: 'reject', reason: 'Catch-all with multiple risk factors' };
}
if (riskFactors.length === 1) {
return { risk: 'medium', action: 'flag', reason: 'Catch-all with one risk factor' };
}
return { risk: 'medium', action: 'accept_with_caution', reason: 'Catch-all domain' };
}
function isRoleBasedAddress(localPart) {
const rolePatterns = [
'admin', 'info', 'support', 'sales', 'contact',
'help', 'webmaster', 'postmaster', 'noreply', 'no-reply'
];
return rolePatterns.some(pattern =>
localPart.toLowerCase().includes(pattern)
);
}
function looksRandomlyGenerated(localPart) {
// Check for high entropy (random-looking strings)
const consonants = localPart.match(/[bcdfghjklmnpqrstvwxyz]/gi) || [];
const vowels = localPart.match(/[aeiou]/gi) || [];
if (consonants.length > 0 && vowels.length === 0) {
return true; // No vowels suggests random
}
if (localPart.length > 20) {
return true; // Very long local parts are suspicious
}
// Check for number sequences
if (/\d{5,}/.test(localPart)) {
return true; // Long number sequences
}
return false;
}
æŠç¥2:ãšã³ã²ãŒãžã¡ã³ãããŒã¹ã®ãã£ã«ã¿ãªã³ã°
ããŒã±ãã£ã³ã°ç®çã§ã¯ããšã³ã²ãŒãžã¡ã³ãããŒã¿ã䜿çšããŠãã£ãããªãŒã«ã¢ãã¬ã¹ããã£ã«ã¿ãªã³ã°ããããšãæ€èšããŠãã ãã:
function shouldIncludeInCampaign(email, engagementData, catchAllStatus) {
// Always include if we have positive engagement history
if (engagementData.hasOpened || engagementData.hasClicked) {
return { include: true, reason: 'Previous engagement confirmed' };
}
// Non-catch-all verified emails are safe
if (!catchAllStatus.isCatchAll && catchAllStatus.verified) {
return { include: true, reason: 'Verified deliverable' };
}
// Catch-all with no engagement history - be cautious
if (catchAllStatus.isCatchAll) {
// Check if we've successfully delivered before
if (engagementData.previousDeliveries > 0 && engagementData.bounceRate < 0.1) {
return { include: true, reason: 'Previous successful deliveries' };
}
// New catch-all address with no history
return {
include: false,
reason: 'Catch-all domain with no engagement history',
recommendation: 'Send verification email first'
};
}
return { include: true, reason: 'Default include' };
}
æŠç¥3:段éçãªãŠã©ãŒãã³ã°
ãã£ãããªãŒã«ã¢ãã¬ã¹ãæ±ãéã¯ã段éçãªéä¿¡æŠç¥ãå®è£ ããŸã:
class CatchAllWarmingStrategy {
constructor() {
this.warmingGroups = {
verified: { dailyLimit: 1000, priority: 1 },
catchAllEngaged: { dailyLimit: 500, priority: 2 },
catchAllNew: { dailyLimit: 100, priority: 3 }
};
}
categorizeAddress(email, verification, engagement) {
if (!verification.catchAll) {
return 'verified';
}
if (engagement.hasInteracted) {
return 'catchAllEngaged';
}
return 'catchAllNew';
}
buildSendingQueue(emails, verifications, engagements) {
const categorized = {
verified: [],
catchAllEngaged: [],
catchAllNew: []
};
emails.forEach(email => {
const category = this.categorizeAddress(
email,
verifications[email],
engagements[email] || {}
);
categorized[category].push(email);
});
// Build queue respecting daily limits
const queue = [];
Object.entries(this.warmingGroups)
.sort((a, b) => a[1].priority - b[1].priority)
.forEach(([category, config]) => {
const addresses = categorized[category].slice(0, config.dailyLimit);
queue.push(...addresses.map(email => ({
email,
category,
priority: config.priority
})));
});
return queue;
}
}
å®éã®ã±ãŒã¹ã¹ã¿ãã£
ã±ãŒã¹ã¹ã¿ãã£1:Eã³ããŒã¹äŒæ¥ã®ãªã¹ãã¯ãªãŒãã³ã°
50äžäººã®ã¡ãŒã«è³Œèªè ãæ±ããäžèŠæš¡Eã³ããŒã¹äŒæ¥ãå°éçãæ¹åããããšèããŠããŸãããåæã®çµæã次ã®ããšãæããã«ãªããŸãã:
åæç¶æ :
- åèšè³Œèªè æ°500,000人
- ãã£ã³ããŒã³ã®ããŠã³ã¹ç12%
- ãã£ãããªãŒã«ãã¡ã€ã³äžã®ã¢ãã¬ã¹45,000ä»¶(9%)
æ€èšŒçµæ:
- æ€èšŒæžã¿é ä¿¡å¯èœ425,000ä»¶(éãã£ãããªãŒã«)
- ãã£ãããªãŒã«ã¢ãã¬ã¹ç¹å®45,000ä»¶
- ç¡å¹ãªã¢ãã¬ã¹åé€30,000ä»¶
ãã£ãããªãŒã«åŠçæŠç¥:
ãã¹ãŠã®ãã£ãããªãŒã«ã¢ãã¬ã¹ãåé€ãã代ããã«ã段éçãªã¢ãããŒããå®è£ ããŸãã:
- ãã£ã¢1 - ä¿æ: éå»ã®ãšã³ã²ãŒãžã¡ã³ã(6ã¶æä»¥å ã®éå°ãŸãã¯ã¯ãªãã¯)ããããã£ãããªãŒã«ã¢ãã¬ã¹15,000ä»¶
- ãã£ã¢2 - æ€èšŒ: åãšã³ã²ãŒãžã¡ã³ããã£ã³ããŒã³ãéä¿¡ãããã£ãããªãŒã«ã¢ãã¬ã¹20,000ä»¶
- ãã£ã¢3 - åé€: ãšã³ã²ãŒãžã¡ã³ãå±¥æŽããªãçããããã¿ãŒã³ããããã£ãããªãŒã«ã¢ãã¬ã¹10,000ä»¶
3ã¶æåŸã®çµæ:
- ããŠã³ã¹çã2.1%ã«äœäž
- éå°çã18%åäž
- éä¿¡è ã¬ãã¥ããŒã·ã§ã³ã¹ã³ã¢ãå€§å¹ ã«æ¹å
- ã¡ãŒã«å°éçã98.5%ã«éãã
ã±ãŒã¹ã¹ã¿ãã£2:B2B SaaSãªãŒãæ€èšŒ
æé10,000ä»¶ã®æ°èŠãªãŒããåãåãB2B SaaSäŒæ¥ãããµã€ã³ã¢ãããããŒã«ãã£ãããªãŒã«æ€åºãå®è£ ããŸãã:
課é¡: å€ãã®B2BãªãŒãã¯ããã£ãããªãŒã«ãšããŠèšå®ãããäŒæ¥ãã¡ã€ã³ããæ¥ãŠãããæ€èšŒãå°é£ã§ããã貎éãªãªãŒãã倱ãããšãªãããã¹ãŠã®ãã£ãããªãŒã«ã¢ãã¬ã¹ãåçŽã«æåŠããããšã¯ã§ããŸããã§ããã
ãœãªã¥ãŒã·ã§ã³:
async function validateB2BLead(email, companyInfo) {
const verification = await verifyEmail(email);
const catchAllResult = await detectCatchAll(email.split('@')[1]);
if (!verification.valid) {
return { accept: false, reason: 'Invalid email' };
}
if (!catchAllResult.isCatchAll) {
return { accept: true, reason: 'Verified deliverable', confidence: 'high' };
}
// Catch-all domain - use company info to validate
const domainMatchesCompany = email.split('@')[1].includes(
companyInfo.name.toLowerCase().replace(/\s+/g, '')
);
if (domainMatchesCompany) {
// Email domain matches company name - likely legitimate
return {
accept: true,
reason: 'Catch-all but matches company domain',
confidence: 'medium',
requireVerification: true
};
}
// Catch-all with unrelated domain
return {
accept: true,
reason: 'Catch-all domain',
confidence: 'low',
requireVerification: true,
sendDoubleOptIn: true
};
}
çµæ:
- ãªãŒãåãå ¥ãçã95%ãç¶æ
- åœéœæ§æåŠã60%åæž
- ãã£ãããªãŒã«ã®ããã«ãªããã€ã³ç¢ºèªç:72%
- å šäœçãªãªãŒãå質ã25%åäž
BillionVerifyã䜿çšãããã£ãããªãŒã«æ€åº
ç¬èªã®ãã£ãããªãŒã«æ€åºãæ§ç¯ããããšã¯å¯èœã§ãããBillionVerifyã®ãããªãããã§ãã·ã§ãã«ãªã¡ãŒã«æ€èšŒãµãŒãã¹ã䜿çšããããšã§ã倧ããªå©ç¹ããããŸã:
APIçµ±åäŸ
const axios = require('axios');
async function verifyWithBillionVerify(email) {
const response = await axios.post(
'https://api.billionverify.com/v1/verify',
{ email },
{
headers: {
'Authorization': `Bearer ${process.env.BILLIONVERIFY_API_KEY}`,
'Content-Type': 'application/json'
}
}
);
const result = response.data;
return {
email: result.email,
deliverable: result.deliverable,
isCatchAll: result.is_catch_all,
isDisposable: result.is_disposable,
isRoleBased: result.is_role_address,
qualityScore: result.quality_score,
recommendation: result.recommendation
};
}
// Bulk verification with catch-all handling
async function bulkVerifyWithStrategy(emails) {
const results = await Promise.all(
emails.map(email => verifyWithBillionVerify(email))
);
return {
safe: results.filter(r => r.deliverable && !r.isCatchAll),
catchAll: results.filter(r => r.deliverable && r.isCatchAll),
invalid: results.filter(r => !r.deliverable),
stats: {
total: results.length,
safeCount: results.filter(r => r.deliverable && !r.isCatchAll).length,
catchAllCount: results.filter(r => r.deliverable && r.isCatchAll).length,
invalidCount: results.filter(r => !r.deliverable).length
}
};
}
BillionVerifyã䜿çšããå©ç¹
ããé«ã粟床: åœç€Ÿã®ãã£ãããªãŒã«æ€åºã¯ãè€æ°ã®æ€èšŒæè¡ã䜿çšããæ¢ç¥ã®ãã£ãããªãŒã«ãã¡ã€ã³ã®åºç¯ãªããŒã¿ããŒã¹ãç¶æããŠããŸãã
远å ã®ã€ã³ããªãžã§ã³ã¹: ãã£ãããªãŒã«æ€åºã«å ããŠãäœ¿ãæšãŠã¡ãŒã«æ€åºãããŒã«ããŒã¹ã¢ãã¬ã¹èå¥ãå質ã¹ã³ã¢ãªã³ã°ãååŸã§ããŸãã
ã¬ãŒãå¶é管ç: ã¬ãŒãå¶éãšIPããŒããŒã·ã§ã³ãåŠçãããããã¯ãããããšãªãäžè²«ããæ€èšŒãä¿èšŒããŸãã
å±¥æŽããŒã¿: å±¥æŽæ€èšŒããŒã¿ãžã®ã¢ã¯ã»ã¹ã¯ããã¿ãŒã³ã®ç¹å®ãšæææ±ºå®ã®æ¹åã«åœ¹ç«ã¡ãŸãã
ãªã¢ã«ã¿ã€ã æŽæ°: ãã¡ã€ã³èšå®ã倿Žããããšããã£ãããªãŒã«ããŒã¿ããŒã¹ãç¶ç¶çã«æŽæ°ãããŸãã
ãŸãšã
ãã£ãããªãŒã«ã¡ãŒã«æ€åºã¯ãå æ¬çãªã¡ãŒã«æ€èšŒæŠç¥ã®éèŠãªã³ã³ããŒãã³ãã§ãããããã®ãµãŒããŒã¯æ€èšŒã«èª²é¡ãæç€ºããŸãããããããã©ã®ããã«æ©èœããããçè§£ããé©åãªæ€åºãšåŠçæŠç¥ãå®è£ ããããšã§ã貎éãªé£çµ¡å ã倱ãããšãªãé«ãå°éçãç¶æã§ããŸãã
ãã®ã¬ã€ãããã®éèŠãªãã€ã³ã:
- ãã£ãããªãŒã«ãµãŒããŒã¯ãã¹ãŠã®ã¡ãŒã«ãåãå ¥ãã ç¹å®ã®ã¡ãŒã«ããã¯ã¹ãååšãããã©ããã«é¢ä¿ãªã
- æ€åºã«ã¯ã確å®ã«ååšããªãã¢ãã¬ã¹ã§ã®ãã¹ããå«ãŸãã
- ãã£ãããªãŒã«ã¢ãã¬ã¹ãèªåçã«æåŠããªãâãªã¹ã¯ããŒã¹ã®æŠç¥ãå®è£ ãã
- ãšã³ã²ãŒãžã¡ã³ãããŒã¿ã䜿çšãã ãã£ãããªãŒã«é£çµ¡å ã«ã€ããŠæ å ±ã«åºã¥ããæ±ºå®ãè¡ã
- ãããã§ãã·ã§ãã«ãµãŒãã¹ãæ€èšãã æ¬çªã·ã¹ãã ã§ã¯BillionVerifyã®ãããª
ã¯ãŒã¯ãããŒã«ãã£ãããªãŒã«æ€åºãå®è£ ããæºåã¯ã§ããŸããã? åã ã®ã¢ãã¬ã¹ããã¹ãããã«ã¯ãã¡ãŒã«ãã§ãã«ãŒããŒã«ã詊ãããã¢ããªã±ãŒã·ã§ã³ãžã®ã·ãŒã ã¬ã¹ãªçµ±åã®ããã«BillionVerify APIãæ¢çŽ¢ããŠãã ããã
ãã£ãããªãŒã«ãã¡ã€ã³ãé©åã«åŠçããããšã§ãã¡ãŒã«å°éçãæ¹åããéä¿¡è ã¬ãã¥ããŒã·ã§ã³ãä¿è·ããã¡ãŒã«é£çµ¡å ã«ã€ããŠããè¯ã決å®ãäžãããšãã§ããŸãã