Firebase
๐ค Firebase API Key๋ฅผ ๊ณต๊ฐํ๋ ๊ฒ์ด ์์ ํฉ๋๊น?
seungmin2
2021. 1. 8. 15:33
๐ ๊ณ ๋ฏผํ๊ฒ ๋ ๊ณ๊ธฐ
- ์คํฐ๋ ๋ชจ์ง ๊ฐ์ธ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ๋ฐฑ์ค๋๋ก
firebase
๋ฅผ ์ฌ์ฉํ๊ฒ ๋์์ต๋๋ค. - ์ฌ์ฉํ๋ฉด์ API Key์ด๋๊น ๋น์ฐํ
.env
ํ์ผ๋ก ๊ด๋ฆฌํด์ผ์ง๋ผ๊ณ ์๊ฐํ ๋ค ๊ด๋ฆฌ๋ฅผ ํ๊ฒ ๋์๊ณ , ๊ทธ ํ Ci/Cd๋ฅผ ์ํด Github-Action ์ฌ์ฉํ๊ณ ์์๊ณ CI๋ฅผ ์งํํ๋ฉด์.env
ํ์ผ๋ก ์ธํด ํ ์คํธ๊ฐ ๊นจ์ง๋ ํ์์ด ๋ฐ์ํ๊ฒ ๋์์ต๋๋ค.
ํ ์คํธ๋ฅผ ํ ๋ apikey๋ฅผ ์ฌ์ฉํ๋ ๋ถ๋ถ์ mockingํด์ ํ ๋ ค๊ณ ํ์๊ณ ๊ณ ๋ฏผ์ ํด๋ณด์์ง๋ง ์ฝ๊ฒ ์งํ๋์ง ์์์ต๋๋ค.
FIREBASE_API_KEY= "your api-key"
FIREBASE_AUTH_DOMAIN = "your auth-domain"
FIREBASE_DATA_BASEURL = "your data-baseurl"
FIREBASE_PROJECT_ID = "your project-id"
FIREBASE_STORAGE_BUCKET = "your storage-bucket"
FIREBASE_MESSAGING_SENDER_ID = "your messaging-sender-id"
FIREBASE_APP_ID = "your app-id"
FIREBASE_MEASUREMENT_ID = "your measurement-id"
- ๊ทธ๋ ๋ค๊ฐ ์ฐ์ฐ์น์๊ฒ Is it safe to expose Firebase apiKey to the public?๋ผ๋ ์ ๋ชฉ์ ์ฃผ์ ๋ก api ํค๋ฅผ ๊ณต๊ฐํ๋ ๊ฒ์ด ์์ ํ์ง์ ๋ํ ์คํ ์ค๋ฒํ๋ก์ฐ์ ์ง๋ฌธ์ด ๋์ ๋ค์ด์ค๊ฒ ๋์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด ๋ถ๋ถ์ ๋ํด์ ๊ณ์ ๊ณ ๋ฏผํ๊ฒ ๋์๊ณ , ๋ต์ ์ ๊ธ์ ๋ช ํํ ๋์์์ง๋ง API Key๋ผ๋ ์ ์ด ๊ฑธ๋ ค ์ ๊ฐ ์๊ฒ๋ ์ฌ์ค์ ์ ์ด ๊ณต์ ํด๋ณผ๊น ํฉ๋๋ค.
๐จ Is it safe to expose Firebase apiKey to the public?
- ๊ณ ๋ฏผ์ ๋ฐ๋จ์ด ๋ ์คํ์ค๋ฒํ๋ก์ฐ์ ์ง๋ฌธ๋ ์ ๋ชฉ ๊ทธ๋๋ก firebase apikey๋ฅผ ๊ณต๊ฐ๋ฅผ ํ๋ ๊ฒ์ด ์์ ํ์ง์ ๋ํด์ ๋ฌผ์ด๋ณธ ๊ฒ์ด์์ต๋๋ค.
๊ทธ ์ง๋ฌธ์ ๋ต๋ณ์ Google์ Firebase ์์ง๋์ด๋ถ์ด ๋ฌ์์ฃผ์ จ๋๋ฐ ๊ทธ ๋ต์ ๋ค์๊ณผ ๊ฐ์์ต๋๋ค.
- apiKey๋ Google ์๋ฒ์์ Firebase ํ๋ก์ ํธ๋ง ์๋ณํฉ๋๋ค. ๋๊ตฐ๊ฐ๊ฐ ๊ทธ๊ฒ์ ์๋ ๊ฒ์ ๋ณด์ ์ํ์ด ์๋๋๋ค. ์ฌ์ค, ๊ทธ๋ค์ด ๋น์ ์ Firebase ํ๋ก์ ํธ์ ์ํธ ์์ฉํ๊ธฐ ์ํด์๋ ๊ทธ๋ค์ด ๊ทธ๊ฒ์ ์์์ผํฉ๋๋ค. ์ด ๋์ผํ ๊ตฌ์ฑ ๋ฐ์ดํฐ๋ Firebase๋ฅผ ๋ฐฑ์๋๋ก ์ฌ์ฉํ๋ ๋ชจ๋ iOS ๋ฐ Android ์ฑ์๋ ํฌํจ๋ฉ๋๋ค.
- Firebase ๋ฐฑ์๋ ์๋น์ค์ ๋ํ ๋ชจ๋ ๋ฐ์ดํฐ ์ก์ธ์ค๋ฅผ ์น์ธํ๋ ๋ฐฉ๋ฒ์ ์์ ๋ณด๋ ค๋ฉด Firebase ๋ณด์ ๊ท์น์ ๋ํ ๋ฌธ์๋ฅผ ์ฝ์ด ๋ณด์ธ์. ์ด๋ฌํ ๊ท์น์ ํ์ผ ์ ์ฅ์ ๋ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ก์ธ์ค์ ๋ํ ์ก์ธ์ค๋ฅผ ์ ์ดํ๋ฉฐ Firebase ์๋ฒ์ ์ ์ฉ๋ฉ๋๋ค.
- ์ด๋ฌํ ๋ต๋ณ์ผ๋ก ๊ฒฐ๋ก ์ Firebase์ API-Key๋ Google ์๋ฒ์์ firebase ํ๋ก์ ํธ๋ง ์๋ณํ๊ธฐ ๋๋ฌธ์ ๋ณด์์ ๋ฌธ์ ๊ฐ ๋์ง ์๋๋ค๋ ๋ช ํํ ๋ต๋ณ์ด์์ต๋๋ค. ํ์ง๋ง, ๊ทธ ์ง๋ฌธ์ ๊ธ์ ๋ฌด๋ ค 5๋ ์ ์ง๋ฌธ์ด์๊ณ API-key์ ๋ํ ์์ฌ์ ์์ด์ ๋ ์ฐพ์๋ณด๊ฒ ๋์์ต๋๋ค.
๐ฃ Firebase ๊ณต์ ๋ฌธ์
- Firebase์ ๊ณต์ ๋ฌธ์์์ Firebase ์ฉ API ํค ์ฌ์ฉ ๋ฐ ๊ด๋ฆฌ์ ๋ํด ์์๋ณด๊ธฐ๋ผ๋ ์ ๋ชฉ์ผ๋ก Api key์ ๊ด๋ฆฌ ๋ฐฉ๋ฒ์ด ์์ธํ ์ค๋ช ๋์ด์์ต๋๋ค.
- ์ฌ๊ธฐ์๋ ์๋์ ๊ฐ์ด Firebase์ฉ APIํค๋ ์ผ๋ฐ์ ์ธ APIํค์ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ APIํค๊ฐ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ ๋ฐฉ์๊ณผ ๋ฌ๋ฆฌ Firebase ์๋น์ค์ฉ APIํค๋ ๋ฐฑ์๋ ๋ฆฌ์์ค์ ๋ํ ์ก์ธ์ค๋ฅผ ์ ์ดํ๋ ๋ฐ ์ฌ์ฉ ๋์ง ์์ต๋๋ค. ๋ผ๋ ๋ด์ฉ์ด ์ ํ์์์ต๋๋ค.
- ํ์ง๋ง ์ ๊ฐ ์งํํ๊ณ ์๋ ๊ฐ์ธ ํ๋ก์ ํธ๋ ์ด๋ฉ์ผ/๋น๋ฐ๋ฒํธ ๋ก๊ทธ์ธ ๋ฐฉ๋ฒ์ผ๋ก Firebase ์ธ์ฆ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์ ํด๋นํ์๊ธฐ ๋๋ฌธ์ APIํค์ ์ ํ์ ์ ์ฉํด์ผํ๋ ๊ตฌ์ฒด์ ์ธ ๊ฒฝ์ฐ์ ํด๋นํ๊ฒ ๋์์ต๋๋ค.
- ๋ํ, ๋ค๋ฅธ ๊ตฌ์ฒด์ ์ธ ๊ฒฝ์ฐ๋ ๊ณต์ ๋ฌธ์ ์ฐธ๊ณ ํ์๊ธธ ๋ฐ๋๋๋ค.
๐ Firebase ๋ณด์ ๊ท์น
- ๊ณต์ ๋ฌธ์์ ๋ด์ฉ์ ๋ฐ๋ผ ์ํธ ๊ธฐ๋ฐ ์ธ์ฆ์ ์ฌ์ฉํ ๋ ๋ณดํธํ๋ ๋ฐฉ๋ฒ์ธ firebase ๋ณด์ ๊ท์น์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋ณด์ ๊ท์น์ ์ ์ ์ฉํ๋ฉด ๋ณด์ ๊ท์น์ ์ํด ๋ณดํธ๋๋ ํ ํ๋ก์ ํธ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋๋ ์ ์ฅ์ ๋ฐ์ดํฐ๊ฒ ์์ธ์ค ํ ์ ์๊ฒ ๋ฉ๋๋ค. - ๊ณต์ ๋ฌธ์์ ๋ณด์ ๊ท์น์ ๊ต์ฅํ ์์ธํ ์ค๋ช ์ด ๋์ด์์ผ๋ฏ๋ก ์ฐธ๊ณ ํ์๊ธฐ ๋ฐ๋๋๋ค. ํนํ ์์ ํ์ง ์์ ๊ท์น ์ฐจ๋จ์ ์ฐธ๊ณ ํ์๋ฉด ์ข์ ๊ฑฐ ๊ฐ์ต๋๋ค.
- ๋ง์ฝ
firestore
์ ๋ค์๊ณผ ๊ฐ์ ๊ท์น์ ์์ฑ ํ ์ ์ฉํฉ๋๋ค.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /groups/{group} {
allow read: if true // ์ฝ๊ธฐ ๋ชจ๋ ํ์ฉ
// ์ฐ๊ธฐ: ์ฝํ
์ธ ์์ ์ ์ ์ฉ
allow write: if request.auth != null && request.auth.uid == request.resource.data.author_uid
}
}
}
๋ณธ์ธ์ด ์์ฑํ ๊ฒ์ด ์๋ ๋ค๋ฅธ ์ฌ๋์ด ์์ฑํ ๊ฒ์ ์์ ํ ๋ ค๊ณ ํ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ด ๊ถํ์ด ์์ด ๊ฑฐ๋ถ๋๋ ์๋ฌ๋ฅผ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค.
- ๋ค์์ ์ธ์ฆ๋ ์ฌ์ฉ์๋ง ์ฐ๊ธฐ
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /groups/{group} {
allow read: if true; // ์ฝ๊ธฐ ๋ชจ๋ ๊ฐ๋ฅ
allow write: if request.auth.uid != null; // ์ฐ๊ธฐ ์ธ์ฆ๋ ์ฌ์ฉ์๋ง
}
}
}
- ์ถ๊ฐ์ ์ธ ๊ท์น์ ๊ณต์ ๋ฌธ์ ์ฐธ๊ณ ํ์๊ธธ ๋ฐ๋๋๋ค.
๐ฅ ๋ค๋ฅธ ์น ์ฌ์ดํธ์์ Firebase ํ๋ก์ ํธ์ ์์ฑ, ์์ ๋ฑ์ ๋ชปํ๊ฒ ํ๋ ๋ฐฉ๋ฒ
- firebase๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐฐํฌ๋ฅผ ํ๊ฒ๋๊ณ ๋ด ํ๋ก์ ํธ์ ๊ณต๊ฐ๋ API-key๋ฅผ ์ฌ์ฉํด์ ์์ ํ ์ ์๋ ์ฌ์ง๊ฐ ์๊ธฐ๊ฒ ๋ฉ๋๋ค.
- ์๋ฅผ ๋ค์ด์ Github์ ๊ณต๊ฐ๋ API-key๋ฅผ ์ฌ์ฉํด์ fork ํ ๋ก์ปฌ์์ ํ๋ก์ ํธ๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค. (localhost) ๊ทธ๋ ๊ฒ ๋๋ฉด ํด๋น ๊ตฌ์ฑ ํ์ผ์ ์ฌ์ฉํ์ฌ ์ ์ฌ์ฉ์๋ฅผ ๋ง๋ค ์ ์๊ฒ ๋๊ณ , ๋ก์ปฌ์์ ๊ธ์ ์์ฑ, ์์ ๋ฑ์ ํ ์๊ฒ ์๊ฒ ๋ฉ๋๋ค.
- ์ด์ ๋ํ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๋ค์ ๋ธ๋ก๊ทธ์ ์์ธํ ๋์์๊ณ , ์ด ๋ถ๋ถ์ firebase ๊ณต์ ๋ฌธ์์ ์ถ์ ์ฒดํฌ๋ฆฌ์คํธ ์ค ํ๋์ ๋๋ฉ์ธ ํ์ฉ ๋ชฉ๋ก์ ์ถ๊ฐํ์ฌ ๋ฌด๋จ ์ฌ์ฉ์ ๋ฐฉ์งํฉ๋๋ค. ๋ผ๊ณ ์๊ฐ๋์ด์์ต๋๋ค. ๋ํ, ๊ตฌ๊ธ์ firebase ์์ง๋์ด Frank van Puffelen ๊ป์ ํผ์ฑ ๊ณต๊ฒฉ์ ๋ํด ์ด ๋ฐฉ๋ฒ์ ์ธ๊ธํ์ จ์ต๋๋ค.
- Google Cloud Platform API ๋ฐ ์๋น์ค์ ์ ์ํ์ฌ ํด๋น ํ๋ก์ ํธ๋ฅผ ์ ํ ํ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ํญ์ผ๋ก ์ด๋ํฉ๋๋ค.
- ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ํญ์ API ํค ๋ถ๋ถ์ Browser Key (auto created by Firebase) ๋ฅผ ์ ํํฉ๋๋ค.
- ์ ํ ํ APIํค ์ ํ ๋ฐ ์ด๋ฆ ๋ณ๊ฒฝ ํ์ด์ง๋ก ์ด๋ํ๊ฒ ๋๋๋ฐ ๊ทธ ๋ถ๋ถ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ ์ฌํญ์ HTTP ๋ฆฌํผ๋ฌ(์น์ฌ์ดํธ) ๋ฅผ ์ ํํด์ค๋๋ค.
- ์ ํํ๋ฉด ์๋์ ์น์ฌ์ดํธ ์ ํ์ฌํญ ํญ๋ชฉ์ด ๋ํ๋๊ฒ ๋๋๋ฐ ๊ทธ ํญ๋ชฉ์ ๋ฐฐํฌ๋์ด ์๋ ํด๋น ํ๋ก์ ํธ URL๋ฅผ ์ ๋ ฅ ํ ์ ์ฅ์ ํฉ๋๋ค.
- ์ด๋ ๊ฒ ๋ชจ๋ ์์
์ ์๋ฃํ๋ฉด ๋ค๋ฅธ ์ฌ์ดํธ์์ API๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค. ํ์ง๋ง ์ด๋ ๊ฒ ํ๋ฉด ๋ก์ปฌ์์ ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค.
๋ก์ปฌ์์ ํด๋น API๋ฅผ ๊ฐ์ง๊ณ ๋ก๊ทธ์ธ์ ์๋ํด๋ณด์์ ๋ ๋ค์๊ณผ ๊ฐ์ด 403 ์๋ฌ๊ฐ ๋ํ๋๊ฒ ๋ฉ๋๋ค.
- ๋ก์ปฌ์์ ํ
์คํธ ๋ฐ ๊ฐ๋ฐ์ ๋ชปํ๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์๋ ์๋ก์ด firebase ํ๋ก์ ํธ๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. ๊ฐ๋ฐ ๋ฐ ํ
์คํธ๋ง์ ์ํ firebase ํ๋ก์ ํธ๋ฅผ ์์ฑํ๊ณ ์๋กญ๊ฒ ๋ง๋ firebase ํ๋ก์ ํธ์ ํ๊ฒฝ ๋ณ์๋ฅผ ์ฌ์ฉํด์ ํ
์คํธ๋ฅผ ์งํํ ์ ์์ต๋๋ค.
์๋์ ๊ฐ์ด ๊ฐ๋ฐ ํ๊ฒฝ์ ๋ฐ๋ผ ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํด์ค ์ ์์ต๋๋ค.
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
import devConfig from '../../config/dev'; // ๊ฐ๋ฐ ํ๊ฒฝ ์ค์
import prodConfig from '../../config/prod'; // ๋ฐฐํฌ ํ๊ฒฝ ์ค์
const config = process.env.NODE_ENV === 'production'
? prodConfig
: devConfig;
firebase.initializeApp(config);
const fireStore = firebase.firestore;
export default fireStore;
๐ ํ์ง๋ง, ์ด๋ ๊ฒ ์ค์ ํ๋๊ฑฐ์ ๋ํด์ ๋ฐ๋ฐ๋ ์กด์ฌํ์ต๋๋ค.
HTTP ๋ฆฌํผ๋ฌ๊ฐ ๋งค์ฐ ์ฝ๊ฒ ์คํธํ ๋ ์ ์๋ค๋ ์ ์ผ๋ก ๊ฒฐ๊ตญ ์ด๋ ๊ฒ ํ๋ ๊ฑด ์์ฉ์ด ์๋ค๋ ์ ์ด์๊ณ , api key๋ฅผ ๋ ธ์ถํ๊ฑฐ๋ ๋ ธ์ถํ์ง ์๋ ๊ฒ์ ์ค์ํ์ง ์๊ณ ์ด ๋ฐฉ๋ฒ์ ์ค์ ๋ก ์ด๋ฌํ ๊ตฌ์ฑ ์ ๋ณด๋ ๊ณต๊ฐ ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋๋ค๋ ๋ง์ด์์ต๋๋ค.
๐ ๊ฒฐ๋ก
- ์ด๋ฐ ์ ๋ฐ ๋ด์ฉ์ ์ฐพ์๋ณด์์ง๋ง ๊ฒฐ๊ตญ ๊ฒฐ๋ก ์ firebase apiํค๋ ๊ณต๊ฐ๊ฐ ๋๋ ์๊ด์ด ์๋ค๋ ์ ์ ๋๋ค. ํ์ง๋ง, ๊ถํ์ ์ฃผ์ด ๋ ์์ ํ๊ฒ ๊ด๋ฆฌํ ์ ์๋ค๋ ์ ์ ๋๋ค.
.env
๋ก api-key๋ฅผ ์จ๊ธฐ๋๋ผ๋ ์์ ๊ฐ์ด ๊ถํ์ด ๊ฑฐ๋ถ๋ ๋๋ ๊ฒฐ๊ตญ apiํค์ ์ ๋ณด๊ฐ ๋ ธ์ถ๋ฉ๋๋ค.
- API๋ Firebals/Google ์๋น์ค์ ์ํธ ์์ฉํ ๋ Firebase ํ๋ก์ ํธ๋ฅผ ์๋ณํ๋ ๋ฐ ์ฌ์ฉํฉ๋๋ค. API ํค ์ฌ์ฉ์ ๋ํ ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํด์ฃผ์๋ฉด ๋ฉ๋๋ค.
- Google์ Firebase JS ๋ชจ๋ฐ์ผ ๊ฐ๋ฐ์์ธ Jorge Vergara๋ถ๋ ๋ฉ์ผ๋ก FIrebase API ํค๋ฅผ ์จ๊ฒจ์ผํ๋๋์ ๋ํ ๋ฉ์ผ์ด ํ๋ฌ์ 3 ~ 4๋ฒ์ด ์จ๋ค๊ณ ํฉ๋๋ค. ์ด ๊ธ์์ ํต์ฌ์ ๊ฒฐ๊ตญ ๊ณต๊ฐ๋๋ ๋ฌธ์ ๊ฐ ๋์ง ์๋๋ค๋ ์ ์ด์๊ณ , ์๋์ ๊ฐ์ด ์ฐ์ ๊ธ์ ๋ณด๊ณ ํ์ ์ ๊ฐ์ง ์ ์๊ฒ๋์์ต๋๋ค.
apiKey๋ฅผ ์๋ค๋ ๊ฒ์ ์ฌ๋๋ค์ด ๋น์ ์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ์ฆ์ ํดํน ํ ์ ์๋ค๋ ๊ฒ์ ์๋ฏธํ๋ ๊ฒ์ด ์๋๋ผ ๊ทธ๋ค์ด ๋น์ ์ ์ฑ/ํ๋ก์ ํธ์ ์ฐ๊ฒฐํ ์ ์๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
- ์ด ๋ต๋ณ์ ๋ฃ๊ณ ๋ช ํํ๊ฒ ์ดํดํ ์ ์์์ต๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ฐ๊ฒฐ์ ๋ํ ๋ถ๋ถ์ ์์ ๊ฐ์ด ํด๊ฒฐํด์ฃผ๋ฉด ๋ฌธ์ ๋ ์๋ค๋ ๊ฒฐ๋ก ์ ๋ด๋ ธ์ต๋๋ค.
๐ ์ฐธ๊ณ ๋งํฌ
- ๊ฐ์ธ ํ๋ก์ ํธ์ Firebase์ ๋ํ ์ฐธ๊ณ ์ฌํญ ์ด์
- How to secure your Firebase project even when your API key is publicly available
- Do you need to hide your Firebase API keys for Ionic apps?
- Firebase Security & Rules
- Firebase ์ฉ API ํค ์ฌ์ฉ ๋ฐ ๊ด๋ฆฌ์ ๋ํด ์์๋ณด๊ธฐ
- How to restrict Firebase data modification?
- Is it safe to expose Firebase apiKey to the public?
- Firebase ๋ณด์ ๊ท์น
- You do realize that HTTP referrers can be spoofed very easily right?