SMTP ã¡ãŒã«æ€èšŒã¯ãã¡ãŒã«ã¢ãã¬ã¹ãå®éã«ã¡ãã»ãŒãžãåä¿¡ã§ãããã©ããã確èªãããŽãŒã«ãã¹ã¿ã³ããŒãã§ããåºæ¬çãªæ§ææ€èšŒããã¡ã€ã³ãã§ãã¯ãšã¯ç°ãªããSMTP æ€èšŒã¯åä¿¡è ã®ã¡ãŒã«ãµãŒããŒãšçŽæ¥éä¿¡ããŠãç¹å®ã®ã¡ãŒã«ããã¯ã¹ãååšããã¡ãŒã«ãåä¿¡ã§ããããšã確èªããŸãããã®åŒ·åãªã¡ãŒã«ããªããŒã·ã§ã³æè¡ã¯ããããã§ãã·ã§ãã«ãªã¡ãŒã«æ€èšŒãµãŒãã¹ã®åºç€ã圢æããäŒæ¥ãã¯ãªãŒã³ãªã¡ãŒã«ãªã¹ããç¶æããéä¿¡è ã¬ãã¥ããŒã·ã§ã³ãä¿è·ããã¡ãŒã«é ä¿¡æ§ãåäžãããã®ã«åœ¹ç«ã¡ãŸããåºç€çãªæŠå¿µã«ã€ããŠã¯ãã¡ãŒã«æ€èšŒã®å®å šã¬ã€ããã芧ãã ããã
SMTP ã¡ãŒã«æ€èšŒã®çè§£
SMTP(Simple Mail Transfer Protocol)ã¯ãã¡ãŒã«éä¿¡ã®ããã®ã€ã³ã¿ãŒãããæšæºã§ããã¡ãŒã«ãéä¿¡ãããã³ã«ãã¡ãŒã«ã¯ã©ã€ã¢ã³ãããµãŒããŒã¯ SMTP ã䜿çšããŠããã®ã¡ãã»ãŒãžãåä¿¡è ã®ã¡ãŒã«ãµãŒããŒã«é ä¿¡ããŸããSMTP ã¡ãŒã«æ€èšŒã¯ããã®åããããã³ã«ãå©çšããŠã¡ãŒã«ã¢ãã¬ã¹ãååšãããã©ããããã§ãã¯ããŸãããå®éã«ã¡ãã»ãŒãžãéä¿¡ããããšã¯ãããŸããã
SMTP æ€èšŒã®çŸããã¯ãã¡ãŒã«ã¢ãã¬ã¹ããœãŒã¹ã§ç¢ºèªã§ããããšã«ãããŸãã圢åŒããã¡ã€ã³ã«åºã¥ããŠã¢ãã¬ã¹ãæå¹ãã©ãããæšæž¬ããã®ã§ã¯ãªããSMTP æ€èšŒã¯ã¡ãŒã«ãµãŒããŒã«çŽæ¥å°ããŸã:ããã®ã¢ãã¬ã¹å®ã®ã¡ãŒã«ãåãå ¥ããŸãã?ããµãŒããŒã®å¿çã«ãããã¡ãŒã«ããã¯ã¹ãååšããããæºæ¯ãããŸãã¯ç¡å¹åãããŠããããæããã«ãªããŸãã
SMTP ã¡ãŒã«æ€èšŒã®ä»çµã¿
SMTP æ€èšŒããã»ã¹ã¯ãã¡ãŒã«é ä¿¡ã®éå§ãæš¡å£ããç¹å®ã®ã³ãã³ãã·ãŒã±ã³ã¹ã«åŸããŸãããå®éã«ã¡ãã»ãŒãžã³ã³ãã³ããéä¿¡ããåã«åæ¢ããŸãã以äžãã¹ããããã€ã¹ãããã®è©³çްã§ã:
ã¹ããã 1: MX ã¬ã³ãŒãã® DNS ã«ãã¯ã¢ãã
ã¡ãŒã«ãµãŒããŒã«æ¥ç¶ããåã«ãæ€èšŒããã»ã¹ã¯ãã¡ã€ã³ã®ã¡ãŒã«ãåŠçãããµãŒããŒãç¹å®ããå¿ èŠããããŸããããã«ã¯ãMail Exchange(MX)ã¬ã³ãŒãã® DNS ã¯ãšãªãå«ãŸããŸãããã¡ã€ã³ã«ã¯ç°ãªãåªå 床ãæã€è€æ°ã® MX ã¬ã³ãŒããããå Žåãããããã©ã€ããªãµãŒããŒãå©çšã§ããªãå Žåã®ãã©ãŒã«ããã¯ãå¯èœã«ããŸãã
ã¹ããã 2: TCP æ¥ç¶ã®ç¢ºç«
ã¡ãŒã«ãµãŒããŒãç¹å®ããããšãæ€èšŒããŒã«ã¯ããŒã 25(æšæº SMTP ããŒã)ããŸãã¯éä¿¡çšã®ä»£æ¿ããŒã 587 ã 465 ã§ TCP æ¥ç¶ã確ç«ããŸãã
ã¹ããã 3: SMTP ãã³ãã·ã§ã€ã¯(HELO/EHLO)
æ¥ç¶ã¯æšæ¶ããå§ãŸããŸããæ€èšŒããŒã«ã¯ EHLO(Extended HELO)ãŸã㯠HELO ã³ãã³ããéä¿¡ããŠãã¡ãŒã«ãµãŒããŒã«èªå·±ç޹ä»ããŸãããµãŒããŒã¯ãã®æ©èœã§å¿çããç¶è¡ããæºåãã§ããŠããããšã確èªããŸãã
ã¹ããã 4: MAIL FROM ã³ãã³ã
æ€èšŒããŒã«ã¯ MAIL FROM ã³ãã³ãã䜿çšããŠéä¿¡è ã¢ãã¬ã¹ãæå®ããŸãããã®ã¢ãã¬ã¹ã¯æçµçãªéä¿¡è ã§ããå¿ èŠã¯ãããŸããããMAIL FROM ãã¡ã€ã³ã«é©å㪠DNS ã¬ã³ãŒãããªãå ŽåããŸãã¯ãã¡ã€ã³ãçããããšæãããå Žåãã¡ãŒã«ãµãŒããŒã¯æ€èšŒã®è©Šã¿ãæåŠããå¯èœæ§ããããŸãã
ã¹ããã 5: RCPT TO ã³ãã³ã(éèŠãªã¹ããã)
ããã§å®éã®æ€èšŒãè¡ãããŸããæ€èšŒããŒã«ã¯ããã§ãã¯ããã¡ãŒã«ã¢ãã¬ã¹ãå«ã RCPT TO ã³ãã³ããéä¿¡ããŸãããã®ã³ãã³ãã«å¯Ÿããã¡ãŒã«ãµãŒããŒã®å¿çã¯ãã¡ãŒã«ããã¯ã¹ãååšãããã©ããã瀺ããŸã:
- 250 OK: ã¡ãŒã«ããã¯ã¹ãååšããã¡ãŒã«ãåä¿¡ã§ããŸã
- 550 User unknown: ã¡ãŒã«ããã¯ã¹ãååšããŸãã
- 551 User not local: ãµãŒããŒã¯ãŠãŒã¶ãŒãèªèããŠããŸãããå¥ã®ã¢ãã¬ã¹ãææ¡ããŠããŸã
- 552 Mailbox full: ã¡ãŒã«ããã¯ã¹ã¯ååšããŸãããã¡ãã»ãŒãžãåä¿¡ã§ããŸãã
- 553 Mailbox name not allowed: ã¢ãã¬ã¹ã®æ§æãæåŠãããŸãã
ã¹ããã 6: QUIT
RCPT TO ã¬ã¹ãã³ã¹ãåä¿¡ããåŸãæ€èšŒããŒã«ã¯ QUIT ãéä¿¡ããŠãå®éã«ã¡ãã»ãŒãžãéä¿¡ããããšãªãæ¥ç¶ãæ£åžžã«éããŸãã
SMTP ã¬ã¹ãã³ã¹ã³ãŒãã®èª¬æ
SMTP ã¬ã¹ãã³ã¹ã³ãŒããçè§£ããããšã¯ãæ£ç¢ºãªã¡ãŒã«æ€èšŒã·ã¹ãã ãæ§ç¯ããããã«äžå¯æ¬ ã§ãããããã®3æ¡ã®ã³ãŒãã¯ãæ€èšŒçµæã決å®ããç¹å®ã®æå³ãæã£ãŠããŸãã
2xx æåã³ãŒã
- 250: èŠæ±ãããã¢ã¯ã·ã§ã³ãæ£åžžã«å®äºããŸãã(ã¡ãŒã«ãååšããŸã)
- 251: ãŠãŒã¶ãŒã¯ããŒã«ã«ã§ã¯ãããŸãããæå®ããããã¹ã«è»¢éããŸã
4xx äžæçãªå€±æã³ãŒã
- 421: ãµãŒãã¹ãå©çšã§ããŸãããéä¿¡ãã£ãã«ãéããŸã
- 450: èŠæ±ãããã¡ãŒã«ã¢ã¯ã·ã§ã³ãå®è¡ãããŸããã§ãã:ã¡ãŒã«ããã¯ã¹ãå©çšã§ããŸãã(ããžãŒãŸãã¯äžæçã«ãããã¯ãããŠããŸã)
- 451: èŠæ±ãããã¢ã¯ã·ã§ã³ãäžæ¢ãããŸãã:åŠçäžã®ããŒã«ã«ãšã©ãŒ
- 452: èŠæ±ãããã¢ã¯ã·ã§ã³ãå®è¡ãããŸããã§ãã:ã·ã¹ãã ã¹ãã¬ãŒãžãäžè¶³ããŠããŸã
5xx æ°žç¶çãªå€±æã³ãŒã
- 550: èŠæ±ãããã¢ã¯ã·ã§ã³ãå®è¡ãããŸããã§ãã:ã¡ãŒã«ããã¯ã¹ãå©çšã§ããŸãã(ååšããŸãã)
- 551: ãŠãŒã¶ãŒã¯ããŒã«ã«ã§ã¯ãããŸãããå¥ã®ãã¹ã詊ããŠãã ãã
- 552: èŠæ±ãããã¡ãŒã«ã¢ã¯ã·ã§ã³ãäžæ¢ãããŸãã:ã¹ãã¬ãŒãžå²ãåœãŠãè¶ ããŸãã
- 553: èŠæ±ãããã¢ã¯ã·ã§ã³ãå®è¡ãããŸããã§ãã:ã¡ãŒã«ããã¯ã¹åãèš±å¯ãããŠããŸãã
- 554: ãã©ã³ã¶ã¯ã·ã§ã³ã倱æããŸãã
4xx ãš 5xx ã³ãŒãã®åºå¥ã¯éåžžã«éèŠã§ãã4xx ã¬ã¹ãã³ã¹ã¯äžæçãªåé¡ã瀺åããŸãâã¡ãŒã«ããã¯ã¹ã¯åŸã§å©çšå¯èœã«ãªãå¯èœæ§ããããŸãã5xx ã¬ã¹ãã³ã¹ã¯æ°žç¶çãªå€±æã瀺ããŸãâã¢ãã¬ã¹ã¯ç¡å¹ãšèŠãªãããã¹ãã§ãã
SMTP ã¡ãŒã«æ€èšŒã®å®è£
SMTP æ€èšŒã·ã¹ãã ãæ§ç¯ããã«ã¯ããããã³ã«ã®è©³çްããšã©ãŒåŠçãã¬ãŒãå¶éã«çްå¿ã®æ³šæãæãå¿ èŠããããŸãã以äžã¯å®çšçãªå®è£ ã¬ã€ãã§ãã
åºæ¬ç㪠SMTP æ€èšŒãããŒ
以äžã®æ¬äŒŒã³ãŒãã¯ãã³ã¢æ€èšŒããžãã¯ã瀺ããŠããŸã:
function verifyEmail(email):
domain = extractDomain(email)
// ã¹ããã 1: MX ã¬ã³ãŒããååŸ
mxRecords = getMXRecords(domain)
if mxRecords is empty:
return INVALID_DOMAIN
// åªå
床ã§ãœãŒã(æå°ã®æ°å€ = æé«ã®åªå
床)
sortByPriority(mxRecords)
// å MX ãµãŒããŒã詊ã
for mx in mxRecords:
try:
// ã¹ããã 2: æ¥ç¶
connection = connectSMTP(mx.host, 25)
// ã¹ããã 3: EHLO
response = sendCommand("EHLO verifier.example.com")
if response.code != 250:
continue // 次㮠MX ã詊ã
// ã¹ããã 4: MAIL FROM
response = sendCommand("MAIL FROM:<verify@example.com>")
if response.code != 250:
continue
// ã¹ããã 5: RCPT TO
response = sendCommand("RCPT TO:<" + email + ">")
// ã¹ããã 6: QUIT
sendCommand("QUIT")
closeConnection()
// çµæãè§£é
if response.code == 250:
return VALID
else if response.code >= 500:
return INVALID
else:
return UNKNOWN
catch ConnectionError:
continue // 次㮠MX ã詊ã
return UNABLE_TO_VERIFY
Node.js å®è£
以äžã¯ããã€ãã£ãã® net ãš dns ã¢ãžã¥ãŒã«ã䜿çšããå®çšç㪠Node.js å®è£
ã§ã:
const dns = require('dns');
const net = require('net');
async function verifyEmailSMTP(email) {
const domain = email.split('@')[1];
// MX ã¬ã³ãŒããååŸ
const mxRecords = await new Promise((resolve, reject) => {
dns.resolveMx(domain, (err, addresses) => {
if (err) reject(err);
else resolve(addresses.sort((a, b) => a.priority - b.priority));
});
});
if (!mxRecords || mxRecords.length === 0) {
return { valid: false, reason: 'No MX records found' };
}
// å MX ãµãŒããŒã詊ã
for (const mx of mxRecords) {
try {
const result = await checkMailbox(mx.exchange, email);
return result;
} catch (err) {
continue; // 次㮠MX ãµãŒããŒã詊ã
}
}
return { valid: null, reason: 'Unable to verify' };
}
function checkMailbox(mxHost, email) {
return new Promise((resolve, reject) => {
const socket = net.createConnection(25, mxHost);
let step = 0;
let response = '';
socket.setTimeout(10000);
socket.on('data', (data) => {
response = data.toString();
const code = parseInt(response.substring(0, 3));
switch (step) {
case 0: // ãµãŒããŒã®æšæ¶
if (code === 220) {
socket.write('EHLO verifier.example.com\r\n');
step++;
} else {
socket.end();
reject(new Error('Server rejected connection'));
}
break;
case 1: // EHLO ã¬ã¹ãã³ã¹
if (code === 250) {
socket.write('MAIL FROM:<verify@example.com>\r\n');
step++;
} else {
socket.end();
reject(new Error('EHLO failed'));
}
break;
case 2: // MAIL FROM ã¬ã¹ãã³ã¹
if (code === 250) {
socket.write(`RCPT TO:<${email}>\r\n`);
step++;
} else {
socket.end();
reject(new Error('MAIL FROM rejected'));
}
break;
case 3: // RCPT TO ã¬ã¹ãã³ã¹ - æ€èšŒçµæ
socket.write('QUIT\r\n');
socket.end();
if (code === 250) {
resolve({ valid: true, reason: 'Mailbox exists' });
} else if (code >= 500) {
resolve({ valid: false, reason: 'Mailbox does not exist', code });
} else {
resolve({ valid: null, reason: 'Unable to determine', code });
}
break;
}
});
socket.on('timeout', () => {
socket.end();
reject(new Error('Connection timeout'));
});
socket.on('error', (err) => {
reject(err);
});
});
}
Python å®è£
Python 㯠smtplib ã¢ãžã¥ãŒã«ã§ã¯ãªãŒã³ãª SMTP æ€èšŒãæäŸããŸã:
import dns.resolver
import smtplib
import socket
def verify_email_smtp(email):
domain = email.split('@')[1]
# MX ã¬ã³ãŒããååŸ
try:
mx_records = dns.resolver.resolve(domain, 'MX')
mx_hosts = sorted([(r.preference, str(r.exchange).rstrip('.'))
for r in mx_records])
except dns.resolver.NXDOMAIN:
return {'valid': False, 'reason': 'Domain does not exist'}
except dns.resolver.NoAnswer:
return {'valid': False, 'reason': 'No MX records found'}
# å MX ãµãŒããŒã詊ã
for priority, mx_host in mx_hosts:
try:
result = check_mailbox(mx_host, email)
if result['valid'] is not None:
return result
except Exception as e:
continue
return {'valid': None, 'reason': 'Unable to verify'}
def check_mailbox(mx_host, email):
try:
# SMTP ãµãŒããŒã«æ¥ç¶
smtp = smtplib.SMTP(timeout=10)
smtp.connect(mx_host, 25)
# EHLO
code, message = smtp.ehlo('verifier.example.com')
if code != 250:
smtp.quit()
return {'valid': None, 'reason': 'EHLO failed'}
# MAIL FROM
code, message = smtp.mail('verify@example.com')
if code != 250:
smtp.quit()
return {'valid': None, 'reason': 'MAIL FROM rejected'}
# RCPT TO - æ€èšŒã¹ããã
code, message = smtp.rcpt(email)
smtp.quit()
if code == 250:
return {'valid': True, 'reason': 'Mailbox exists'}
elif code >= 500:
return {'valid': False, 'reason': 'Mailbox does not exist', 'code': code}
else:
return {'valid': None, 'reason': 'Temporary failure', 'code': code}
except socket.timeout:
return {'valid': None, 'reason': 'Connection timeout'}
except smtplib.SMTPServerDisconnected:
return {'valid': None, 'reason': 'Server disconnected'}
except Exception as e:
return {'valid': None, 'reason': str(e)}
SMTP ã¡ãŒã«æ€èšŒã«ããã課é¡
SMTP æ€èšŒã¯åŒ·åã§ãããããã€ãã®èª²é¡ãå®è£ ãè€éã«ãã粟床ã«åœ±é¿ãäžããå¯èœæ§ããããŸãã
ãã£ãããªãŒã«ãã¡ã€ã³
äžéšã®ã¡ãŒã«ãµãŒããŒã¯ãã£ãããªãŒã«ãšããŠèšå®ãããŠãããç¹å®ã®ã¡ãŒã«ããã¯ã¹ãååšãããã©ããã«é¢ä¿ãªãããã®ãã¡ã€ã³ã®ä»»æã®ã¢ãã¬ã¹å®ãŠã®ã¡ãŒã«ãåãå ¥ããŸããã©ã³ãã ãªæåãå«ãä»»æã®ã¢ãã¬ã¹ã«å¯Ÿã㊠RCPT TO ãéä¿¡ããŠãããµãŒããŒã¯ 250 OK ã§å¿çããŸãã
ãã£ãããªãŒã«èšå®ã«ãããSMTP æ€èšŒã§ã¯ãã®ãã¡ã€ã³äžã®æå¹ãªã¢ãã¬ã¹ãšç¡å¹ãªã¢ãã¬ã¹ãåºå¥ã§ããªããªããŸããBillionVerify ã®ãããªãããã§ãã·ã§ãã«ãªã¡ãŒã«æ€èšŒãµãŒãã¹ã¯ããããã®ãã¡ã€ã³ãèå¥ããé©åãªä¿¡é ŒåºŠã¹ã³ã¢ãæäŸããããã®ç¹æ®ãªãã£ãããªãŒã«æ€åºã¢ã«ãŽãªãºã ãå®è£ ããŠããŸãã
ã°ã¬ã€ãªã¹ãã£ã³ã°
ã°ã¬ã€ãªã¹ãã£ã³ã°ã¯ãæªç¥ã®éä¿¡è ããã®ã¡ãŒã«ãäžæçã«æåŠããã¹ãã 察çæè¡ã§ããæåã® SMTP æ¥ç¶è©Šè¡ã¯ 4xx ã®äžæçãªãšã©ãŒãè¿ããŸããæ£åœãªã¡ãŒã«ãµãŒããŒã¯é ä¿¡ãå詊è¡ããŸãããå€ãã®ã¹ãã ã·ã¹ãã ã¯å詊è¡ããŸããã
ã¡ãŒã«æ€èšŒã®å Žåãã°ã¬ã€ãªã¹ãã£ã³ã°ã¯äžæçãªå€±æãšããŠè¡šç€ºãããŸããé©åãªå®è£ ã«ã¯æ¬¡ãå¿ èŠã§ã:
- ã°ã¬ã€ãªã¹ãã£ã³ã°ã¬ã¹ãã³ã¹ã®èªè(å€ãã®å Žå 450 ãŸã㯠451)
- é©åãªé å»¶ã䌎ãå詊è¡ããžãã¯ã®å®è£
- ã°ã¬ã€ãªã¹ãã£ã³ã°ã䜿çšãããµãŒããŒã®è¿œè·¡
ã¬ãŒãå¶éãšããããã³ã°
ã¡ãŒã«ãµãŒããŒã¯ãæ¥ç¶ãã¬ãŒãå¶éããããšã§æªçšããèªèº«ãä¿è·ããŸããåäžã® IP ã¢ãã¬ã¹ããã®çæéã§ã®éåºŠã®æ€èšŒè©Šè¡ã¯ã以äžãããªã¬ãŒããå¯èœæ§ããããŸã:
- äžæçãªãããã¯(4xx ã¬ã¹ãã³ã¹)
- æ°žç¶çãªãã©ãã¯ãªã¹ãç»é²
- æ¥ç¶ã¿ã€ã ã¢ãŠã
- CAPTCHA ããã£ã¬ã³ãž
ãããã§ãã·ã§ãã«ãªã¡ãŒã«æ€èšŒãµãŒãã¹ã¯ãå€ãã® IP ã¢ãã¬ã¹ã«æ€èšŒãªã¯ãšã¹ãã忣ãããããã®ä¿è·ãããªã¬ãŒããªãããã«é«åºŠãªã¬ãŒãå¶éãå®è£ ããŠããŸãã
åœéœæ§ãšåœé°æ§
SMTP æ€èšŒã¯ 100% æ£ç¢ºã§ã¯ãããŸãããããã€ãã®ã·ããªãªã§èª€ã£ãçµæãçæãããå¯èœæ§ããããŸã:
åœéœæ§(ç¡å¹ãæå¹ãšããŠå ±å)
- ãã¹ãŠãåãå ¥ãããã£ãããªãŒã«ãã¡ã€ã³
- SMTP äžã¯åãå ¥ãããåŸã§ããŠã³ã¹ãããµãŒããŒ
- æ¥ç¶ã¯åãå ¥ãããæºæ¯ã®ã¡ãŒã«ããã¯ã¹
åœé°æ§(æå¹ãç¡å¹ãšããŠå ±å)
- æåã®è©Šè¡ãæåŠããã°ã¬ã€ãªã¹ãã£ã³ã°
- æ£åœãªãã§ãã¯ããããã¯ããã¬ãŒãå¶é
- ãµãŒããŒã®èšå®ãã¹
- äžæçãªåæ¢
SMTP ãµãŒããŒã®ããªãšãŒã·ã§ã³
ç°ãªãã¡ãŒã«ãµãŒããŒã¯ãæ€èšŒã«åœ±é¿ãäžããããªãšãŒã·ã§ã³ãæã€ SMTP ãå®è£ ããŠããŸã:
Microsoft Exchange/Office 365
- 詳现ãªã¬ã¹ãã³ã¹ã«ã¯èªèšŒãå¿ èŠãªå Žåãå€ã
- SMTP äžã¯åãå ¥ãããåŸã§é ä¿¡ãæåŠããå Žåããã
- é«åºŠãªã¹ãã 察çãå®è£
Gmail/Google Workspace
- åãå ¥ã/æåŠã«ã€ããŠã¯äžè¬çã«ä¿¡é Œã§ãã
- ç©æ¥µçãªæ€èšŒè©Šè¡ãã¬ãŒãå¶éããå Žåããã
- äžè²«ããã¬ã¹ãã³ã¹ãè¿ã
Yahoo Mail
- 峿 Œãªã¬ãŒãå¶éã§ç¥ãããŠãã
- ãã£ã¬ã³ãžã®è§£æ±ºãå¿ èŠãªå Žåããã
- ã°ã¬ã€ãªã¹ãã£ã³ã°ãå®è£
ã«ã¹ã¿ã ã¡ãŒã«ãµãŒããŒ
- åäœã¯å€§ããç°ãªã
- éæšæºã®èšå®ãããå Žåããã
- ã»ãã¥ãªãã£èšå®ãæ€èšŒç²ŸåºŠã«åœ±é¿ãã
SMTP ã¡ãŒã«æ€èšŒã®ãã¹ããã©ã¯ãã£ã¹
ä¿¡é Œæ§ã®é«ã SMTP æ€èšŒãæ§ç¯ããã«ã¯ãå®èšŒæžã¿ã®ãã¹ããã©ã¯ãã£ã¹ã«åŸãå¿ èŠããããŸãã
é©å㪠EHLO/HELO èšå®
EHLO ãã¹ãåã¯ä»¥äžãæºããå¿ èŠããããŸã:
- æ€èšŒãµãŒããŒã® IP ã«è§£æ±ºããã
- æå¹ãªéåŒã DNS(PTR ã¬ã³ãŒã)ãæã€
- ãã©ãã¯ãªã¹ãã«èŒã£ãŠããªã
- 管çããæ£åœãªãã¡ã€ã³ã§ãã
EHLO verify.yourdomain.com
ã¹ãã ãã£ã«ã¿ãŒãããªã¬ãŒããäžè¬çãŸãã¯çããããã¹ãåã¯é¿ããŠãã ããã
MAIL FROM ã¢ãã¬ã¹ã®éžæ
MAIL FROM ã¢ãã¬ã¹ã¯æ€èšŒã®åãå ¥ãã«éèŠã§ã:
- æå¹ãª MX ã¬ã³ãŒããæã€å®éã®ãã¡ã€ã³ã䜿çšãã
- SPF ã¬ã³ãŒããæ€èšŒãµãŒããŒãèš±å¯ããŠããããšã確èªãã
- æ€èšŒå°çšã®ãã¡ã€ã³ã®äœ¿çšãæ€èšãã
- æ¢ç¥ã®ã¹ãã ãã©ãããã¡ã€ã³ãé¿ãã
æ¥ç¶ç®¡ç
å¹ççãªæ¥ç¶ç®¡çã«ãããæ€èšŒé床ãšä¿¡é Œæ§ãåäžããŸã:
// æ¥ç¶ããŒãªã³ã°ã®äŸ
class SMTPConnectionPool {
constructor(maxConnections = 10) {
this.pools = new Map(); // ãã¡ã€ã³ -> æ¥ç¶
this.maxConnections = maxConnections;
}
async getConnection(mxHost) {
if (!this.pools.has(mxHost)) {
this.pools.set(mxHost, []);
}
const pool = this.pools.get(mxHost);
// å©çšå¯èœãªå Žåã¯æ¢åã®æ¥ç¶ãåå©çš
if (pool.length > 0) {
return pool.pop();
}
// æ°ããæ¥ç¶ãäœæ
return await this.createConnection(mxHost);
}
releaseConnection(mxHost, connection) {
const pool = this.pools.get(mxHost);
if (pool && pool.length < this.maxConnections) {
pool.push(connection);
} else {
connection.end();
}
}
}
ã¿ã€ã ã¢ãŠãèšå®
å¿çããªããµãŒããŒã§ãã³ã°ããã®ãé¿ããããã«ãé©åãªã¿ã€ã ã¢ãŠããèšå®ããŸã:
const TIMEOUT_CONFIG = {
connection: 10000, // æ¥ç¶ç¢ºç«ã« 10 ç§
greeting: 30000, // ãµãŒããŒæšæ¶ã« 30 ç§
command: 30000, // ã³ãã³ãã¬ã¹ãã³ã¹ããšã« 30 ç§
total: 60000 // æ€èšŒããšã«æå€§ 60 ç§
};
ãšã©ãŒåŠçãšå詊è¡ããžãã¯
ã€ã³ããªãžã§ã³ããªå詊è¡ã䌎ãå ç¢ãªãšã©ãŒåŠçãå®è£ ããŸã:
async function verifyWithRetry(email, maxRetries = 3) {
const delays = [1000, 5000, 15000]; // ææ°ããã¯ãªã
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const result = await verifyEmailSMTP(email);
// æç¢ºãªçããåŸãããå Žåã¯å詊è¡ããªã
if (result.valid !== null) {
return result;
}
// ãšã©ãŒãå詊è¡å¯èœããã§ãã¯
if (isGreylisting(result) || isTemporaryError(result)) {
await sleep(delays[attempt]);
continue;
}
return result;
} catch (err) {
if (attempt === maxRetries - 1) {
return { valid: null, reason: err.message };
}
await sleep(delays[attempt]);
}
}
}
function isGreylisting(result) {
return result.code === 450 || result.code === 451;
}
function isTemporaryError(result) {
return result.code >= 400 && result.code < 500;
}
ã¬ãŒãå¶éã®å®è£
æ€èšŒã€ã³ãã©ã¹ãã©ã¯ãã£ãä¿è·ããè¯å¥œãªè©å€ãç¶æããŸã:
class RateLimiter {
constructor() {
this.domainLimits = new Map();
this.globalCounter = 0;
this.globalLimit = 100; // ç§ããã
this.domainLimit = 10; // ãã¡ã€ã³ããšç§ããã
}
async waitForSlot(domain) {
// ã°ããŒãã«å¶éããã§ãã¯
while (this.globalCounter >= this.globalLimit) {
await sleep(100);
}
// ãã¡ã€ã³å¶éããã§ãã¯
const domainCount = this.domainLimits.get(domain) || 0;
while (domainCount >= this.domainLimit) {
await sleep(100);
}
// ã¹ããããäºçŽ
this.globalCounter++;
this.domainLimits.set(domain, domainCount + 1);
// 1ç§åŸã«è§£æŸ
setTimeout(() => {
this.globalCounter--;
this.domainLimits.set(domain,
(this.domainLimits.get(domain) || 1) - 1);
}, 1000);
}
}
SMTP æ€èšŒãšä»ã®æ¹æ³ã®æ¯èŒ
SMTP æ€èšŒãä»ã®ã¡ãŒã«ããªããŒã·ã§ã³æè¡ãšã©ã®ããã«æ¯èŒãããããçè§£ããããšã§ãé©åãªã¢ãããŒããéžæã§ããŸãã
æ§ææ€èšŒ
æ§ææ€èšŒã¯ãæ£èŠè¡šçŸãã¿ãŒã³ã䜿çšããŠã¡ãŒã«ã¢ãã¬ã¹ãæ£ãã圢åŒã«åŸã£ãŠããããã§ãã¯ããŸããé«éã§ã¯ã©ã€ã¢ã³ãåŽã§å®è¡ã§ããŸãããæãããªåœ¢åŒãšã©ãŒã®ã¿ããã£ããããŸãã
匷ã¿:
- å³åº§ã®çµæ
- ãããã¯ãŒã¯ãªã¯ãšã¹ãäžèŠ
- ã¿ã€ããã¹ããã£ãã
å¶é:
- ååšã確èªã§ããªã
- å€ãã®ç¡å¹ãªã¡ãŒã«ãæ§æãã§ãã¯ãéé
ãã¡ã€ã³/MX æ€èšŒ
MX ã¬ã³ãŒãæ€èšŒã¯ãã¡ãŒã«ãµãŒããŒã¬ã³ãŒãããã§ãã¯ããããšã§ãã¡ã€ã³ãã¡ãŒã«ãåä¿¡ã§ããããšã確èªããŸãã
匷ã¿:
- ååšããªããã¡ã€ã³ããã£ãã
- é«é㪠DNS ã«ãã¯ã¢ãã
- SMTP æ¥ç¶äžèŠ
å¶é:
- ç¹å®ã®ã¡ãŒã«ããã¯ã¹ã確èªã§ããªã
- ãã¡ã€ã³ã« MX ããã£ãŠãæå¹ãªãŠãŒã¶ãŒãããªãå Žåããã
SMTP æ€èšŒ
SMTP æ€èšŒã¯ãç¹å®ã®ã¡ãŒã«ããã¯ã¹ãååšããã¡ãŒã«ãåä¿¡ã§ããããšã確èªããŸãã
匷ã¿:
- ã¡ãŒã«ããã¯ã¹ã®ååšã«å¯Ÿããæé«ã®ç²ŸåºŠ
- ã¡ãŒã«ãµãŒããŒãšã®çŽæ¥éä¿¡
- å€ãã®ç¡å¹ãªã¢ãã¬ã¹ããã£ãã
å¶é:
- ä»ã®æ¹æ³ããé ã
- ãã£ãããªãŒã«ãã¡ã€ã³ã®åœ±é¿ãåãã
- ã¬ãŒãå¶éã«ãããããã¯ãããå¯èœæ§
æ€èšŒéå±€
å æ¬çãªã¡ãŒã«æ€èšŒæŠç¥ã¯ããããã®æ¹æ³ãéå±€åããŸã:
- æ§ææ€èšŒ - æããã«ç¡å¹ãªåœ¢åŒããã£ã«ã¿ãªã³ã°
- ãã¡ã€ã³æ€èšŒ - ãã¡ã€ã³ãååšã MX ã¬ã³ãŒããæã€ããšã確èª
- SMTP æ€èšŒ - ç¹å®ã®ã¡ãŒã«ããã¯ã¹ã確èª
- 远å ãã§ã㯠- äœ¿ãæšãŠã¡ãŒã«æ€åºãããŒã«ããŒã¹æ€åºããã£ãããªãŒã«æ€åº
BillionVerify ã®ãããªãããã§ãã·ã§ãã«ãªã¡ãŒã«æ€èšŒãµãŒãã¹ã¯ããã®å®å šãªéå±€ãå®è£ ããSMTP æ€èšŒã®è€éããåŠçããªãããã¡ãŒã«å質ã«é¢ãã远å ã®ã€ã³ããªãžã§ã³ã¹ãæäŸããŸãã
ãããã§ãã·ã§ãã«ãª SMTP æ€èšŒãµãŒãã¹ã®äœ¿çš
ç¬èªã® SMTP æ€èšŒã·ã¹ãã ãæ§ç¯ããããšã¯æè²çã§ãããæ¬çªã¢ããªã±ãŒã·ã§ã³ã¯è€éããåŠçãããããã§ãã·ã§ãã«ãªã¡ãŒã«æ€èšŒ API ããæ©æµãåããããšããããããŸãã
ãããã§ãã·ã§ãã«ãµãŒãã¹ã®å©ç¹
ã€ã³ãã©ã¹ãã©ã¯ãã£
- äžçäžã«åæ£ãããæ€èšŒãµãŒããŒ
- ã¯ãªãŒã³ãª IP ã¬ãã¥ããŒã·ã§ã³ç®¡ç
- é«å¯çšæ§ãšåé·æ§
ã€ã³ããªãžã§ã³ã¹
- ãã£ãããªãŒã«ãã¡ã€ã³æ€åº
- äœ¿ãæšãŠã¡ãŒã«èå¥
- ããŒã«ããŒã¹ã¢ãã¬ã¹ã®ãã©ã°èšå®
- ã¹ãã ãã©ããæ€åº
ã³ã³ãã©ã€ã¢ã³ã¹
- ãã©ã€ãã·ãŒã«æºæ ããåŠç
- å®å šãªããŒã¿åŠç
- ç£æ»èšŒè·¡
BillionVerify API çµ±å
BillionVerify 㯠SMTP ãã§ãã¯ãå«ãå æ¬çãªã¡ãŒã«æ€èšŒãæäŸããŸã:
async function verifyWithBillionVerify(email) {
const response = await fetch('https://api.billionverify.com/v1/verify', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({ email })
});
const result = await response.json();
return {
isValid: result.is_valid,
isDeliverable: result.is_deliverable,
isCatchAll: result.is_catch_all,
isDisposable: result.is_disposable,
isRoleBased: result.is_role_based,
smtpCheck: result.smtp_check,
mxRecords: result.mx_records,
riskScore: result.risk_score
};
}
API ã¯å éšçã«ãã¹ãŠã® SMTP ã®è€éããåŠçããªãããè€è£œããã«ã¯å€§ããªã€ã³ãã©ã¹ãã©ã¯ãã£ãå¿ èŠãªè¿œå ã®ã€ã³ããªãžã§ã³ã¹ãæäŸããŸãã
äžæ¬ SMTP æ€èšŒ
å€§èŠæš¡ãªã¡ãŒã«ãªã¹ããæ€èšŒããå Žåãäžæ¬æ€èšŒã¯ããã»ã¹ãæé©åããŸã:
async function bulkVerify(emails) {
// ãããåŠççšã®ãã¡ã€ã«ãã¢ããããŒã
const formData = new FormData();
formData.append('file', createCSV(emails));
const uploadResponse = await fetch('https://api.billionverify.com/v1/bulk/upload', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
},
body: formData
});
const { jobId } = await uploadResponse.json();
// å®äºãããŒãªã³ã°
let status = 'processing';
while (status === 'processing') {
await sleep(5000);
const statusResponse = await fetch(
`https://api.billionverify.com/v1/bulk/status/${jobId}`,
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const job = await statusResponse.json();
status = job.status;
}
// çµæãããŠã³ããŒã
const resultsResponse = await fetch(
`https://api.billionverify.com/v1/bulk/download/${jobId}`,
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
return await resultsResponse.json();
}
ãµã€ã³ã¢ãããã©ãŒã åããªã¢ã«ã¿ã€ã SMTP æ€èšŒ
ãŠãŒã¶ãŒç»é²äžã«ãªã¢ã«ã¿ã€ã ã®ã¡ãŒã«æ€èšŒãå®è£ ããããšã§ãæåããããŒã¿å質ãåäžããŸãã
ããã³ããšã³ãå®è£
// å
¥åæã®ãããŠã³ã¹ãããã¡ãŒã«æ€èšŒ
const emailInput = document.getElementById('email');
let verificationTimeout;
emailInput.addEventListener('input', (e) => {
clearTimeout(verificationTimeout);
const email = e.target.value;
if (!isValidSyntax(email)) {
showError('æå¹ãªã¡ãŒã«åœ¢åŒãå
¥åããŠãã ãã');
return;
}
verificationTimeout = setTimeout(async () => {
showLoading();
try {
const result = await verifyEmail(email);
if (result.isValid) {
showSuccess('ã¡ãŒã«ã確èªãããŸãã');
} else if (result.isCatchAll) {
showWarning('ãã®ã¡ãŒã«ãå®å
šã«ç¢ºèªã§ããŸãã');
} else {
showError('ãã®ã¡ãŒã«ã¢ãã¬ã¹ã¯ç¡å¹ã®ããã§ã');
}
} catch (err) {
// æ€èšŒãšã©ãŒã§ãµã€ã³ã¢ããããããã¯ããªã
clearStatus();
}
}, 500); // å
¥ååæ¢åŸ 500ms åŸ
æ©
});
async function verifyEmail(email) {
const response = await fetch('/api/verify-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
return response.json();
}
ããã¯ãšã³ã API ãšã³ããã€ã³ã
// Express.js ãšã³ããã€ã³ã
app.post('/api/verify-email', async (req, res) => {
const { email } = req.body;
// ãŸãç°¡åãªæ§æãã§ãã¯
if (!isValidEmailSyntax(email)) {
return res.json({ isValid: false, reason: 'Invalid syntax' });
}
try {
// å®å
šãªæ€èšŒã®ããã« BillionVerify API ãåŒã³åºã
const result = await emailVerify.verify(email);
res.json({
isValid: result.is_valid && result.is_deliverable,
isCatchAll: result.is_catch_all,
isDisposable: result.is_disposable,
suggestion: result.did_you_mean // ã¿ã€ããã¹ã®ææ¡
});
} catch (err) {
// ãã§ã€ã«ãªãŒãã³ - API ãšã©ãŒã§ãµã€ã³ã¢ããããããã¯ããªã
res.json({ isValid: true, verified: false });
}
});
SMTP æ€èšŒã®ã»ãã¥ãªãã£äžã®èæ ®äºé
SMTP æ€èšŒã«ã¯ãã»ãã¥ãªãã£æèãå¿ èŠãšãããããã¯ãŒã¯æ¥ç¶ãå«ãŸããŸãã
ã€ã³ãã©ã¹ãã©ã¯ãã£ã®ä¿è·
ãã¡ã€ã¢ãŠã©ãŒã«èšå®
- æ€èšŒãµãŒããŒããã®ã¢ãŠãããŠã³ã SMTP æ¥ç¶ã®ã¿ãèš±å¯
- ç°åžžãªæ¥ç¶ãã¿ãŒã³ãç£èŠ
- æ¢ç¥ã®æªæã®ãã IP ç¯å²ããããã¯
TLS/SSL ã®äœ¿çš
- å©çšå¯èœãªå Žå㯠STARTTLS ã䜿çš
- ãµãŒããŒèšŒææžãæ€èšŒ
- èšŒææžãšã©ãŒãé©åã«åŠç
ãã©ãã¯ãªã¹ãç»é²ã®åé¿
æ€èšŒãµãŒããŒãã¹ãã ãéä¿¡ããŠããããã«èŠããå ŽåããŸãã¯ã¡ãŒã«ãµãŒããŒãæªçšããŠããå Žåããã©ãã¯ãªã¹ãã«ç»é²ãããå¯èœæ§ããããŸã:
- 峿 Œãªã¬ãŒãå¶éãå®è£
- æ€èšŒå°çšã® IP ã䜿çš
- ãã©ãã¯ãªã¹ãã®ã¹ããŒã¿ã¹ã宿çã«ç£èŠ
- é©åãªéåŒã DNS ãç¶æ
- æªçšã®èŠæ ã«è¿ éã«å¯Ÿå¿
ããŒã¿ãã©ã€ãã·ãŒ
ã¡ãŒã«ã¢ãã¬ã¹ã¯ä¿è·ãå¿ èŠãªå人ããŒã¿ã§ã:
- å®å šãªã¡ãŒã«ã¢ãã¬ã¹ãäžå¿ èŠã«ãã°ã«èšé²ããªã
- ä¿åãããæ€èšŒçµæãæå·å
- ããŒã¿ä¿æããªã·ãŒãå®è£
- GDPR ããã³ãã®ä»ã®èŠå¶ã«æºæ
- API åŒã³åºãã«ã¯å®å šãªæ¥ç¶ã䜿çš
SMTP æ€èšŒã®ããã©ãŒãã³ã¹æž¬å®
æ€èšŒã·ã¹ãã ãé©åã«æ©èœããŠããããšã確èªããããã«ãäž»èŠãªææšã远跡ããŸãã
äž»èŠææš
ç²ŸåºŠææš
- çéœæ§ç(æå¹ãæ£ããèå¥)
- åœéœæ§ç(ç¡å¹ãæå¹ãšããŠããŒã¯)
- ãã£ãããªãŒã«æ€åºç²ŸåºŠ
- äžæ/確èªäžå¯èœç
ããã©ãŒãã³ã¹ææš
- å¹³åæ€èšŒæé
- 95 ããŒã»ã³ã¿ã€ã«ã¬ã¹ãã³ã¹æé
- æ¥ç¶æåç
- ãã¡ã€ã³å¥ã¿ã€ã ã¢ãŠãç
éçšææš
- æ¥æ¬¡æ€èšŒããªã¥ãŒã
- ã¿ã€ãå¥ãšã©ãŒç
- ãã©ãã¯ãªã¹ãã€ã³ã·ãã³ã
- API å¯çšæ§
ç£èŠããã·ã¥ããŒãã®äŸ
class VerificationMetrics {
constructor() {
this.counters = {
total: 0,
valid: 0,
invalid: 0,
catchAll: 0,
unknown: 0,
errors: 0
};
this.timings = [];
}
record(result, duration) {
this.counters.total++;
this.timings.push(duration);
if (result.valid === true) this.counters.valid++;
else if (result.valid === false) this.counters.invalid++;
else if (result.isCatchAll) this.counters.catchAll++;
else this.counters.unknown++;
}
recordError() {
this.counters.errors++;
}
getStats() {
const sortedTimings = this.timings.sort((a, b) => a - b);
return {
counts: this.counters,
accuracy: {
validRate: this.counters.valid / this.counters.total,
unknownRate: this.counters.unknown / this.counters.total
},
performance: {
avgTime: average(this.timings),
p95Time: sortedTimings[Math.floor(sortedTimings.length * 0.95)],
errorRate: this.counters.errors / this.counters.total
}
};
}
}
çµè«
SMTP ã¡ãŒã«æ€èšŒã¯ãã¡ãŒã«ã¢ãã¬ã¹ãã¡ãã»ãŒãžãåä¿¡ã§ãããã©ããã確èªããããã®æãæ£ç¢ºãªæ¹æ³ãæäŸããŸããSMTP ãããã³ã«ã䜿çšããŠã¡ãŒã«ãµãŒããŒãšçŽæ¥éä¿¡ããããšã§ãå®éã«ã¡ãŒã«ãéä¿¡ããããšãªãã¡ãŒã«ããã¯ã¹ã®ååšã倿ã§ããŸãã
广ç㪠SMTP æ€èšŒãæ§ç¯ããã«ã¯ããããã³ã«ã®è©³çްãçè§£ãããã£ãããªãŒã«ãã¡ã€ã³ãã°ã¬ã€ãªã¹ãã£ã³ã°ãªã©ã®ããŸããŸãªèª²é¡ãåŠçããé©åãªã¬ãŒãå¶éãšãšã©ãŒåŠçãå®è£ ããå¿ èŠããããŸããã»ãšãã©ã®æ¬çªã¢ããªã±ãŒã·ã§ã³ã§ã¯ãBillionVerify ã®ãããªãããã§ãã·ã§ãã«ãªã¡ãŒã«æ€èšŒãµãŒãã¹ããæ€èšŒã€ã³ãã©ã¹ãã©ã¯ãã£ãæ§ç¯ããã³ç¶æããè€éããªãã«ãå¿ èŠãªã€ã³ãã©ã¹ãã©ã¯ãã£ãã€ã³ããªãžã§ã³ã¹ãä¿¡é Œæ§ãæäŸããŸãã
åŠç¿ç®çã§ç¬èªã® SMTP æ€èšŒãå®è£ ããå Žåã§ãããããã§ãã·ã§ãã«ãªã¡ãŒã«æ€èšŒ API ãçµ±åããå Žåã§ãããã®ã¬ã€ãã§èª¬æããååã¯ãå€§èŠæš¡ã«ã¡ãŒã«ã¢ãã¬ã¹ãæ€èšŒããéã®èå°è£ã§äœãèµ·ãã£ãŠããããçè§£ããã®ã«åœ¹ç«ã¡ãŸãã
SMTP æ€èšŒã¯å æ¬çãªã¡ãŒã«ããªããŒã·ã§ã³ã®äžèŠçŽ ã«ãããªãããšãå¿ããªãã§ãã ãããæ§ææ€èšŒããã¡ã€ã³æ€èšŒãäœ¿ãæšãŠã¡ãŒã«æ€åºããã£ãããªãŒã«èå¥ãšçµã¿åãããããšã§ãéä¿¡è ã¬ãã¥ããŒã·ã§ã³ãä¿è·ããã¡ãŒã«é ä¿¡æ§ãåäžãããã¡ãŒã«ãªã¹ãã®å質ãç¶æããå®å šãªã¡ãŒã«æ€èšŒæŠç¥ãäœæãããŸãã
å³åº§ã®ãã£ãŒãããã¯ã®ããã«åºæ¬çãªæ§æãšãã¡ã€ã³ãã§ãã¯ããå§ãã培åºçãªæ€èšŒã®ããã« SMTP æ€èšŒãéå±€åããå°çšã®ã¡ãŒã«æ€èšŒã€ã³ãã©ã¹ãã©ã¯ãã£ããåŸãããä¿¡é Œæ§ã粟床ã远å ã®ã€ã³ããªãžã§ã³ã¹ãå¿ èŠãªå Žå㯠BillionVerify ã®ãããªãããã§ãã·ã§ãã«ãµãŒãã¹ãæ€èšããŠãã ããã