Schlagwort: Netzwerk

Zertifikat einer Adresse mit C# ermitteln 👍 👎

Manchmal möchte man gerne Zertifikate von Webseiten automatisiert abrufen. Im Rahmen eines Monitorings bietet es sich beispielsweise an, die Gültigkeitsdauer zu überwachen. Dazu eine beispielhafte Implementierung:
Zertifikat einer Webseite ermitteln
010203040506070809
public static X509Certificate2 GetCertificate(string hostName, int port = 443) {    using(TcpClient client = new TcpClient(hostName, port)) {        using(SslStream stream = new SslStream(client.GetStream())) {            stream.AuthenticateAsClient(hostName);
return new X509Certificate2(stream.RemoteCertificate); } }}
Die Implementierung ist dabei – wie üblich – auf das Wesentliche beschränkt; es findet keine umfassende Problembehandlung statt. Sofern die Methode in größerem Kontext zum Einsatz kommen soll, empfiehlt sich auf jeden Fall ein Blick auf die beteiligten Klassen TcpClient, SslStream und X509Certificate2 im MSDN.

Nun stehen uns diverse Informationen zur Verfügung; einige Beispiele zur Ausgabe auf der Konsole:
Methode verwenden und Informationen ausgeben
010203040506
X509Certificate2 certificate = GetCertificate("coders-online.net");
Console.WriteLine(certificate.Subject); // "CN=coders-online.net"
Console.WriteLine(certificate.NotBefore); // 30.04.2017 08:01:00Console.WriteLine(certificate.NotAfter); // 29.07.2017 08:01:00
Im Besonderen sei abschließend noch auf RemoteCertificateValidationCallback hingewiesen, mit welchem sich das Verhalten bei Zertifizierungsfehlern beeinflussen lässt.

HTTP-Statuscode einer Webseite mit C# ermitteln 👍 👎

Hin und wieder kann es – beispielsweise im Rahmen eines Monitorings - erforderlich sein, den HTTP-Statuscode einer Webseite zu ermitteln. Das lässt sich mit C# grundsätzlich bereits mit wenigen Zeilen lösen:
Hilfsmethode
01020304050607
public static HttpStatusCode GetHttpStatusCode(Uri uri) {    HttpWebRequest httpWebRequest = WebRequest.CreateHttp(uri);
using(HttpWebResponse webResponse = (HttpWebResponse) httpWebRequest.GetResponse()) { return webResponse.StatusCode; }}
In der Praxis möchte man vermutlich zumindest noch das Weiterleitungs- und Zwischenspeicherverhalten konfigurieren, daher bietet es sich an, unsere Methode entsprechend zu erweitern:
Hilfsmethode (erweitert)
010203040506070809101112131415
public static HttpStatusCode GetHttpStatusCode(    Uri uri,    bool allowAutoRedirect = false,    RequestCacheLevel requestCacheLevel = RequestCacheLevel.NoCacheNoStore) {    HttpWebRequest httpWebRequest = WebRequest.CreateHttp(uri);    {        httpWebRequest.AllowAutoRedirect = allowAutoRedirect;        httpWebRequest.CachePolicy = new RequestCachePolicy(requestCacheLevel);    }
using(HttpWebResponse webResponse = (HttpWebResponse) httpWebRequest.GetResponse()) { return webResponse.StatusCode; }}
Die Verwendung gestaltet sich nun sehr intuitiv:
Hilfsmethode verwenden
01020304
Uri uri = new Uri("https://ccc.coders-online.net/");
HttpStatusCode statusCode = GetHttpStatusCode(uri); // Found (302)statusCode = GetHttpStatusCode(uri, true); // OK (200)
Wenn ihr den Wert der HttpStatusCode-Aufzählung zu int konvertiert erhaltet ihr im Übrigen den entsprechenden numerischen Wert nach Definition des HTTP-Protokolls.

Zeitserver mit C# abfragen 👍 👎

