MongoDB ユーザー認証設定は必ずしましょう

# 概要

下記のURLの記事は2019年5月8日の記事でMongoDBがパスワードなしでアクセス出来て2.75億件のデータが取得可能な状態だったと伝えています。

https://www.bleepingcomputer.com/news/security/over-275-million-records-exposed-by-unsecured-mongodb-database/

また、過去(2015年)の記事でも40,000台の保護されていないMongoDBが見つかったと伝えています。

https://thehackernews.com/2015/02/mongodb-database-hacking.html

よって、MongoDBをインストールしたらユーザー認証の設定は必ずしましょう。さらに、ユーザ認証の設定をせずにIPのバインド設定がlocalhost以外の設定にしてしまったらもう最悪です。

[「MongoDBを始めた頃に知っていたら、と思う14のこと」](https://www.infoq.com/jp/articles/Starting-With-MongoDB)の記事でも最初にユーザー認証のことが書かれています。

以降ではユーザー認証の設定とユーザーの登録を行いますが、ユーザー管理については説明していません。

# ユーザー認証の設定パラメータ

コマンドラインオプションの場合は`--auth`を追加します

```
mongod --dbpath <directory>  --auth
```

設定ファイルの場合`authorization`を `enabled`にします

```yaml
security:
   authorization: enabled
```
# 最初のスーパーユーザーの登録

まず、ユーザー認証を有効にして`mongod`を立ち上げます。

**スーパーユーザー**とはLinuxのrootのようなもです。 MongoDBは決まったユーザーIDはありませんので、スーパーユーザーは何によってきまるかというとそれは**ロール**です。ここでは**ロール**としてビルトインされている`root`を使います。 

https://docs.mongodb.com/manual/reference/built-in-roles/index.html#superuser-roles

以下ではユーザーIDを`mongo`、パスワードを`18fuw63x`としています。

```
$ mongo
> use admin
switched to db admin
> db.createUser({user:"mongo", pwd:"18fuw63x", roles:["root"]})
Successfully added user: { "user" : "mongo", "roles" : [ "root" ] }
> exit
```

# ユーザーID/パスワードを指定してMongoDBに接続する

`-u`を付けます。

```
$ mongo -u mongo
MongoDB shell version v4.0.9-31-g52532c50a4
Enter password:
```

mongodbコネクションストリングでは

```
$ mongo mongodb://mongo@localhost/
MongoDB shell version v4.0.9-31-g52532c50a4
Enter password:
```

パスワードも一緒に指定するときは以下のようにしますが、使わないようにしてください。理由はlinuxのpsコマンドなどでパスワードが他人に見られてしまいます。

```
$ mongo -u mongo -p 18fuw63x

$ mongo mongodb://mongo:18fuw63x@localhost/
```

その他の方法として、MongoDBに接続してから認証を実行する。

```
$ mongo
> use admin
switched to db admin
> db.auth("mongo","18fuw63x")
1
```

また、下記のようにデータベースを指定するとエラーになります。

```
$ mongo test -u mongo

$ mongo mongodb://mongo@localhost/test
```
理由は指定したDB名(`test`)が認証用のDBとして使われるためです。ユーザーを登録したのは`admin`です。そこで、下記のように認証用のDBを指定します。

```
$ mongo test -u mongo --authenticationDatabase admin

$ mongo mongodb://mongo@localhost/test?authSource=admin
```

# コンソールからユーザーID・パスワードの入力を省略したいとき

ユーザーホームに`.mongorc.js`というファイルを作り、下記のスクリプトを記述します。`.mongorc.js`はmongoを起動する際に読み込まれるJavaScriptファイルです。パスワードが書かれているので`.mongorc.js`のアクセス権の設定には注意してください。

```javascript:.mongorc.js
db.getSiblingDB("admin").auth("mongo","18fuw63x")
```

# ユーザー登録してもまだ安心できない

`--auth`付きで起動する場合は必ずバッチファイルを用意して`mongod`を起動するか、設定ファイルを使用しましょう。

理由として、`mongod`を再起動するときに`--auth`パラメータ無しで再起動するとユーザー認証なしでも使えてしまうからです。これは設定ファイルで`authorization: enabled`を付けなくても同様ですが、一度設定して設定を消すことは無いと思います。しかし、`--auth`を付け忘れることはありがちです。
次に`--auth`を付けなかったときの場合分けは次の通りです。

1. 正しいユーザー・パスワードを指定したときはOK
1. 間違ったユーザー・パスワードを指定したときはエラー
1. ユーザー認証しなかったときはOK

1.,2.は経験するので`--auth`付きだと思ってしまいます。しかし、3.はやらないので`--auth`無しとは気が付きにくいです。特に`.mongorc.js`に`auth()`コマンドを記述した場合はなおさら気が付きません。



psコマンドや設定ファイルを確認する以外に、どうやったら起動中のMongoDBのユーザー認証有無が分かるか調べたら、次のコマンドが有効でした。設定ファイルの場合も同様で`parsed.security.authorization`を見れば分かります。ユーザー認証を指定しない場合は`parsed.security.authorization`が無いようです。

```
> db.adminCommand( { getCmdLineOpts: 1  } )
{
        "argv" : [
                "mongod",
                "--dbpath",
                "db",
                "--auth"
        ],
        "parsed" : {
                "security" : {
                        "authorization" : "enabled"
                },
                "storage" : {
                        "dbPath" : "db"
                }
        },
        "ok" : 1
}
>
```

これ以外に無いかいろいろ試した結果、存在しない`system.???`を`find()`すると分かります。どのバージョンでもなのかは分かりません。

ユーザー認証が有効の場合

```
> db.system.xx.find()
Error: error: {
        "ok" : 0,
        "errmsg" : "not authorized on test to execute command { find: \"system.xx\", filter: {}, lsid: { id: UUID(\"80d87cee-460f-4f97-b965-552a8417d604\") }, $db: \"test\" }",
        "code" : 13,
        "codeName" : "Unauthorized"
}
>
```

ユーザー認証が無効の場合

```
> db.system.xx.find()
>
```