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

เนื่องจากว่าวันนี้ได้อ่านเจอบทความเกี่ยวกับ 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.1connecting to: mongodb://127.0.0.1:27017MongoDB server version: 3.4.1
>
2. Create Admin user
ต่อมา เมื่อ shell เข้าด้วย Mongo Shell แล้ว ก็มาทำการสร้าง User ที่เป็น Administrator กันดีกว่า
$ mongo
> show dbs
admin 0.000GBlocal 0.000GB
> use adminswitched 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 anotherDbswitched 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.000GBlocal 0.000GB
> use adminswitched to db admin
> show collectionssystem.userssystem.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
- Authors
-
Chai Phonbopit
เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust