SMTP Zugangsdaten im Programm speichern

  • VB.NET

Es gibt 50 Antworten in diesem Thema. Der letzte Beitrag () ist von DerSmurf.

    SMTP Zugangsdaten im Programm speichern

    Neu

    Hallo Leute
    Ich habe mein VBA Adressbuch mithilfe eines DataSets in VB.Net nachgebaut.
    Nun fehlt noch die Funktion Email.
    Hierzu gibt es einen Button, der dann die Form aus dem angehängten Bild läd und den Empfänger (sofern angeklickt) aus dem Adressbuch (bzw. aus dem DataSet holt).
    Unter dem obigen Dropdown "Absender" soll der User verschiedene Emailadressen auswählen können, die über eine Einstellungsform konfiguriert werden können.
    Speichern möchte ich zum senden relevanten Daten, also Email, Passwort, Server und Port.
    In meinem VBA Programm habe ich diese Daten fest im Klartext im Code hinterlegt. Das ist ja aber zum einen durch den User nicht änderbar, zum andern irgendwie eine Vollkatastrophe.

    Wie kann ich meine Maildaten sicher im Programm hinterlegen, und wie schaffe ich die "Verknüpfung" zwischen ausgewählter Emailadresse aus dem Dropdown und den hinterlegten Daten? Also woher weiß mein Programm, welches Passwort zu welcher Mail gehört?
    Bilder
    • Anmerkung 2019-06-12 084540.jpg

      115,83 kB, 1.296×1.310, 26 mal angesehen

    Neu

    Wenn die Mail vom Client aus geschickt wird, geht "sicher" nicht wirklich.

    Wie du schon selbst erkannt hast, ist die Speicherung im Programm ein NoGo.

    Etwas sicherer ist es, wenn du die Credentials in einer zentralen DB speicherst und zur Laufzeit ausliest.
    Dabei kann die DB ggf. die Benutzerberechtigungen des aufrufenden User auswerten, damit er nur die Daten der von ihm berechtigen Versendern bekommt.

    Einigermassen sicher wird es erst, wenn die Mails über einen Server laufen, der die Berechtigungen überprüft und die Mails dann selbst verschickt.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

    Neu

    Huhu
    Danke für deine Rückmeldung.
    Die Mails über einen Server laufen lassen übersteigt meine Kentnisse und wahrhscheinlich auch meine technischen Voraussetzungen deutlich.

    Bisher nutze ich den Thunderbird (portable). Hier gibt es ja die Möglichkeit in den Einstellungen die Passwörter im Klartext anzeigen zu lassen.
    Dann müssen ja die Passwörter lokal gespeichert sein.
    Ist das genau so "schlecht" wie speichern im Code, oder gibt es hier einen "Trick", dass das ganze sicherer wird?

    Also wie wäre es wenn, ich irgendein Chiffrierverfahren anwende?
    Oder scheitert das daran, dass ich ja dann im Programm dechiffrieren muss?

    Neu

    Speichere doch alle wichtigen Daten außer dem Passwort.
    Dann sollte der User eben sein Passwort selber eingeben.
    Oder du machst eine CheckBox, dass der User dann das Passwort einspeichert.
    Mit der Information das, dann das Passwort verschlüsselt auf dem Computer gespeichert wird.
    Dann kann der User selber entscheiden.
    Visual Basic.NET 8o
    MS-SQL
    8o

    Neu

    Ja, eine Checkbox mit Speicheroption ist eine gute Idee.

    Ich würde die Settings (Email, Passwort, Port, Server) jeweils in einem Array in My.Settings speichern.
    Dann müsste ich ja, wenn ich mithilfe des EmailArrays die Listbox befülle, über den Index die anderen Settings ebenfalls über Index rausfiltern können.
    Also in "Gedankencode" If EmailArray.selectedIndex = 1 then Password = Passwordarray.Index(1)
    Oder gibt es eine elegantere Lösung?
    Nur ein Array und die Daten getrennt durch "," und dann mit Split arbeiten.

    Und welche Verschlüsselungsmethode bietet sich hier an - oder ist es einfach wurscht, weil ja eh jede Verschlüsselung durch einen Blick auf den Code knackbar ist?

    Neu

    Ich würde den String mit einer # splitten.
    Da jede Verschlüsselungsmethode knackbar ist, sollte man deshalb nicht so viel Zeit verschwenden.
    Falls jemand die Verschlüsselung knacken möchte, schaft er dies meistens auch.
    Es reicht einfach das Passwort zum Beispiel in ein MD5-Hash zu ändern.
    Der normale User, kann damit auch nichts anfangen.
    123456 wird zu e10adc3949ba59abbe56e057f20f883e

    Link:
    md5-generator.de/
    Visual Basic.NET 8o
    MS-SQL
    8o

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Cheffboss“ ()

    Neu

    Super!
    Wie ich mit Vb.Net einen Md5-Hash erstelle und aus Md5-Hash das Passwort erzeuge, werde ich wohl ergoogelt bekommen.
    Wenn mein Code dann steht kann ich mit deinem Link kontrollieren, ob alles passt.
    Und ich mache mir ein Array in My.Settings welches ich mit # splitte.

    Alles geklärt :o)

    Ich danke euch!

    Neu

    Vielleicht hab ich mich beim Thema Sicherheit noch nicht ausreichend reingekniet, aber ich blick's gerade nicht. Aus nem MD5-Hashwert kann man kein Passwort wiederherstellen - ohne ne Rainbowtable herzunehmen. Das ist ja der Sinn der Sache. Hashes sind m.E. Einbahnstraßen. Würde das gehen, dann könnte man ja auch als Außenstehender das "verschlüsselte Passwort" (was ein Hashwert nicht ist!) zu nem Klartext machen. Im Sinne von: Sag Du mir, was Du für nen Hash-Algorithmus verwendest und ich sag Dir, was Du verschlüsselt hast.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.

    Neu

    Das ist alles Quatsch. Die Daten gehören verschlüsselt. Und überhaupt MD5 noch ins Spiel zu bringen ist fahrlässig. md5.gromweb.com/?md5=e10adc3949ba59abbe56e057f20f883e

    Im Programmcode DÜRFEN überhaupt keine Keys, Hashes oder sonstige sensible Daten gespeichert werden. Es gibt überhaupt kein Argument, was dafür spricht. Eine sinnvolle Lösung ist die Daten verschlüsselt in einer Datei vorzuhalten und bei jedem Programmstart den User nach dem Passwort zu fragen. Daraufhin werden die Daten freigegeben und es kann damit gearbeitet werden.

    Verschlüsselung ist kein Hexenwerk...

    VB.NET-Quellcode

    1. Imports System.Security.Cryptography
    2. Imports System.IO
    3. Imports System.Text
    4. Public Class CryptoManager
    5. Public Shared Function EncrypString(data As String, password As String) As String
    6. Dim key As New Rfc2898DeriveBytes(password, New Byte() {10, 255, 255, 7, 14, 16, 23, 155})
    7. Using x As New TripleDESCryptoServiceProvider()
    8. x.Key = key.GetBytes(x.KeySize \ 8)
    9. x.IV = key.GetBytes(x.BlockSize \ 8)
    10. Using mS As New MemoryStream()
    11. Using cS As New CryptoStream(mS, x.CreateEncryptor(), CryptoStreamMode.Write)
    12. Dim dataBytes As Byte() = Encoding.UTF8.GetBytes(data)
    13. cS.Write(dataBytes, 0, dataBytes.Length)
    14. End Using
    15. Return Convert.ToBase64String(mS.ToArray())
    16. End Using
    17. End Using
    18. End Function
    19. Public shared Function DecryptString(data As String, password As String) As String
    20. Dim key As New Rfc2898DeriveBytes(password, New Byte() {10, 255, 255, 7, 14, 16, 23, 155})
    21. Using x As New TripleDESCryptoServiceProvider()
    22. x.Key = key.GetBytes(x.KeySize \ 8)
    23. x.IV = key.GetBytes(x.BlockSize \ 8)
    24. Using mS As New MemoryStream()
    25. Using cS As New CryptoStream(mS, x.CreateDecryptor(), CryptoStreamMode.Write)
    26. Dim dataBytes As Byte() = Convert.FromBase64String(data)
    27. cS.Write(dataBytes, 0, dataBytes.Length)
    28. End Using
    29. Return Encoding.UTF8.GetString(mS.ToArray())
    30. End Using
    31. End Using
    32. End Function
    33. End Class


    Das 2. Argument hier Dim key As New Rfc2898DeriveBytes(password, New Byte() {10, 255, 255, 7, 14, 16, 23, 155}) ist der Salt, dieser gehört normalerweise nochmal extra irgendwo vorgehalten. Und die Fehlerbehandlung ist auch nicht implementiert.
    Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
    Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o

    How to turn OPTION STRICT ON
    Why OPTION STRICT ON

    Neu

    Sag ich ja, Rainbow table. Sagt ja auch die von Dir verlinkte Seite:

    gromweb schrieb:

    Unfortunately, there is a way to decrypt a MD5 hash, using a dictionary populated with strings and their MD5 counterpart. As most users use very simple passwords (like "123456", "password", "abc123", etc), MD5 dictionaries make them very easy to retrieve.

    Oder vielleicht bin ich n bisken zu ego und fühl mich mit dem

    SpaceyX schrieb:

    Das ist alles Quatsch.
    fälschlicherweise auch angesprochen.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.

    Neu

    Nee @VaporiZed das bezog sich nicht auf Dich, sondern auf die Konversation vor Dir...
    Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
    Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o

    How to turn OPTION STRICT ON
    Why OPTION STRICT ON

    Neu

    Hallo ihr lieben.
    Na toll. Dann hab ich es doch nicht :(
    Und hier gibt es eine Diskussion wie in (fast) allen Artikeln die ich google zu diesem Thema gefunden habe.

    Ich habe nun keine Ahnung, was ich tun soll :(

    Neu

    DerSmurf schrieb:

    was ich tun soll
    Du hast doch in Post #9 einen sehr guten Ansatz, wie du deine Passwörter ver- und entschlüsselst.
    Wenn du lokal arbeiten willst, ist das die einzige Chance, es dem unbedarften Anwender schwer genug zu machen.
    Wenn du das Verschlüsselungspasswort nirgends speichern willst, kannst du Security by Obscurity betreiben und die SMTP-Serveradresse verwenden.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

    Neu

    hoihoi
    Ich finde den Ansatz aus Post 9 ebenfalls gut.
    Jedoch kann ich mit meinem Wissensstand ja leider nicht beurteilen, ob das tatsächlich gut ist.
    Also ohne hier einem der Helfer zu Nahe treten zu wollen, wenn Uneinigkeit herrscht - weiß ich ja nicht wer nun Recht hat und wer nicht.

    Neu

    DerSmurf schrieb:

    ob das tatsächlich gut ist
    Es ist das Beste, was du ohne Server erreichen kannst.
    Nicht unknackbar, aber wenn du die Anwendung nur in deiner Firma an vertrauenswürdige Personen verteilst ist die Hürde hoch genug, dass keiner daran bastelt.
    Ins Netz darfst du es nicht stellen, sonst ist dein Mail-Account schnell gehackt.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

    Neu

    Alles klar, dann werde ich es so machen. Im Netz wird nur die "leere" Version des Programmes landen.
    Wer sein Programm mit Settings dann hochläd ist wohl selber schuld.

    Jedoch tue ich mich schwerer als gedacht mit dem Speichern der Mailsettings... (bisher noch ohne Verschlüsselung, da nichtmal das speichern gescheit funktioniert)
    Die "Mailsendesub" und die Form dazu sind fertig.
    Nun habe ich eine Form gebastelt mit Laden und speichern Button, einer Combobox für die Absendeadresse und 3 Textboxen für Smtp Server, Port und Passwort.
    Hier sollen die Maileinstellungen eingegeben und gespeichert werden.
    Die Mailsendesub soll dann je nach absenderadresse die entsprechenden Einstellungen (pw, Port, smtp Server) laden.
    In den Settings habe ich dafür eine String Collection erstellt und den Buttons speichern und laden folgenden Code zugewiesen:

    VB.NET-Quellcode

    1. Private Sub Speichern_Click(sender As Object, e As EventArgs) Handles speichern.Click
    2. Dim Maildaten As String = ComboBox1.Text & "#" & TextBox1.Text & "#" & TextBox2.Text & "#" & TextBox3.Text
    3. If My.Settings.EmailSettings Is Nothing Then
    4. My.Settings.EmailSettings = New Specialized.StringCollection
    5. End If
    6. My.Settings.EmailSettings.Add(Maildaten)
    7. End Sub
    8. Private Sub Laden_Click(sender As Object, e As EventArgs) Handles laden.Click
    9. If My.Settings.EmailSettings Is Nothing Then Exit Sub
    10. Dim Mailarray As List(Of String) = New List(Of String)
    11. For Each Element As String In My.Settings.EmailSettings
    12. Mailarray.Add(Element)
    13. Next Element
    14. End Sub


    Allerdings ist das ganze irgendwie ganz schön umständlich.
    Denn in der LadeSub müsste ich meine erstelle List Of String ja nun noch splitten, um nur die Absendeadresse in die Combobox zu laden.
    Dann muss ich ja aber beim Auswählen eines Eintrages der Combobox prüfen, ob sich der Eintrag im ersten Teil (vor dem ersten "#") in meinen Settings befindet und mittels Split die Textboxen entsprechend füllen.

    Gleiches würde ja für die Mailsendesub gelten.
    Außerdem muss ich beim speichern einer Mail prüfen, ob sich diese bereits in My.Settings gibt. Und eine Emaildaten ändern Sub, sollte es ja auch noch geben.

    Ich vermute ich mache das ganze komplizierter als nötig, daher frage ich einfach mal, ob es nicht auch einfacher geht, als das was ich hier veranstalte...

    Neu

    DerSmurf schrieb:

    Wer sein Programm mit Settings dann hochläd ist wohl selber schuld.
    Wer sollte denn als Endnutzer Dein Programm hochladen? Die Settings sind eh in nem Extraordner abgespeichert. Klar, wer das dann mitliefert, ist quasi selber schuld. Aber Moment! Du bist der Programmerzeuger. Wenn Du das nicht irgendwo dokumentierst, dass man gewisse Sachen nicht machen sollte, kannst Du dafür verantwortlich gemacht werden!
    Zum anderen: Du könntest ein tDS zum Abspeichern der Daten zu verwenden und diese in den selben Ordner wie die Settings schieben und nicht die XML-Daten (DeinTds.GetXML) als Klartext abspeichern, sondern vorher verschlüsselt, z.B. mit dem Code, den SpaceyX postete. Dann gibt der User zu Beginn ein Masterpasswort ein und die tDS-Daten werden geladen. Du würdest Dir die Frickelei mit den Settings ersparen. Und mit nem tDS kannst Du ja schon umgehen.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.

    Neu

    Hi,

    Du machst Dir das unnötig schwer. Eine Anwendung wie diese schreit nach einem DataSet. Hier mit Settings rumzuwurschteln ist nicht schön und unnötig aufwändig. Ich hab mir mal kurz die Zeit genommen und Dir eine kleine Beispielanwendung zusammengestellt.

    Das DataSet beinhaltet eine DataTable.



    Diese DataTable bindest Du an ein DataGridView und Du bekommst die Eingabemöglichkeit quasi geschenkt. Keine TextBoxen, keine ComboBoxen usw.



    Das DataSet bietet auch eine gute Möglichkeit Daten zu speichern und zu laden. Es genügt ein Aufruf von ​DataSetXXX.WriteXML(), bzw. DataSetXXX.ReadXML(). Das schöne dabei ist, dass diese Methoden auch einen Stream als Argument annehmen und hier kommt dann das in Post 9 gezeigte zu Einsatz. Du schreibst die Daten einfach in den CryptoStream und schon wird verschlüsselt, bzw. entschlüsselt.

    VB.NET-Quellcode

    1. Private Sub SaveData()
    2. If File.Exists(_dataPath) Then File.Delete(_dataPath)
    3. Using cS As CryptoStream = GetEncryptionStream()
    4. Me.AccountDetailsDataSet1.WriteXml(cS)
    5. End Using
    6. End Sub
    7. Private Sub LoadData()
    8. If File.Exists(_dataPath) Then
    9. Using cS As CryptoStream = GetDecryptionStream()
    10. Me.AccountDetailsDataSet1.ReadXml(cS)
    11. End Using
    12. End If
    13. End Sub
    14. Private Function GetEncryptionStream() As CryptoStream
    15. Dim key As New Rfc2898DeriveBytes(_masterPassword, New Byte() {10, 255, 255, 7, 14, 16, 23, 155})
    16. Dim x As New TripleDESCryptoServiceProvider()
    17. Dim fS As FileStream = Nothing
    18. x.Key = key.GetBytes(x.KeySize \ 8)
    19. x.IV = key.GetBytes(x.BlockSize \ 8)
    20. fS = New FileStream(_dataPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None)
    21. Return New CryptoStream(fS, x.CreateEncryptor(), CryptoStreamMode.Write)
    22. End Function
    23. Private Function GetDecryptionStream() As CryptoStream
    24. Dim key As New Rfc2898DeriveBytes(_masterPassword, New Byte() {10, 255, 255, 7, 14, 16, 23, 155})
    25. Dim x As New TripleDESCryptoServiceProvider()
    26. Dim fS As FileStream = Nothing
    27. x.Key = key.GetBytes(x.KeySize \ 8)
    28. x.IV = key.GetBytes(x.BlockSize \ 8)
    29. fS = New FileStream(_dataPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)
    30. Return New CryptoStream(fS, x.CreateDecryptor(), CryptoStreamMode.Read)
    31. End Function


    Beim Programmstart wird nun nur noch nach dem Masterpasswort gefragt und die Daten geladen, bzw. das Programm beendet, wenn die Entschlüsselung fehlschlägt. Schau Dir mein Beispiel einfach einmal an. Im .zip-Archiv -> Debug-Ordner liegt die Datei Accounts.xml, welche ein paar Daten beinhaltet. Versuch auch mal, diese Datei mit einem Texteditor zu öffnen um zu sehen, wie die verschlüsselteten Daten aussehen. Das Passwort beim Programmstart ist password123

    Falls Du Fragen hast, einfach raus damit.
    Dateien
    • WindowsApp51.zip

      (696,29 kB, 9 mal heruntergeladen, zuletzt: )
    Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
    Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o

    How to turn OPTION STRICT ON
    Why OPTION STRICT ON

    Neu

    @SpaceyX: Schön, dass wir uns auch in diesem Punkt einig sind (tDS = meine Abkürzung für typisiertes DataSet, entsprechend meiner Signatur); DerSmurf kennt den Umgang mit DataSets schon, hat er in seinem Erstthread ausführlich (dazu)gelernt. Aber gut, dass Du es nochmal so ausführlich beschrieben hast.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.

    Neu

    Holla, der Hammer! Vielen Dank für deine Mühe @SpaceyX

    DataSet und ich sind Freunde.
    Hierzu habe ich vor kurzer Zeit ein Übungsprogramm erstellt und @VaporiZed hat mir hierzu einiges (gefühlt alles) erklärt.

    Auf die Idee gekommen ein DataSet für diesen Zweck zu verwenden bin ich auch schon gekommen, weils halt (meiner Ansicht nach) sehr viel einfacher ist. Außerdem werden wohl in Zukunft (das Projekt wird ja recht umfangreich) mehrerer solcher Settings auftreten.
    Und generell fand ich es interessant alle relevanten Settings in einer xml zu speichern.
    Wenn ich nun nämlich das Programm z.B. auch auf dem PC meiner Frau verwende, brauche ich nur das HauptDataSet (also das mit den gespeicherten Daten z.B. Adressen) usw. zu kopieren und zack hab ich alle Daten auch da.
    Deswegen habe ich das HauptDataSet in einen Unterordner Data gespeichert. Hier kommt alles rein, was mein Programm an Datensätzen speichert, damit es leicht auf anderen Geräten verfügbar gemacht werden kann.
    Schön wäre es ja, wenn dies mit den Settings ebenso ginge.
    Das reduziert ja z.B. auch die Menge der Dateien, die ich bei einer Datensicherung, sichern muss.

    Nun ist ja aber das Problem, dass die EmailSettings (und auch weitere Programmsettings) nicht in mein HauptDataSet gehören.
    Die will ich ja nicht unbedingt beim kopieren der Daten mitkopieren.

    Also finde ich eure Idee hervorragend.
    Ich erstelle einen neuen Unterordner Settings, da kommt mein SettingsDataSet rein.

    Aber! Ich erinnere mich an den Anfang meines DataSet Lernprogramms.
    Da wollte ich schwachsinnigerweise anstelle einer neuen DataTable, gleich ein (sinnloses) neues (zweites) DataSet erstellen.
    Hier hat mich @VaporiZed darauf aufmerksam gemacht, wie kompliziert es ist mehrere DataTable in einem Projekt unterzubringen.

    Deswegen habe ich im aktuellen Projekt davon wieder Abstand genommen.