가이드 · smtp python

제품 팀은 Python으로 SMTP를 어떻게 안전하게 구현해야 하나요?

Python의 SMTP는 웹 요청에서 직접 실행하지 말고 승인된 백그라운드 워커에서 구현하세요. EmailMessage로 메시지를 만들고, 제공업체의 현재 계약이 요구하면 암시적 TLS에는 SMTP_SSL을 사용하거나 STARTTLS로 명시적으로 업그레이드하고, 서버 측 시크릿으로 인증하고, 상한이 있는 타임아웃으로 send_message를 호출하세요. 연결하기 전에 작업을 저장하고, 수신자별 거부 증거를 기록하고, 모호한 연결 끊김을 대조하고, SMTP 접수와 이후의 전달 및 받은편지함 도달을 구분하세요.

SMTP 전에 발송을 승인하고 저장하기

영수증, 보안 알림, 요청된 인증, 계정 안내 같은 정당한 애플리케이션 이벤트에서 시작하세요. 호출자를 인증하고 테넌트, 메시지 유형, 보이는 From ID, 수신자, 템플릿 리비전을 승인하세요. SMTP 연결을 열기 전에 안정적인 비즈니스 이벤트 키와 함께 내구성 있는 발신 작업을 기록하세요. 이 키는 두 워커가 같은 논리적 메시지를 독립적으로 만드는 것을 막아야 합니다. 브라우저 입력이 SMTP 호스트, 포트, 사용자 이름, 엔벨로프 발신자, 임의의 수신자, 헤더, TLS 정책을 정해서는 안 됩니다. 이 값들은 검토된 서버 설정에 두세요. 큐 워커는 작업 하나를 가져와 발송 시점에 발송 제외와 승인을 다시 확인하고, 각 시도를 기록하고, 명시적인 상태를 통해 작업을 해제하거나 마무리해야 합니다. Python의 SMTP 라이브러리는 준비된 메시지를 전송할 뿐이며 테넌트 승인, 수신 동의, 멱등성, 발송 제외 정책은 제공하지 않습니다.

EmailMessage로 메시지 만들기

원본 헤더와 본문을 이어 붙이지 말고 email.message.EmailMessage를 사용하세요. 애플리케이션의 승인된 모델에 따라 From, To, Subject, Date, 생성된 Message-ID를 설정한 다음 텍스트에는 set_content를, 필요하면 HTML에는 add_alternative를 사용하세요. 주소 객체를 검증하고, 수신자와 첨부 파일 수에 상한을 두고, 값에서 개행 인젝션을 거부하고, 출력 컨텍스트에 맞게 템플릿 데이터를 이스케이프하세요. 텍스트와 HTML을 변경 불가능한 하나의 템플릿 리비전에서 모두 생성하세요. 제목, 사용자 정의 헤더, 파일 이름, 진단 필드, 로그에 시크릿이나 불필요한 개인 데이터를 넣지 마세요. 인증과 반송 처리가 서로 다른 ID에 의존할 수 있으므로 보이는 From 헤더와 SMTP 엔벨로프 발신자를 의도적으로 분리하세요. 정의된 필요 없이 메시지 본문 전체를 보관하는 대신 감사용으로 콘텐츠 리비전이나 개인정보를 보호하는 해시를 저장하세요.

암시적 TLS와 STARTTLS를 명시적으로 선택하기

Python은 처음부터 암호화되는 연결에는 SMTP_SSL을, 이미 수립된 연결을 업그레이드하는 데는 SMTP.starttls를 문서화하고 있습니다. 일반적인 포트 목록에서 추측하지 말고 제공업체의 현재 호스트 이름, 포트, 인증서, 제출 계약을 따르세요. 검증이 켜진 기본 SSL 컨텍스트를 만들고 인증서나 호스트 이름 검사를 비활성화하지 마세요. STARTTLS의 경우 연결하고, 필요하면 EHLO를 보내고, 컨텍스트와 함께 starttls를 호출하고, 업그레이드 후에는 광고되는 확장이 바뀔 수 있으므로 EHLO를 다시 보내세요. 평문 연결로 자격 증명이나 고객 메시지 내용을 절대 보내지 마세요. RFC 8314는 TLS로 보호되는 제출을 권장하고 평문 접근을 지양합니다. 인증서 실패, 호스트 이름 불일치, 필수 STARTTLS의 누락, 예기치 않은 기능 변경은 조용히 대체하지 말고 조사가 필요한 치명적 실패로 다루세요.

SMTP 자격 증명을 좁은 시크릿 경계 안에 두기

