มาทำให้ MongoDB ปลอดภัยขึ้นด้วยการ Enable Auth กันดีกว่า

Published on
Web Development
2017/01/how-to-enable-authentication-in-mongodb
Discord

เนื่องจากว่าวันนี้ได้อ่านเจอบทความเกี่ยวกับ MongoDB จากนี้ ฐานข้อมูล MongoDB ถูกเจาะเป็นจำนวนมาก ข้อมูลถูกล้างและเรียกค่าไถ่ แล้วรู้สึกว่า โอ้โห! Default ของ MongoDB ไม่ได้กำหนด Auth มาให้ ทำให้ user ไหน ก็สามารถ access เข้า database ของเราก็ได้นี่หว่า

วิธีแก้ นี่เลยครับ supo apt-get uninstall mongodb-org ลบมันทิ้งเลย ทีนี้ก็จะไม่โดนเจาะแล้ว ต้องแก้ด้วยต้นเหตุ เราจะแก้ด้วยปลายเหตุด้วยการ config ให้เสียเวลาทำไม ฉลาดไหม

.

.

.

จะบ้าเหรอ!!

ก็เลยลองนั่งไลๆ่อ่าน MongoDB Security Checklist ของทาง MongoDB รู้สึกว่ามันน่าจะมีประโยชน์ ก็เลยเขียนเป็นบทความมาแชร์กันครับ

Environment

- Ubuntu 16.04
- MongoDB 3.4.1

1. Start MongoDB

เริ่มต้น หากว่า MongoDB ไม่ได้รันอยู่ ก็สั่งรันได้ด้วยคำสั่ง

$ sudo service mongod start

หรือ

$ mongod

ลองตรวจสอบ สถานะดูด้วยคำสั่ง

$ sudo service mongod status

● mongod.service - High-performance, schema-free document-oriented database
   Loaded: loaded (/lib/systemd/system/mongod.service; disabled; vendor preset: enabled)
   Active: active (running) since Sun 2017-01-08 10:25:41 UTC; 3s ago
     Docs: https://docs.mongodb.org/manual
 Main PID: 1958 (mongod)
    Tasks: 17
   Memory: 32.0M
      CPU: 574ms
   CGroup: /system.slice/mongod.service
           └─1958 /usr/bin/mongod --quiet --config /etc/mongod.conf

หากว่า MongoDB รันแล้ว ก็ลองทดสอบด้วยการ shell เข้าไปดูด้วยคำสั่ง

$ mongo

MongoDB shell version v3.4.1
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.1

>

2. Create Admin user

ต่อมา เมื่อ shell เข้าด้วย Mongo Shell แล้ว ก็มาทำการสร้าง User ที่เป็น Administrator กันดีกว่า

$ mongo

> show dbs

admin        0.000GB
local        0.000GB

> use admin
switched to db admin

จากนั้นทำการสร้าง User Administrator ขึ้นมา

> db.createUser(
... {
...   user: 'superAdmin',
...   pwd: 'superPassword',
...   roles: [ { role: 'root', db: 'admin' } ]
... }
... )

ใน mongo shell เราสามารถพิมพ์คำสั่งหรือ statement แล้วกด enter ได้เลย จะขึ้น ... ไม่ต้องตกใจ ตัว shell มันจะไม่ execute จนกว่าจะหมด statement นะครับ

หากว่าทำการ create user เสร็จ จะได้ผลลัพธ์แบบนี้

Successfully added user: {
  "user" : "superAdmin",
  "roles" : [
    {
      "role" : "root",
      "db" : "admin"
    }
  ]
}

อธิบายด้านบนคือ เราทำการสร้าง user ชื่อ "superAdmin" และมี role เป็น "userAdminAnyDatabase" และ grant db ไว้ที่ db ชื่อ "admin"

ซึ่ง user นี้เราจะเอาไว้สำหรับจัดการ User อื่นๆ (Add/Edit/Update)

สำหรับ Role ต่างๆ ของ MongoDB ก็อ่านเพิ่มเติมที่ Built-in Roles ส่วน "root" คร่าวๆ ก็รวมๆ roles ต่างๆ ทุกอย่างเลย เช่น readWriteAnyDatabase, dbAdminAnyDatabase, userAdminAnyDatabase, clusterAdmin roles, restore, และ backup

3. Enable Authorization

ต่อมา ตัว MongoDB นั้นเริ่มต้นมา จะไม่ได้ปิด Auth ทำให้ เราสามารถ access ได้โดยไม่ต้องใช้ user/pass ให้ทำการ enable Authorization ซะ โดย ทำการแก้ไขไฟล์ /etc/mongod.conf

sudo vi /etc/mongod.conf

จากนั้นเพิ่มส่วน security ดังนี้

security
  authorization: enabled

เซฟ แล้วลอง restart mongo ใหม่

sudo service mongod restart

Note: นอกจาก authorization: enabled แล้ว เรายังสามารถเปลียน default port นอกจาก 27017 ก็ได้ รวมถึงดูว่า เราได้ปรับ bindIp ไว้หรือเปล่า bindIp: 127.0.0.1 เพื่อให้สามารถ connect เฉพาะ localhost ได้อย่างเดียว ที่อื่นจะ remote access เข้ามาไม่ได้

ทีนี้ลองเข้า Mongo ใหม่ดู

$ mongo

> show dbs

