Github Pages + Secrets + Vanilla JS 배포

NCOOKIE·2025년 2월 21일
0

TIL

목록 보기
4/20

간단하게 만든 웹페이지를 Github pages를 통해 배포하려고 한다. 그러나 firebase 관련 설정 데이터는 로컬에서 .gitignore로 관리하고 있기 때문에 배포 시 문제가 발생한다.

이를 해결하기 위해 Github actions를 사용해봤다.

현재 로컬 개발 환경에서는 외부에 firebase API key를 노출시키지 않기 위해 관련 config 파일을 .gitignore에 등록해서 사용하고 있다.

로컬

config/firebaseConfig.js

// Firebase 구성 정보 설정
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
    apiKey: "~~~",
    authDomain: "~~~",
    projectId: "~~~",
    storageBucket: "~~~",
    messagingSenderId: "~~~",
    appId: "~~~",
    measurementId: "~~~"
};

index.html

...
<head>
    <script src="../config/firebaseConfig.js"></script>

    <script>
        // Firebase SDK 라이브러리 가져오기
        import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js";
        import { getFirestore } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
        import { collection, addDoc } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
        import { getDocs } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";

        // Firebase 인스턴스 초기화
        const app = initializeApp(firebaseConfig);
        const db = getFirestore(app);
    </script>
...

Github

이러한 config를 배포 환경에서도 동일하게 사용하기 위해 github workflow에서 secrets을 사용했다.

deploy.yml

# Sample workflow for building and deploying a Jekyll site to GitHub Pages
name: Deploy Jekyll with GitHub Pages dependencies preinstalled

on:
  # Runs on pushes targeting the default branch
  push:
    branches: ["master"]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  # Build job
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Setup Pages
        uses: actions/configure-pages@v4
      - name: Create Firebase Config
        run: |
          mkdir -p config  # 디렉터리 없으면 생성
          
          echo "const firebaseConfig = {
                apiKey: '${{ secrets.FIREBASE_API_KEY }}',
                authDomain: '${{ secrets.FIREBASE_AUTH_DOMAIN }}',
                projectId: '${{ secrets.FIREBASE_PROJECT_ID }}',
                storageBucket: '${{ secrets.FIREBASE_STORAGE_BUCKET }}',
                messagingSenderId: '${{ secrets.FIREBASE_MESSAGING_SENDER_ID }}',
                appId: '${{ secrets.FIREBASE_APP_ID }}',
                measurementId: '${{ secrets.FIREBASE_MEASUREMENT_ID }}'
            };" > config/firebaseConfig.js
      - name: Build with Jekyll
        uses: actions/jekyll-build-pages@v1
        with:
          source: ./
          destination: ./_site
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3

  # Deployment job
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

시행착오

처음에 Create Firebase Config 부분을 build가 아니라 deploy에 넣었을 때는 firebaseConfig.js 파일이 정상적으로 생성되었음에도 index.html에서 제대로 load하지 못하고 있었다.

추측컨데 build 섹션에서 코드를 빌드하고 해당 파일을 업로드하기 전에 설정 파일을 생성해야 하는데, 이를 배포(deploy) 과정에서 하려니 config 파일을 불러들이지 못한 것 같다.

웃긴건 이 기능을 적용하기 위해 참고했던 블로그에서 이미 비슷한 시행착오를 겪었는데 나는 그걸 대충 보고 삽질하고 있었던 것이다. 굉장히 비효율적인 일이 아닐 수 없다. 스프링 배포 설정할 때 이미 해봤던 작업이라 잘 알고 있다고 생각해서 대충 넘겼던 것 같은데... 결국에는 적지 않은 시간을 날리고서야 해결했다. 앞으로는 유의하자.

API 키 노출됨

그렇게 열심히 배포 설정을 했건만, 개발자 도구에서 확인하니 API 키가 그대로 노출된 것을 볼 수 있었다. 그래서 방법을 이것저것 알아봤으나... Github Pages + Vanilla JS + Firebase 조합으로 API 키가 노출되지 않게 하는 방법은 찾지 못했다.

Vault 같이 외부에서 secrets을 주입해주거나, 아예 클라이언트가 아니라 서버에서 키를 가지고 있는 방법이 일반적인 것 같다. 다만 Firebase 한정해서 사용할 수 있는 방법이 있다.

Firebase 콘솔에서 "앱 제한" 설정

  1. Google Cloud Console에 로그인 및 프로젝트 선택

  1. API 및 서비스 > 사용자 인증 정보로 이동

  1. API 키 선택 (Firebase는 보통 Browser key로 등록되어 있다.)

  1. 애플리케이션 제한 설정. Firebase를 사용할 도메인 주소를 입력한다. 나는 Github Pages의 주소를 입력했다.

  1. 결과 확인. 지정한 도메인 주소가 아닌 다른 곳에서 Firebase를 사용하려고 하면 이런 화면을 보게 된다. 이를 대비해 로컬 개발 환경에서는 개발용 Firebase 프로젝트를 만드는 것을 고려해보자.

결국 차선책 아닌가?

그렇다. 이렇게 해도 API 키가 노출된 이상 공격할 수 있는 방법은 많을 것이다. 다른 방법은 없을까? 프론트엔드 개발자들이 서버 대신 많이 사용하기 때문에 관련된 기능을 제공하지 않을까 생각했는데 말이다. 그러나 firebase의 API key를 외부에서 안전히 보호하는 방법은 없었다.

왜 이런지 이유를 찾아보니... firebase에서는 해당 값들을 API 키가 아니라 "프로젝트 식별자"로 보고 있기 때문인 것 같다. 여타 서비스들과 다르게 API 키가 외부에 노출된다고 해도 Firebase 프로젝트 권한을 획득할 수는 없다. 애초에 Google은 기본적으로 API 키가 노출될 것이라는 전제하에 보호 기능을 제공하고 있다고 한다.

또한 Firestore 보안 규칙을 통해 아무나 데이터를 수정할 수 없도록 설정할 수도 있다. 위에서 언급한 앱 제한을 설정할 수도 있고...

결론은 Firebase API의 키는 노출되더라도 크게 문제는 없지만, 안전하게 사용하려면 여러 보안 규칙을 설정해야 한다. 만약 보안적으로 중요한 프로젝트라면 별도의 서버를 두고 Firebase는 서브로 두는 것이 좋을 것 같다.

참고

profile
일단 해보자

0개의 댓글