사용자 이름과 비밀번호 또는 토큰은 런타임에 관리형 서버 측 시크릿 기능에서 불러오세요. 자격 증명을 소스 코드, 클라이언트 번들, 환경 덤프, URL, 예외 추적, 분석 도구, 노트북, 스크린샷, 프롬프트, 커밋된 픽스처에 넣지 마세요. 각 자격 증명은 제공업체가 지원하는 가장 작은 환경과 워크로드로 범위를 제한하고 개발과 프로덕션을 분리하세요. 필요한 TLS 상태가 수립된 뒤에만 인증하세요. 통제된 수신자로 교체를 연습하세요. 승인된 관리 절차로 대체 자격 증명을 프로비저닝하고, 워커를 업데이트하고, 인증과 전체 이벤트 수명 주기를 확인한 다음 이전 값을 폐기합니다. 인증 실패가 반복되면 빠른 재시도 루프를 돌리지 말고 영향을 받는 경로를 일시 중지하세요. Python의 login 메서드는 서버가 광고하는 메커니즘 중에서 협상하지만, 제공업체의 실제 메커니즘, 계정 정책, 토큰 권한, 교체 동작에는 최신 증거가 필요합니다.

상한이 있는 Python 발송 함수 사용하기

제공업체 어댑터는 작게 유지하고 작업 상태 머신에 구조화된 증거를 반환하세요. 일반적인 흐름은 SSL 컨텍스트를 만들고, 암시적 TLS를 위해 SMTP_SSL(host, port, timeout=10)을 smtp로 열고, smtp.login(username, secret)을 호출한 다음 smtp.send_message(message, from_addr=envelope_from, to_addrs=recipients)를 호출하는 것입니다. 명시적 업그레이드가 필요한 제공업체에는 타임아웃을 지정한 SMTP, ehlo, starttls(context=context), ehlo, 그리고 login 순서로 사용하세요. 예시 호스트 이름이나 포트를 보편적인 기본값처럼 제시하지 마세요. 신뢰할 수 없는 헤더 파싱에 의존하지 말고 정규화된 수신자 목록을 전달하세요. 가능하면 예외 클래스, SMTP 응답 코드, 상한이 있는 진단 텍스트를 기록하되 주소, 자격 증명, 메시지 내용은 가리세요. 운영 실패를 진단할 수 있도록 연결, TLS, 인증, 엔벨로프, 데이터, 종료 단계를 각각 따로 측정하세요.

send_message의 수신자별 결과를 정확히 해석하기

Python 문서에 따르면 sendmail과 send_message는 하나 이상의 수신자에 대해 메일이 접수되면 정상적으로 반환하고, 거부된 수신자에 대해서는 딕셔너리를 반환합니다. 빈 딕셔너리는 그 단계에서 거부된 수신자가 없다는 뜻입니다. 작업 전체를 전달됨으로 표시하지 말고 수신자별 결과를 보존하세요. 모든 수신자가 거부되면 라이브러리는 SMTPRecipientsRefused 예외를 발생시킵니다. 다른 예외는 발신자 거부, DATA 거부, 인증, 연결, 프로토콜 등의 오류를 구분합니다. 정확한 증거를 애플리케이션 상태에 대응시키세요. 제출 서버 접수됨, 영구 거부됨, 일시 거부됨 또는 알 수 없음입니다. 정상 반환은 범위가 한정된 SMTP 제출 결과만 입증합니다. 대상 서버의 수락, 최종 메일함 도달, 열람, 반응은 입증하지 않습니다. 이후의 전송 상태 알림이나 제공업체 이벤트는 별도로 대조해야 합니다.

중복 위험을 통제할 수 있을 때만 재시도하기

다른 시도를 예약하기 전에 실패를 분류하세요. 영구적인 주소, 발신자, 인증, 정책, 콘텐츠 실패는 대개 자동 반복이 아니라 수정이나 발송 제외 처리가 필요합니다. 일시적인 4xx 응답은 지수 백오프, 지터, 시도 횟수 상한, 만료 시각, 대상별 예산으로 재시도할 수 있습니다. 메시지 데이터 이후의 연결 재설정이나 타임아웃은 모호할 수 있습니다. 클라이언트가 최종 응답을 놓쳤더라도 서버가 메시지를 접수했을 수 있습니다. 그 시도를 알 수 없음 상태로 유지하고, 개인정보를 보호하는 상관관계 방식으로 제공업체 활동이나 이후 이벤트를 확인하고, 즉각적이고 무작정 재전송하는 것을 피하세요. SMTP에는 보편적인 애플리케이션 멱등성 키가 없습니다. 내구성 있는 비즈니스 이벤트 키는 동시 애플리케이션 시도를 막지만 원격 SMTP 서버가 접수한 두 제출을 중복 제거하도록 강제할 수는 없습니다. 반복되는 모호한 결과는 에스컬레이션하고 결정에 사용한 정확한 증거를 보존하세요.

일부 수신자와 발송 제외 처리하기