Im Folgenden möchte ich drei Möglichkeiten vorstellen, wie man mit C# über ein Netzwerk eine Zeitangabe abrufen kann. Es gilt zu beachten, dass es sich um sehr reduzierte Beispiele handelt und beispielsweise keine umfassende Fehlerbehandlung berücksichtigt wird. Wir verwenden standardmäßig jeweils einen Server des NIST.

Time Protocol

Beim Time Protocol handelt es sich um ein sehr einfaches (und etwas betagtes) Protokoll. Der Dienst steht für gewöhnlich unter Port 37 zur Verfügung. Details zum Protokoll können in RFC 868 nachgelesen werden.

Methode zur Zeitabfrage per Time Protocol
010203040506070809101112131415161718
public static DateTime GetTime(string host = "time.nist.gov", int port = 37) {    using(TcpClient client = new TcpClient(host, port)) {        using(NetworkStream networkStream = client.GetStream()) {            // Antwort lesen            byte[] timeBuffer = new byte[4];            networkStream.Read(timeBuffer, 0, timeBuffer.Length);
// Byte-Reihenfolge ggf. umkehren if(BitConverter.IsLittleEndian) { Array.Reverse(timeBuffer); }
return (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)) .AddSeconds(BitConverter.ToUInt32(timeBuffer, 0)) .ToLocalTime(); } }}

Daytime Protocol

Beim Daytime Protocol verhält es sich ähnlich, ist jedoch textbasiert. Der Dienst steht für gewöhnlich unter Port 13 zur Verfügung. Details zum Rückgabeformat können in RFC 867 und der Dienstbeschreibung des NIST nachgelesen werden – wir werden uns auf den minimalen Part (Datum und Uhrzeit) beschränken.

Methode zur Zeitabfrage per Daytime Protocol
010203040506070809101112131415161718
public static DateTime GetDaytime(string host = "time.nist.gov", int port = 13) {    using(TcpClient client = new TcpClient(host, port)) {        using(StreamReader streamReader = new StreamReader(client.GetStream())) {            // Antwort lesen und auftrennen            string[] response = streamReader.ReadToEnd().Split(' ');
// Datum und Uhrzeit ermitteln int[] date = Array.ConvertAll(response[1].Split('-'), Int32.Parse); int[] time = Array.ConvertAll(response[2].Split(':'), Int32.Parse);
return new DateTime( CultureInfo.CurrentCulture.Calendar.ToFourDigitYear(date[0]), date[1], date[2], time[0], time[1], time[2], DateTimeKind.Utc ).ToLocalTime(); } }}

Network Time Protocol (NTP)

Beim Network Time Protocol handelt es sich um einen weit verbreiteten Standard zur Zeitsynchronisierung in Computernetzwerken über Port 123. Details zum Protokoll können in RFC 5905 nachgelesen werden – wir werden uns wieder auf einen minimalen Teil beschränken. Als Server bietet sich hier natürlich auch die PTB an.

Methode zur Zeitabfrage per NTP
010203040506070809101112131415161718192021222324252627282930313233343536
public static DateTime GetNetworkTime(string host = "time.nist.gov", int port = 123) {    using(UdpClient client = new UdpClient()) {        client.Connect(host, port);
// Anfrage senden const byte header = ( (0 << 6) // LI = 0 (keine Warnung; zur Verdeutlichung ausformuliert) | (4 << 3) // VN = 4 (Version) | (3 << 0) // Mode = 3 (Client) ); // 00_100_011
byte[] request = new byte[48];
request[0] = header;
client.Send(request, request.Length);
// Antwort lesen IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 0); byte[] response = client.Receive(ref endPoint);
// Daten ab Offset aus ggf. umgekehrter Byte-Reihenfolge ermitteln const int offset = 40;
if(BitConverter.IsLittleEndian) { Array.Reverse(response, offset, 4); Array.Reverse(response, (offset + 4), 4); }
ulong integerPart = BitConverter.ToUInt32(response, offset); ulong fractionalPart = BitConverter.ToUInt32(response, (offset + 4));
return (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)) .AddMilliseconds((integerPart * 1000) + ((fractionalPart * 1000) / UInt32.MaxValue)) .ToLocalTime(); }}
Insbesondere bei diesem letzten Beispiel möchte ich noch einmal explizit darauf hinweisen, dass es sich um eine sehr reduzierte Implementierung handelt. Die Rückgabe erfolgt bei allen Beispielen jeweils bereits in lokaler Zeit.