2017-01-08T10:48:51.823+0000 E QUERY    [main] Error: listDatabases failed:{
  "ok" : 0,
  "errmsg" : "not authorized on admin to execute command { listDatabases: 1.0 }",
  "code" : 13,
  "codeName" : "Unauthorized"
}

> use anotherDb
switched to db anotherDb

จะเห็นว่าเราไม่สามารถจะใช้คำสั่งได list database ได้แล้ว เนื่องจากไม่มี permission นั่นเอง แต่ยังสามารถ switch db ได้ แต่ก็ไม่มีสิทธิ์ทำอะไรอีกนั่นแหละ หากเราตั้งค่าไว้

ต้องทำการเข้าด้วย user pass ของ admin ดังนี้

$ mongo -u 'superAdmin' -p 'superPassword' --authenticationDatabase 'admin'

>
  • -u 'username' : สำหรับใส่ username
  • -p 'password' : สำหรับใส่ password
  • --authenticationDatabase 'db' : สำหรับ db ที่เราต้องการ access

Note: เราสามารถใส่ -p แล้วเว้น password ไว้ได้เพื่อใส่ Password ทีหลัง จะได้ไม่เซฟ password ของเราไว้กับ bash/zsh history นั่นเอง

4. Create New User

ต่อมา เรามาสร้าง User เพื่อเอาไว้สำหรับ connect MongoDB แต่จะกำหนดแค่ว่า user คนนี้ สามารถเข้าได้แค่ database นั้นๆ เท่านั้น โดยให้สิทธิ์เป็น "readWrite" คือสามารถ อ่านและเขียน (Read/Insert db) ได้

$ mongo -u 'superAdmin' -p 'superPassword' --authenticationDatabase 'admin'

# access เข้า myDatabase
> use myDatabase;

db.createUser({
  user: 'chai',
  pwd: 'superPassword',
  roles: ['readWrite']
})

Successfully added user: { "user" : "chai", "roles" : [ "readWrite" ] }

หรือว่าเราจะใช้คำสั่ง ด้านล่างนี้ เพื่อกำหนดให้ user มีสิทธิ์ใน database อื่นก็ได้

db.createUser({
  user: 'chai',
  pwd: 'superPassword',
  roles: [{ role: 'readWrite', db: 'anotherDatabase' }]
})

ลองเช็คดูว่ามี user จริงๆไหม

> show dbs

admin        0.000GB
local        0.000GB

> use admin
switched to db admin

> show collections
system.users
system.version

> db.system.users.find().pretty()

{
  "_id" : "myDatabase.chai",
  "user" : "chai",
  "db" : "myDatabase",
  "credentials" : {
    "SCRAM-SHA-1" : {
      "iterationCount" : 10000,
      "salt" : "8QS....",
      "storedKey" : "Scn...",
      "serverKey" : "w2j..."
    }
  },
  "roles" : [
    {
      "role" : "readWrite",
      "db" : "myDatabase"
    }
  ]
}

5. Connect with mongoose

ทีนี้ลองมา Connect MongoDB ด้วยโปรแกรมดู ด้านล่างนี้ใช้ Mongoose.js สร้างไฟล์ชื่อ app.js

โดยที่ไม่ใส่ User Password เวลา connect

const mongoose = require('mongoose')

const URL = 'mongodb://localhost/myDatabase'

function callback(err, result) {
  if (err) {
    throw err
  }
}

mongoose.connect(URL, callback)

let Cat = mongoose.model('Cat', { name: String })

let kitty = new Cat({ name: 'Jane' })
kitty.save(function (err) {
  if (err) {
    console.log(err)
  } else {
    console.log('meow')
  }
})

ลองสั่งรันดู

$ node app.js


{ MongoError: not authorized on simple_auth to execute command { insert: "cats", documents: [ { name: "Jane",
...
...
  name: 'MongoError',
  message: 'not authorized on simple_auth to execute command { insert: "cats", documents: [ { name: "Jane", _id: ObjectId(\'5872208dff5e220f6d37e217\'), __v: 0 } ], ordered: false, writeConcern: { w: 1 } }',
  ok: 0,
  errmsg: 'not authorized on simple_auth to execute command { insert: "cats", documents: [ { name: "Jane", _id: ObjectId(\'5872208dff5e220f6d37e217\'), __v: 0 } ], ordered: false, writeConcern: { w: 1 } }',
  code: 13,
  codeName: 'Unauthorized' }

ต่อมาเปลี่ยนใหม่ ตอน connect ให้ใส่ options สำหรับ user/pass ไปด้วย เพื่อจะได้ Auth ผ่าน

const options = {
  user: 'chai',
  pass: 'superPassword'
}

mongoose.connect(URL, options, callback)

ลองรันใหม่

$ node app.js

meow

เป็นอันเรียบร้อย ตอนนี้เราก็ Enable Auth แล้ว หวังว่า Database เราจะ Security ขึ้นมาอีกระดับนะครับ :)

โค๊ดทั้งหมด app.js

const mongoose = require('mongoose')

const URL = 'mongodb://localhost/myDatabase'
const options = {
  user: 'chai',
  pass: 'superPassword'
}

function callback(err, result) {
  if (err) {
    throw err
  }
}

mongoose.connect(URL, options, callback)

let Cat = mongoose.model('Cat', { name: String })

let kitty = new Cat({ name: 'Jane' })
kitty.save(function (err) {
  if (err) {
    console.log(err)
  } else {
    console.log('meow')
  }
})

References

Buy Me A Coffee
Authors
Discord