MySQL als Ziel von DoS-Angriffen

Wie bei allen populären Dienste wird auch nach offenen MySQL-Installationen gescannt. Diese Server sind oft auf dem Internet erreichbar, sollen aber nur für bestimmte Quelladressen und Nutzer offen stehen. Das geht mit Bordmitteln. In der Standardeinstellung kann ein MySQL Server leicht blockiert werden.

Wir werden angegriffen!

Ein Kunde meldet auf seinem Server eine ungewöhnliche Fehlermeldung.

mysql_connect(): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (11)

#0 [internal function]: __error(2, 'mysql_connect()...', '.../www/...', 58, Array)
#1 .../library/Contao/Database/Mysql.php(58): mysql_connect('localhost:3306', 'LOGIN', 'PASSWORD')
#2 system/modules/core/library/Contao/Database.php(77): Contao\Database\Mysql->connect()
#3 system/modules/core/library/Contao/Database.php(161): Contao\Database->__construct(Array)
#4 [internal function]: Contao\Database::getInstance()

Ja, tatsächlich ein Stacktrace mit vollem Namen und Paßwort. Und das ist nicht so selten, wie eine Suche zeigt.

Aber wie kommt es dazu? Kann ein MySQL Server wirklich eine lokale Verbindung ablehnen?

Ursachenforschung

MySQL lehnt neue Verbindungen ab, wenn sein max-connection Limit erreicht ist.

Ein Blick in die Prozeßtabelle offenbart einen sehr ungewöhnlichen Anblick:

mysql> show processlist;
+--------+----------------------+---------------------------+----------+---------+------+------
| Id     | User                 | Host                      | db       | Command | Time | State
+--------+----------------------+---------------------------+----------+---------+------+------
| 374649 | root                 | localhost                 | mysql    | Query   |    0 | NULL
| 374693 | unauthenticated user | *******************:41209 | NULL     | Connect | NULL | Reading
| 374694 | unauthenticated user | *******************:41212 | NULL     | Connect | NULL | Reading
| 374695 | unauthenticated user | *******************:41213 | NULL     | Connect | NULL | Reading
| 374696 | unauthenticated user | *******************:41216 | NULL     | Connect | NULL | Reading
| 374697 | unauthenticated user | *******************:41217 | NULL     | Connect | NULL | Reading
| 374698 | unauthenticated user | *******************:41219 | NULL     | Connect | NULL | Reading
| 374699 | unauthenticated user | *******************:41221 | NULL     | Connect | NULL | Reading
| 374700 | unauthenticated user | *******************:41224 | NULL     | Connect | NULL | Reading
| 374701 | unauthenticated user | *******************:41225 | NULL     | Connect | NULL | Reading
| 374702 | unauthenticated user | *******************:41228 | NULL     | Connect | NULL | Reading
[...]

Der komplette Server ist mit dem Verarbeiten von Anmeldungen aus dem Internet belegt. Es würde also nichts helfen, die Limits für max-connections anzuheben.

Wenn man das selbst sehen will, kann man von Hand einfach mal einen Verbindungsaufbau probieren:

$ telnet mysql.ser.ver 3306
8
<binärdaten>

Wie bekommt man aber diese vielen Verbindungen weg?

Lösung

Die häufigste Antwort, die man bekommt (auch von MySQL), ist, eine Firewall vor den Server zu stellen und den Port zu blocken. Das ist aber offenkundig unbefriedigend.

Die zweithäufigste Antwort ist, die Netzwerkfunktionalität von MySQL abzuschalten. Das macht aber erhebliche Probleme, weil nicht alle Programmierumgebungen über Unix-Domain-Sockets arbeiten können (Java) oder gar externer Zugriff für ein Webfrontend (phpmyadmin) oder vom Kundensystem aus nötig ist.

Wie kommt also MySQL dazu, eine Verbindung überhaupt anzunehmen, wo es doch keine Möglichkeit gibt, einen Nutzer von dieser unbekannten Quelle zu authentisieren?

mysql> use mysql;
mysql> select user, host from user;
+------------+-----------------------+
| user       | host                  |
+------------+-----------------------+
| abcd       | %                     | 
| root       | 127.0.0.1             | 
[...]

Ja, was zur Hölle sucht denn hier der Wildcard?

Das ist das Ergebnis der typischen Anleitungen im Internet. Man möge doch bitte 'user'@'localhost' und 'user'@'%' freigeben.

Sobald man die Zeile mit dem Wildcard durch eine qualifizierte Hostangabe ersetzt, hört der Spuk auf.

mysql> update user set host = 'a.b.c.d' where host = '%';
Query OK, 1 row affected (0,00 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0,00 sec)

Und der Test scheitert grandios:

$ telnet mysql.ser.ver 3306
NHost '*****************' is not allowed to connect to this MySQL server
Connection closed by foreign host.

Und die Prozeßtabelle ist wieder aufgeräumt.

Wenn man diese Schritte hinter sich hat, findet man auch die passende Dokumentation.

Ist es nicht schön?

Post a comment

Related content