Kontakt für Domain-Missbrauch mit C# von abuse.net ermitteln 👍 👎

In meinem vorherigen Beitrag habe ich bereits ein paar allgemeine Gedanken und Lösungsansätze zum – leidigen – Thema Spam niedergeschrieben. In diesem Beitrag soll es, wie bereits angekündigt, darum gehen, den bereits kurz beschriebenen Dienst von abuse.net automatisiert (mit C#) anzusprechen.

Der Anbieter stellt alle wesentlichen Informationen zur Umsetzung zur Verfügung. Dort wird der Zugriff per DNS oder Whois beschrieben. Das Herunterladen der (Roh-)Daten ist jedoch nicht möglich. Beachtet bitte außerdem die Nutzungsbedingungen, so ist der kommerzielle Einsatz beispielsweise nicht ohne Weiteres gestattet.

Es werden bei den folgenden beispielhaften Implementierungen lediglich die ermittelten eMail-Adressen an sich zurückgeliefert und keine – ebenfalls verfügbaren – Kommentare, welche insbesondere Angaben zur Quelle enthalten. Des Weiteren findet keine Reduzierung von Duplikaten statt. Beides kann bei Bedarf aber selbstverständlich noch angepasst bzw. ergänzt werden. Die Methoden sind darüber hinaus variadisch und können daher mit mehreren Argumenten (oder einem Feld) aufgerufen werden.

Zugriff per Whois

Eine – auch mit .NET-Bordmitteln – sehr einfache Möglichkeit der Abfrage des für Missbrauch zuständigen Kontakts zu einer Domain ist das Whois-Protokoll. Die Abfrage erfolgt per TCP bei whois.abuse.net:43.

Zugriff per Whois
01020304050607080910111213141516171819202122
public static IEnumerable<string> GetAbuseContactList(params string[] domainList) {    using(TcpClient client = new TcpClient("whois.abuse.net", 43)) {        using(NetworkStream stream = client.GetStream()) {            byte[] request = Encoding.UTF8.GetBytes(String.Join(" ", domainList) + "\r\n");            stream.Write(request, 0, request.Length);
using(StreamReader streamReader = new StreamReader(stream)) { while(!streamReader.EndOfStream) { string response = streamReader.ReadLine();
if(response.Contains("(")) { response = response.Substring(0, response.IndexOf('(')); }
if(!String.IsNullOrWhiteSpace(response)) { yield return response.Trim(); } } } } }}

Es sei jedoch darauf hingewiesen, dass diese Methode vom Anbieter nicht (mehr) empfohlen und möglicherweise eingestellt wird. Darüber hinaus ist das Abfragen im Vergleich zu DNS um den Faktor 2-3 langsamer.

Zugriff per DNS

Eine sehr effiziente Möglichkeit der Abfrage stellt das DNS durch Auflösung des Bezeichners {domain}.contacts.abuse.net dar; die Rückgabe der Adressen erfolgt in Form von TXT-Records.

Leider stellt das .NET-Framework jedoch nur einfache Aufgaben der Adress- und Namensauflösung per DNS-Klasse zur Verfügung. Da die Details der Kommunikation – zumindest in diesem Beitrag – nicht weiter vertieft werden sollen, greifen wir auf eine externe Laufzeitbibliothek zu, die wir mit NuGet bequem per Visual Studio beziehen können. Für das folgende Beispiel verwende ich ARSoft.Tools.Net, welche sich bereits in einigen meiner Entwicklungen im Produktiveinsatz bewährt hat und zudem auch relativ einfach zu verwenden ist.

Zugriff per DNS
010203040506070809101112
public static IEnumerable<string> GetAbuseContactList(params string[] domainList) {    foreach(string domain in domainList) {        DnsMessage response = DnsClient.Default.Resolve(            DomainName.Parse(domain + ".contacts.abuse.net"),            RecordType.Txt        );
foreach(TxtRecord record in response.AnswerRecords) { yield return record.TextData; } }}
Unabhängig davon, welche der beiden Methoden ihr verwendet, gestaltet sich die Verwendung sehr einfach:
Verwendung der Methode(n)
0102030405060708
  // ["abuse@coders-online.net"]IEnumerable<string> abuseContactList = GetAbuseContactList("coders-online.net");
// ["abuse@google.com", "abuse@microsoft.de"]abuseContactList = GetAbuseContactList("google.com", "microsoft.de");
// ["abuse@facebook.com", "spamreport@spamreport.facebook.com"]abuseContactList = GetAbuseContactList("facebook.com");
Auf eine angemessene Fehlerbehandlung habe ich wie üblich zur besseren Verdeutlichung des eigentlichen Anliegens verzichtet, sollte von euch bei der Verwendung aber nicht vernachlässigt werden.

Umgang mit Spam 👍 👎

Spam ist leider nach wie vor ein sehr großes Problem im Internet. Die genauen Arten und Hintergründe zum massenhaften Spam-Versand und insbesondere Ansätze zur grundlegenden Verbesserung der Protokolle sind sicherlich interessant und einen separaten Beitrag wert, für diesen Beitrag soll es jedoch darum gehen, wie man derzeit Spam vermeiden und was man mit bereits eingegangenem Spam anfangen kann, außer ihn zu löschen.

Versand von Spam vermeiden

Idealerweise gelangt Spam erst gar nicht in Umlauf; das spart unnötige Datenverarbeitung mit allen Konsequenzen und theoretisch jeglichen weiteren Aufwand. In der Praxis ist das nur begrenzt möglich, da die Protokolle sehr liberal entworfen wurden – was grundsätzlich lobenswert, bei Spam jedoch problematisch ist.

Inhaber von Postfächern

Was erst einmal selbstverständlich klingt, wird in der Praxis leider oftmals vernachlässigt: Sichere Passwörter tragen wesentlich dazu, dass eigene Benutzerkonten nicht als Absender für Spam fungieren. Wie üblich sollte das Passwort natürlich auch nur für genau diesen Zweck verwendet werden. Problematisch sind diese grundsätzlich "guten" Benutzerkonten insbesondere deshalb, weil sie meist aus den ordentlich konfigurierten Netzen größerer Anbieter stammen, die nicht einfach pauschal als "bekannte" Spam-Versender blockiert werden können.

Nicht zuletzt sollte auch sichergestellt sein, dass sich keine Schadprogramme auf dem eigenen Rechner befinden, welche beispielsweise eMails an Kontakte aus dem Adressbuch zu senden versuchen. Auch eigene Web-Anwendungen sollten immer auf dem aktuellsten Stand sein.

Betreiber von Mailservern

Ein ordentlich konfigurierter Mailserver, der nur authentifizierte Benutzer zulässt und insbesondere nicht als offenes Mail-Relay fungiert, ist selbstverständlich Grundvoraussetzung, um Spam bereits im Ansatz zu vermeiden. Die genaue Konfiguration hängt vom eingesetzten Dienst ab und würde den Rahmen dieses Beitrages sprengen. Üblicherweise gibt es dazu jedoch umfassende Leitfäden der Anbieter.

Ein weiterer Ansatz ist es, Spam an sich zu erkennen und von der weiteren Verarbeitung, insbesondere dem externen Versand, auszuschließen. So ist es beispielsweise möglich, den Umfang der Versandtätigkeit zu überwachen und ggf. auch einzuschränken. Ein durchschnittlicher Webhosting-Kunde wird beispielsweise eher selten mehrere tausend eMails in der Minute einreichen – hier können serverseitige Maßnahmen greifen, die derartige Auffälligkeiten erkennen. Möglicherweise ist es sinnvoll, die Anzahl der Nachrichten pro Zeiteinheit einzuschränken und nur nach vorheriger Rücksprache mit dem Kunden vorübergehend freizugeben.

Darüber hinaus ist nicht zu unterschätzen, dass die Zugangsdaten sehr vieler Benutzerkonten oftmals sehr unsicher gewählt werden und daher leicht zu "ermitteln" sind, bzw. von anderen Portalen kopiert werden können, sofern gleiche Zugangsdaten verwendet wurden. Es sollte daher überwacht werden, ob auffällige Zugriffe erfolgen – dieses Vorhaben kann beispielsweise durch Programme wie Fail2ban unterstützt werden. Auch Web-Anwendungen der Kunden, die oftmals nicht auf dem aktuellsten Stand sind, sind zu beachten.

Empfang von Spam vermeiden

Die meisten Anbieter leisten hier Vorarbeit, sodass der gröbste Spam gar nicht erst in dein Postfach gelangt, sondern bereits serverseitig abgewiesen wird. Alternativ wird teilweise auch nur eine Markierung eingesetzt, beispielsweise indem dem Betreff "[SPAM]" vorangestellt wird.

Inhaber von Postfächern

Du solltest deine eMail-Adresse(n) auf jeden Fall nicht überall verstreut angeben. Die meisten eMail-Programme bieten darüber hinaus standardmäßig oder aber zumindest über entsprechende Erweiterungen die Möglichkeit, Spam über bestimmte Kriterien zu erkennen. Die Erkennungsrate steigert sich bei den etwas besseren Programmen mit der Zeit, es können jedoch auch falsche Einstufungen (false positive) vorkommen.

Weniger der direkten Vermeidung, als vielmehr der Aufklärung dienlich ist der Ansatz, für jeden Dienst, bei dem du deine eMail-Adresse angeben sollst, eine individuelle und nicht erratbare Adresse zu erzeugen, die du nur dort verwendest (jedoch durchaus in einem Postfach mit anderen zusammen sammeln kannst). So kannst du feststellen, aus welcher Richtung du Spam erhältst und mit dem Anbieter Rücksprache halten, wie deine nur dort verwendete Adresse, die nur ihm bekannt sein sollte und nicht öffentlich einsehbar ist, Spam erhalten konnte. Möglicherweise wurde in einem solchen Fall unberechtigt auf die Daten(-bank) zugegriffen. Indirekt lässt sich mit diesem Ansatz Spam vermeiden, indem die fragliche Adresse schlicht gelöscht und ggf. eine neue erzeugt wird.

Betreiber von Mailservern

Es besteht grundsätzlich die Möglichkeit, einzelnen IP-Adressen oder sogar ganzen Adressbereichen die Verbindung zu verweigern. Darüber hinaus können eingehende Nachrichten über externe Programme wie SpamAssassin oder auch über bereits im Mailserver implementierte Funktionalität auf Spam geprüft werden.

Eine weitergehende Option ist das Einbinden von DNS-basierten Blacklisten, wobei die IP-Adresse eingehender Verbindungen gegen entsprechende Dienste geprüft und darauf basierend entschieden wird, ob die Nachricht angenommen wird. Die Anbieter dieser Dienste sammeln dazu auffällige Adressen in ihren Datenbanken. Bekannte Anbieter, die ich auch selbst auf meinem Server einsetze, sind beispielsweise die Folgenden:

Bitte informiert euch jedoch – insbesondere bei geplantem kommerziellen Einsatz – über die genauen Nutzungsbedingungen. Wie ihr die entsprechenden Dienste automatisiert mit C# ansprechen könnt, habe ich bereits in einem separaten Beitrag beschrieben.

Zuletzt sei noch auf die Möglichkeit des Greylistings hingewiesen, wobei eingehende eMails bei unbekannten Absendern zuerst abgewiesen werden. Hierbei wird ausgenutzt, dass korrekt konfigurierte Mailserver die Zustellung nach einer gewissen Zeit nochmals versuchen – Spammer vermeiden diesen Aufwand oftmals. Der Erfolg mit dieser Methode kann durchaus beachtlich sein, sorgt jedoch leider auch dafür, dass "dringende" eMails teilweise nur stark verzögert eingehen, was natürlich lästig sein kann.

Spam behandeln

Ist trotz aller Maßnahmen eine Spam-Nachricht im eigenen Postfach gelandet, so sollte man selbstverständlich keinesfalls irgendwelche Links anklicken (auch keine angeblichen "abmelden"-Links), Dateianhänge öffnen oder dem (angeblichen) Absender antworten. Bevor die Nachricht jedoch gelöscht wird, kann man durchaus noch sinnvolle Sachen damit anstellen.

Den Betreibern der verantwortlichen Server kann man eine Information zukommen lassen, dass man Spam erhalten hat. Dies ermöglicht dem Betreiber des Mailservers, der die Nachricht verschickt bzw. weitergeleitet hat, seinen Kunden darauf hinzuweisen oder ggf. auch zu sperren. Betreiber von Servern, auf die durch Links in den Spam-Nachrichten verwiesen wird, können u. U. auch interessiert sein, da nicht selten Sicherheitslücken auf legitimen Webseiten missbraucht werden, um beispielsweise Schadcode zu verbreiten. Größere Unternehmen reagieren nicht selten auch mit ihrer Rechtsabteilung, wenn beispielsweise (Ruf-)Schädigung durch Phishing droht, was dem Ganzen nochmals etwas mehr Nachdruck verleiht.

Die meisten direkt sichtbaren Domain-Angaben bei Spam-Nachrichten sind jedoch gefälscht, d. h. eine einfache Whois-Abfrage ist hier meist nicht zielführend. Vielmehr muss der eigentliche Server ausfindig gemacht (→ Untersuchung der eMail-Kopfzeilen) und über die zuständige Regional Internet Registry der passende Ansprechpartner ermittelt werden.

Leider ist es jedoch nicht ganz trivial, die richtigen Adressaten herauszufinden und für Laien nahezu unmöglich. Hier kann es eher passieren, dass man die Falschen "beschuldigt" und diese entsprechende Meldungen ab einem gewissen Ausmaß schlicht ignorieren. Hier helfen jedoch Anbieter weiter, die sich darauf spezialisiert haben:

  • abuse.net
    Dieser Dienst ermittelt zu einer Domain den Kontakt für Missbrauchsfälle, jedoch muss man dazu die "verantwortliche" Domain bereits kennen. Wie man diesen Dienst automatisiert (mit C#) ansprechen kann, erkläre ich in einem separaten Beitrag.
  • SpamCop
    Dieser Dienst nimmt die meiste Arbeit ab und ermittelt bei Eingabe einer vollständigen Spam-Nachricht (inkl. Kopfzeilen) die passenden Empfänger. Selbstverständlich empfiehlt sich jedoch eine Kontrolle, da in bestimmten Konstellationen die Automatismen unzureichend sind. An eine spezielle Adresse können Spam-Nachrichten auch direkt weitergeleitet werden.

Selbstverständlich ist das keine Garantie dafür, dass sich der Anbieter auch ernsthaft darum kümmert – hier gibt es leider auch uninteressierte Zeitgenossen. Ich melde jedoch generell alle Spam-Nachrichten und habe damit durchaus überwiegend gute Erfahrungen gemacht und erhalte hin und wieder auch persönliche Antworten.

Projektverweise

Kategorien / Archiv  |  Übersicht RSS-Feed

Schlagworte

Suche