Es ist üblich, eine Zip mit einem Passwort an eine E-Mail anzuhängen und zu sagen: "Das Passwort wird separat gesendet." Ich mache es nicht selbst, aber es ist ärgerlich, weil ich es laut der anderen Partei tun muss.
Hier spielen die Vor- und Nachteile dieser Methode keine Rolle. Egal wie viel ich predige, die Situation, diesen Brauch zu haben, ändert sich nicht.
Und ich denke nicht daran, diese Praxis zu brechen. Ich überlasse das etwas mit enormer Kraft.
Der alte Idiot sagte. "Wickeln Sie es um einen langen." Ich denke jedoch, dass es besser ist, darüber nachzudenken, wie man es aufwickelt.
Es gibt nur eine Sache, die ich lösen möchte, wenn es aufgerollt ist. Sei nicht verärgert. Wenn Sie zu diesem Zweck ein Websystem erstellen und den Browser öffnen, um so etwas zu tun, ist dies überwältigend. Ich möchte es so nah wie möglich an der normalen Postübertragung realisieren.
Nachdem ich darüber nachgedacht hatte, versuchte ich, es mit einem serverlosen Gefühl unter Verwendung von Amazon SES zu lösen, während ich einige Einschränkungen zuließ.
Es gibt jedoch die folgenden Einschränkungen. Persönlich ist es akzeptabel.
Lambda Es ist das erste Mal, dass ich Python ernsthaft schreibe, aber ist es so in Ordnung? Es geht um einen Kampf zwischen E-Mail, Zeichencodes und Dateien.
# -*- coding: utf-8 -*-
import os
import sys
import string
import random
import json
import urllib.parse
import boto3
import re
import smtplib
import email
import base64
from email import encoders
from email.header import decode_header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from datetime import datetime
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'vendored'))
import pyminizip
s3 = boto3.client('s3')
class MailParser(object):
"""
Mail-Analyseklasse
(Referenz) http://qiita.com/sayamada/items/a42d344fa343cd80cf86
"""
def __init__(self, email_string):
"""
Initialisieren
"""
self.email_message = email.message_from_string(email_string)
self.subject = None
self.from_address = None
self.reply_to_address = None
self.body = ""
self.attach_file_list = []
#Interpretation von eml
self._parse()
def get_attr_data(self):
"""
Mail-Daten abrufen
"""
attr = {
"from": self.from_address,
"reply_to": self.reply_to_address,
"subject": self.subject,
"body": self.body,
"attach_files": self.attach_file_list
}
return attr
def _parse(self):
"""
Mail-Dateien analysieren
"""
#Analyse des Nachrichtenkopfteils
self.subject = self._get_decoded_header("Subject")
self.from_address = self._get_decoded_header("From")
self.reply_to_address = self._get_decoded_header("Reply-To")
#Extrahieren Sie nur die Zeichenfolge der E-Mail-Adresse
from_list = re.findall(r"<(.*@.*)>", self.from_address)
if from_list:
self.from_address = from_list[0]
reply_to_list = re.findall(r"<(.*@.*)>", self.reply_to_address)
if reply_to_list:
self.reply_to_address = ','.join(reply_to_list)
#Analyse des Nachrichtentextteils
for part in self.email_message.walk():
#Wenn der ContentType mehrteilig ist, ist der tatsächliche Inhalt noch größer
#Da es sich im Innenteil befindet, überspringen Sie es
if part.get_content_maintype() == 'multipart':
continue
#Dateinamen abrufen
attach_fname = part.get_filename()
#Sollte der Body sein, wenn es keinen Dateinamen gibt
if not attach_fname:
charset = str(part.get_content_charset())
if charset != None:
if charset == 'utf-8':
self.body += part.get_payload()
else:
self.body += part.get_payload(decode=True).decode(charset, errors="replace")
else:
self.body += part.get_payload(decode=True)
else:
#Wenn es einen Dateinamen gibt, handelt es sich um eine angehängte Datei
#Daten bekommen
self.attach_file_list.append({
"name": attach_fname,
"data": part.get_payload(decode=True)
})
def _get_decoded_header(self, key_name):
"""
Ruft das dekodierte Ergebnis aus dem Header-Objekt ab
"""
ret = ""
#Schlüssel ohne das entsprechende Element geben leere Zeichen zurück
raw_obj = self.email_message.get(key_name)
if raw_obj is None:
return ""
#Machen Sie das dekodierte Ergebnis unicode
for fragment, encoding in decode_header(raw_obj):
if not hasattr(fragment, "decode"):
ret += fragment
continue
#Wenn es keine Codierung gibt, UTF vorerst-Mit 8 dekodieren
if encoding:
ret += fragment.decode(encoding)
else:
ret += fragment.decode("UTF-8")
return ret
class MailForwarder(object):
def __init__(self, email_attr):
"""
Initialisieren
"""
self.email_attr = email_attr
self.encode = 'utf-8'
def send(self):
"""
Komprimieren Sie die angehängte Datei mit einem Kennwort, übertragen Sie sie und senden Sie eine Kennwortbenachrichtigungs-E-Mail
"""
#Passwortgenerierung
password = self._generate_password()
#Generierung von Zip-Daten
zip_name = datetime.now().strftime('%Y%m%d%H%M%S')
zip_data = self._generate_zip(zip_name, password)
#Senden Sie Zip-Daten
self._forward_with_zip(zip_name, zip_data)
#Passwort senden
self._send_password(zip_name, password)
def _generate_password(self):
"""
Passwortgenerierung
Mische, indem du jeweils 4 Buchstaben aus Symbolen, Buchstaben und Zahlen nimmst
"""
password_chars = ''.join(random.sample(string.punctuation, 4)) + \
''.join(random.sample(string.ascii_letters, 4)) + \
''.join(random.sample(string.digits, 4))
return ''.join(random.sample(password_chars, len(password_chars)))
def _generate_zip(self, zip_name, password):
"""
Generieren Sie Daten für die Zip-Datei mit dem Passwort
"""
tmp_dir = "/tmp/" + zip_name
os.mkdir(tmp_dir)
#Speichern Sie die Datei lokal
for attach_file in self.email_attr['attach_files']:
f = open(tmp_dir + "/" + attach_file['name'], 'wb')
f.write(attach_file['data'])
f.flush()
f.close()
#Mit Passwort komprimieren
dst_file_path = "/tmp/%s.zip" % zip_name
src_file_names = ["%s/%s" % (tmp_dir, name) for name in os.listdir(tmp_dir)]
pyminizip.compress_multiple(src_file_names, dst_file_path, password, 4)
# #Lesen Sie die generierte Zip-Datei
r = open(dst_file_path, 'rb')
zip_data = r.read()
r.close()
return zip_data
def _forward_with_zip(self, zip_name, zip_data):
"""
Generieren Sie Daten für die Zip-Datei mit dem Passwort
"""
self._send_message(
self.email_attr['subject'],
self.email_attr["body"].encode(self.encode),
zip_name,
zip_data
)
return
def _send_password(self, zip_name, password):
"""
Passwort für die Zip-Datei senden
"""
subject = self.email_attr['subject']
message = """
Dies ist das Kennwort für die Datei, die Sie zuvor gesendet haben.
[Gegenstand] {}
[Dateiname] {}.zip
[Passwort] {}
""".format(subject, zip_name, password)
self._send_message(
'[password]%s' % subject,
message,
None,
None
)
return
def _send_message(self, subject, message, attach_name, attach_data):
"""
E-Mail senden
"""
msg = MIMEMultipart()
#Header
msg['Subject'] = subject
msg['From'] = self.email_attr['from']
msg['To'] = self.email_attr['reply_to']
msg['Bcc'] = self.email_attr['from']
#Text
body = MIMEText(message, 'plain', self.encode)
msg.attach(body)
#Angehängte Datei
if attach_data:
file_name = "%s.zip" % attach_name
attachment = MIMEBase('application', 'zip')
attachment.set_param('name', file_name)
attachment.set_payload(attach_data)
encoders.encode_base64(attachment)
attachment.add_header("Content-Dispositon", "attachment", filename=file_name)
msg.attach(attachment)
#Senden
smtp_server = self._get_decrypted_environ("SMTP_SERVER")
smtp_port = self._get_decrypted_environ("SMTP_PORT")
smtp_user = self._get_decrypted_environ("SMTP_USER")
smtp_password = self._get_decrypted_environ("SMTP_PASSWORD")
smtp = smtplib.SMTP(smtp_server, smtp_port)
smtp.ehlo()
smtp.starttls()
smtp.ehlo()
smtp.login(smtp_user, smtp_password)
smtp.send_message(msg)
smtp.quit()
print("Successfully sent email")
return
def _get_decrypted_environ(self, key):
"""
Verschlüsselte Umgebungsvariablen entschlüsseln
"""
client = boto3.client('kms')
encrypted_data = os.environ[key]
return client.decrypt(CiphertextBlob=base64.b64decode(encrypted_data))['Plaintext'].decode('utf-8')
def lambda_handler(event, context):
#Abrufen des Bucket-Namens und des Schlüsselnamens vom Ereignis
bucket = event['Records'][0]['s3']['bucket']['name']
key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'])
try:
#Lesen Sie den Inhalt der Datei aus S3
s3_object = s3.get_object(Bucket=bucket, Key=key)
email_string = s3_object['Body'].read().decode('utf-8')
#E-Mail analysieren
parser = MailParser(email_string)
#Mail-Weiterleitung
forwarder = MailForwarder(parser.get_attr_data())
forwarder.send()
return
except Exception as e:
print(e)
raise e
pyminizip Es scheint, dass passwortgeschütztes Zip nicht mit einer Standardbibliothek durchgeführt werden kann. Daher habe ich mich nur hier auf eine externe Bibliothek namens pyminizip verlassen. Dies war jedoch eine Bibliothek, die zum Zeitpunkt der Installation erstellt wurde, um eine Binärdatei zu erstellen. Daher habe ich einen Docker-Container für Amazon Linux lokal eingerichtet, um ihn auf Lambda auszuführen, und eine Binärdatei erstellt. Gibt es einen anderen guten Weg? ..
AWS SAM Übrigens habe ich dies lokal mit AWS SAM getestet. Es war gut, bis ich versuchte, die Informationen des SMTP-Servers direkt zu schreiben, aber als ich sie in die Umgebungsvariable verschob, funktionierte sie nicht gut und ich war frustriert. Es sieht so aus, als ob es behoben, aber nicht veröffentlicht wurde.
Ich werde es veröffentlichen, weil es eine große Sache ist. Codename zaru
.
Bitte verzeihen Sie mir, obwohl die Einstellmethode schlammig bleibt. ..
https://github.com/Kta-M/zaru
Ich habe es nur in meiner Umgebung (Mac, Thunderbird) ausprobiert, daher funktioniert es je nach Mailer und anderen Umgebungen möglicherweise nicht. Bitte übernehmen Sie die Verantwortung für Ihre Handlungen.
SES SES ist in der Region Tokio noch nicht verfügbar, daher werden wir es in der Region Oregon (us-west-2) bauen.
Zunächst überprüfen wir die Domain, damit Sie E-Mails an SES senden können. Es gibt verschiedene Methoden, daher werde ich diesen Bereich weglassen. Dies kann beispielsweise hilfreich sein-> Domain-Mail mit Amazon SES / Route53 mit Rails senden
Erstellen Sie nach Überprüfung der Domäne eine Regel.
Klicken Sie unter "Regelsätze" auf der rechten Seite des Menüs auf "Aktiven Regelsatz anzeigen".
Klicken Sie auf "Regel erstellen".
Registrieren Sie die zu empfangende E-Mail-Adresse. Geben Sie die E-Mail-Adresse der verifizierten Domain ein und klicken Sie auf "Empfänger hinzufügen".
Registrieren Sie die Aktion, wenn Sie eine E-Mail erhalten. Wählen Sie als Aktionstyp "S3" und geben Sie den Bucket zum Speichern der empfangenen E-Mail-Daten an. Wenn Sie zu diesem Zeitpunkt einen Bucket mit "S3-Bucket erstellen" erstellen, wird die erforderliche Bucket-Richtlinie automatisch registriert. Dies ist praktisch. Es wird eine Richtlinie festgelegt, die das Hochladen von Dateien von SES in den Bucket ermöglicht.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSESPuts-XXXXXXXXXXXX",
"Effect": "Allow",
"Principal": {
"Service": "ses.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::<ses-bucket-name>/*",
"Condition": {
"StringEquals": {
"aws:Referer": "XXXXXXXXXXXX"
}
}
}
]
}
Außerdem können die im Bucket gespeicherten E-Mail-Daten gespeichert werden. Daher ist es möglicherweise besser, einen Lebenszyklus festzulegen, damit sie nach einer bestimmten Zeit gelöscht werden.
Geben Sie der Regel einen Namen. Der Rest ist standardmäßig.
Überprüfen Sie die Registrierungsdaten und registrieren Sie sich!
Lambda
Bereitstellen in der Region Oregon sowie in SES. Da CloudFormation verwendet wird, erstellen Sie bitte einen S3-Bucket, um Daten hochzuladen.
# git clone [email protected]:Kta-M/zaru.git
# cd zaru
# aws cloudformation package --template-file template.yaml --s3-bucket <cfn-bucket-name> --output-template-file packaged.yaml
# aws cloudformation deploy --template-file packaged.yaml --stack-name zaru-stack --capabilities CAPABILITY_IAM --region us-west-2
Wenn Sie zur Lambda-Konsole gehen, wird die Funktion erstellt. Außerdem werden die IAM-Rollen erstellt, die zum Ausführen dieser Funktion erforderlich sind.
Stellen Sie Lambda auf Arbeit ein, indem Sie Mail-Daten auslösen, um in den Bucket zu gelangen.
Wechseln Sie im Bildschirm mit den Funktionsdetails zur Registerkarte Trigger.
Klicken Sie auf "Trigger hinzufügen", um ein S3-Ereignis zu erstellen. Der Bucket, in dem die Daten von SES stammen, ist der Ereignistyp Put. Davon abgesehen ist dies die Standardeinstellung. Bucket is
In dieser Lambda-Funktion werden SMTP-bezogene Informationen aus der verschlüsselten Umgebungsvariablen abgerufen. Erstellen Sie einen Schlüssel für diese Verschlüsselung.
Klicken Sie in der IAM-Konsole unten links auf den Verschlüsselungsschlüssel. Ändern Sie die Region in Oregon und erstellen Sie einen Schlüssel.
Sie müssen lediglich einen Alias Ihrer Wahl festlegen, der Rest ist standardmäßig in Ordnung.
Gehen Sie zurück zu Lambda und legen Sie die in der Funktion verwendeten Umgebungsvariablen fest. Am unteren Rand der Registerkarte Code befindet sich ein Formular zum Festlegen von Umgebungsvariablen. Aktivieren Sie "Verschlüsselungshilfe aktivieren" und geben Sie den zuvor erstellten Verschlüsselungsschlüssel an. Geben Sie für Umgebungsvariablen den Variablennamen und den Wert (Klartext) ein und klicken Sie auf die Schaltfläche "Verschlüsselung". Anschließend wird es mit dem angegebenen Verschlüsselungsschlüssel verschlüsselt. Die folgenden vier Umgebungsvariablen werden festgelegt.
Variablennamen | Erläuterung | Beispiel |
---|---|---|
SMTP_SERVER | SMTP-Server | smtp.example.com |
SMTP_PORT | SMTP-Port | 587 |
SMTP_USER | Benutzername, um sich beim SMTP-Server anzumelden | [email protected] |
SMTP_PASSWORD | SMTP_Benutzer-Passwort |
Geben Sie der Rolle, die diese Lambda-Funktion ausführt, die erforderlichen Berechtigungen.
Gehen Sie zunächst zur "Richtlinie" der IAM-Konsole und erstellen Sie die folgenden beiden Richtlinien mit "Richtlinie erstellen" -> "Eigene Richtlinie erstellen".
** Richtlinie: s3-get-object-zaru **
Geben Sie für
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1505586008000",
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::<ses-bucket-name>/*"
]
}
]
}
** Richtlinie; kms-decrypt-zaru **
Geben Sie für "
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1448696327000",
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": [
"<kms-arn>"
]
}
]
}
Fügen Sie diese beiden Richtlinien schließlich der Lambda-Funktionsausführungsrolle hinzu. Gehen Sie zunächst in der IAM-Konsole zu "Rolle", wählen Sie eine Rolle aus und hängen Sie sie an "Richtlinie anhängen" an.
Es sollte jetzt funktionieren. Bitte stellen Sie die für SES festgelegte E-Mail-Adresse in "An" und die E-Mail-Adresse der anderen Partei in "Antwort an" ein und senden Sie sie mit einer entsprechenden angehängten Datei. Wie ist das?
Dontokoi Reißverschluss angebracht!
Recommended Posts