Zwh's Blog

ZH/EN | 一个人不在计算机专业却时不时就想折腾计算机相关东西的咸鱼

Broken Water | A Story About Database Being Hacked


On the night of the 10th at 11 o'clock, a considerable number of people in the group discovered that the background needed to be reinitialized, and the Mongo database was cleared, leaving only a ransom letter. There were previous cases, and other members, including myself, quickly checked and found that I was also affected. Innei even specifically issued a security alert for this.

Ransom letter original text: (From wibus)

We delete all databases, but download a copy to our server. The only way of recovery is you must send 0.01 BTC to bc1qmaacz9fdvnkujqlf8m547mzzh0l5t0ajn699th. You have until 48 hours to pay or data will be inaccessible. Once paid please email with code: xxxxxx and we will recover your database. please read for more information.

Of course, as an excellent CMS system, mx-space comes with automatic backup, which backs up the data of the day every day at 1 am. So I directly went to the file management and downloaded the backup files of the day and the surrounding days. On the one hand, it is convenient for recovery, and on the other hand, I shared the backup files in the group to see what people have to say.


There is no doubt that being hacked means there are vulnerabilities, as the saying goes, flies don't bite seamless eggs. Based on the descriptions of several users who were hacked in the group (including myself), there are common points for those who had their databases deleted: no firewall for the service provider/VPS, the Mongo container has mapped ports open and directly mapped to (i.e., accessible from the public network), and the database has no authentication mechanism, so it was hacked.

Solutions and Follow-up Measures#

Disable unnecessary port mapping#

In the initial security alert, Innei mentioned one point, which is to disable unnecessary port mapping.

In the previous version of the Docker Compose file, both mongo and Redis had a port mapping, and since no IP was specified, Docker's default behavior would directly map it to, exposing it to the public network. However, considering that mongo and Redis are in the same network as the main container and there is no problem with the connection, the port mapping can be completely removed. The following is a partial content after removing the port mapping.

    container_name: mongo
    image: mongo
      - ./data/db:/data/db
      - app-network
    restart: always
    image: redis
    container_name: redis
      - app-network
    restart: always

    driver: bridge

Set up a firewall#

Secondly, considering that this is a problem caused by port exposure, a UFW firewall was installed on the server to block ports. Of course, after installing it, the SSH port and 1Panel port of the server need to be allowed before starting the firewall, otherwise, it would be embarrassing if you can't connect to the server/access the panel (.

sudo apt update
sudo apt install ufw
sudo ufw allow 22/tcp # If SSH is running on a non-standard port, replace the above command with the corresponding SSH port.
sudo ufw allow 8090/tcp # Allow the 1Panel system port, replace 8090 with the panel's port
sudo ufw enable # UFW, start!

1Panel also has a corresponding firewall function, so after starting UFW, you can directly manage UFW in the panel, which is very convenient for slacking off.

Reduce container privileges#

Thanks to timo's suggestions and guidance. The operation is very simple, but my understanding of Linux is not deep enough, so I searched for 114514 commands on Baidu when I was tinkering (.

First, directly modify the Compose configuration file and add a line user: node, as follows (excerpt from the file):

version: '3.8'

    container_name: mx-server
    image: innei/mx-server:latest
    command: bash ./
      - TZ=Asia/Shanghai
      - NODE_ENV=production
      - JWT_SECRET
      - ./data/mx-space:/root/.mx-space
      - '2333:2333'
      - mongo
      - redis
      - mongo
      - redis
      - app-network
    restart: always
    user: node
      interval: 1m30s
      timeout: 30s
      retries: 5
      start_period: 30s

After adding this constraint, the container will run commands as the node user (UID 1000), and the process ownership on the host machine will also use UID 1000 (there may not be a user with UID 1000 in the system), instead of the default root, which reduces the security risk.

However, since the container runs with UID 1000, the data inside the container is placed under /home/node/, and the ownership of the data folder is still in the hands of root. Therefore, it is necessary to modify the container configuration file to change the location of the data inside the container and the ownership of the corresponding mapped folder on the host machine. These are basic operations in Linux, and if you don't want to use the command line, you can directly make the changes in the panel's file management, so I won't elaborate on them further.

Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.