메시지에 수신자가 여러 명이면 SMTP는 일부를 접수하고 나머지는 거부할 수 있습니다. 각 수신자의 응답을 저장하고 접수된 일부만 다음 상태로 진행하세요. 주소 하나가 일시적으로 거부되었다는 이유만으로 원래 목록 전체를 다시 보내지 마세요. 재시도를 포함해 매 시도 전에 영구 반송, 스팸 신고, 수신 거부, 법적, 테넌트, 관리자 단위의 발송 제외를 적용하세요. 메시지 유형은 명시적으로 문서화된 정책으로만 구분하세요. 메시지를 트랜잭션으로 표시한다고 해서 수신자 안전이나 제공업체의 제한이 사라지지 않습니다. 개인정보 보호와 개별 상태를 위해 비용을 감수할 가치가 있는 민감한 워크플로에는 수신자 한 명당 작업 하나를 선호하세요. To나 Cc로 수신자 목록을 노출하지 말고, Bcc 동작을 승인의 대용으로 쓰지 마세요. SMTP 응답에는 수신자 주소나 수신 측별 세부 정보가 들어 있을 수 있으므로 진단 텍스트를 제한하고 가리세요.

통제된 시스템으로 실패 경로 테스트하기

메시지 구성, 유니코드, 텍스트 및 HTML 대체본, 첨부 파일, 헤더 거부, 수신자 정규화, TLS 검증, STARTTLS 누락, 잘못된 자격 증명, 발신자 거부, 수신자 일부 및 전체 거부, DATA 거부, 접수 가능성 전후의 타임아웃, 연결 끊김, 속도 제한 응답, 재시도 만료, 중복 워커, 발송 제외 변경, 시크릿 교체를 테스트하세요. 결정적인 단위 및 통합 테스트에는 통제된 테스트 SMTP 서비스나 로컬 가짜 서버를 사용하고, 하위 환경의 트래픽이 실수로 고객 주소로 가지 않게 하세요. 프로덕션 카나리에서는 승인된 수신자를 사용하고 원본 헤더에서 보이는 From, 엔벨로프 경로, Message-ID, DKIM, SPF, DMARC 정렬, 제공업체 증거를 확인하세요. 로그와 지표에 자격 증명이나 메시지 본문이 유출되지 않는지 확인하세요. 워커가 테넌트 승인을 우회하거나, TLS를 낮추거나, 상한 없이 재시도하거나, 부분 거부를 무시하거나, 발송 경로를 일시 중지할 수 없다면 출시를 통과시키지 마세요.

SendHQ의 활용 방식

SendHQ는 예상되는 제품 커뮤니케이션을 위한 워크스페이스 단위 이메일 API입니다. 문서에서 발송, 검증된 도메인, 전달 이벤트, 발송 제외를 다룹니다. Python과 SendHQ를 연동할 때 문서화된 HTTP API를 사용하세요.

자주 묻는 질문

Python에서는 SMTP_SSL과 STARTTLS 중 무엇을 써야 하나요?

제공업체의 현재 제출 계약이 요구하는 모드를 사용하세요. SMTP_SSL은 연결 시작부터 암호화하고, STARTTLS는 명시적으로 업그레이드하며 검증된 TLS와 새 EHLO가 필요합니다.

send_message가 정상적으로 반환되면 전달이 증명되나요?

아니요. 그 SMTP 제출 단계에서 하나 이상의 수신자가 접수되었다는 뜻입니다. 대상 서버의 수락, 메일함 도달, 반응에는 이후의 범위가 한정된 증거가 필요합니다.

send_message가 반환하는 딕셔너리는 무엇을 의미하나요?

SMTP 서버가 거부한 수신자를 응답 증거에 대응시킵니다. 빈 딕셔너리는 그 단계에서 거부된 수신자가 없다는 뜻이며, 모든 메시지가 받은편지함에 도착했다는 뜻이 아닙니다.

타임아웃이 발생하면 즉시 재시도해도 되나요?

제출이 이루어졌을 수 있는 시점 이후에 발생했다면 안전하지 않습니다. 그 시도를 모호한 상태로 보존하고, 제공업체나 이후 이벤트 증거와 대조하고, 상한이 있는 중복 위험 정책 아래에서만 다시 보내세요.

SMTP 비밀번호는 어디에 보관해야 하나요?

워크로드와 환경 접근이 좁게 제한되고, 조회가 감사되고, 교체가 검증되어 있으며, 클라이언트, 로그, 프롬프트, 픽스처에 노출되지 않는 관리형 서버 측 시크릿 기능을 사용하세요.

프로덕션에서 인증서 검증을 비활성화해도 되나요?

아니요. 인증서나 호스트 이름 실패는 안전하지 않거나 잘못된 구성의 증거입니다. TLS 검증을 조용히 약화하지 말고 경로를 중단하고 원인을 진단하세요.

일부 수신자가 거부되면 어떻게 처리해야 하나요?

각 수신자의 결과를 저장하고, 접수된 일부를 다음 상태로 진행하고, 재시도할 수 있는 일시적 거부만 재시도하세요. 이미 접수된 수신자를 원래 목록 전체와 함께 다시 보내지 마세요.

이 페이지가 SendHQ의 SMTP 지원을 증명하나요?

아니요. 이 가이드는 일반적인 Python SMTP를 다루므로 이메일 API는 SendHQ의 최신 문서를 사용하세요.

출처