firebase 在NextJS中向浏览器公开环境变量是否是不安全和不好的做法?

tjvv9vkg  于 2023-01-31  发布在  其他
关注(0)|答案(1)|浏览(101)

我目前正在一个NextJS应用程序中尝试设置Firebase v9认证。我最初尝试使用Next的服务器端环境变量作为Firebase配置,但注意到我的所有环境变量都未定义。但后来我更新了我的Firebase配置,使用NEXT_PUBLIC_,然后它就工作正常了。所以我想我的问题是:
1.向浏览器公开Firebase Config变量是否安全?
1.如果它不安全,你如何确保应用程序在服务器端初始化,然后在客户端使用?(链接任何特定的指南或文章将不胜感激)
下面我按顺序提供了我的firebase配置文件、AuthContext提供程序和登录页面。

import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET_URL,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
  measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};

const app = initializeApp(firebaseConfig);
export const auth = getAuth();
import { createContext, useContext, useEffect, useState } from 'react';
import {
  onAuthStateChanged,
  signInWithEmailAndPassword,
  signOut,
} from 'firebase/auth';
import { auth } from '@/lib/firebaseApp';

const AuthContext = createContext({});

export const useAuth = () => useContext(AuthContext);

export function AuthContextProvider({ children }) {
  const [user, setUser] = useState({ email: null, uid: null });
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      if (user) {
        setUser({
          email: user.email,
          uid: user.uid,
        });
      } else {
        setUser({ email: null, uid: null });
      }
    });
    setLoading(false);
    return () => unsubscribe();
  }, []);

  function login(email, password) {
    return signInWithEmailAndPassword(auth, email, password);
  }

  async function logout() {
    setUser({ email: null, uid: null });
    await signOut(auth);
  }

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {loading ? null : children}
    </AuthContext.Provider>
  );
}
import { useState } from 'react';
import { useRouter } from 'next/router';

import { Button } from '@/components';
import { useAuth } from '@/context/AuthContext';

export default function Login() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const { login } = useAuth();
  const router = useRouter();

  async function handleSubmit(event) {
    event.preventDefault();
    try {
      await login(email, password);
      router.push('/dashboard');
    } catch (error) {
      console.log(error.message);
    }
  }

  return (
    <div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
      <div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
        <div className="mx-auto w-11/12 rounded-2xl border border-zinc-100 p-6 py-8 px-4 dark:border-zinc-700/40 sm:px-10 md:w-full">
          <form className="space-y-6" onSubmit={(event) => handleSubmit(event)}>
            <div>
              <label
                htmlFor="email"
                className="block text-sm font-semibold text-zinc-900 dark:text-zinc-100"
              >
                Email address
              </label>
              <div className="mt-1">
                <input
                  id="email"
                  name="email"
                  type="email"
                  autoComplete="email"
                  required
                  className="block w-full appearance-none rounded-md border border-zinc-900/10 bg-white px-3 py-2 shadow-sm shadow-zinc-800/5 placeholder:text-zinc-400 focus:border-teal-500 focus:outline-none focus:ring-4 focus:ring-teal-500/10 dark:border-zinc-700 dark:bg-zinc-700/[0.15] dark:text-zinc-200 dark:placeholder:text-zinc-500 dark:focus:border-teal-400 dark:focus:ring-teal-400/10 sm:text-sm"
                  onChange={(e) => setEmail(e.target.value)}
                />
              </div>
            </div>
            <div>
              <label
                htmlFor="password"
                className="block text-sm font-semibold text-zinc-900 dark:text-zinc-100"
              >
                Password
              </label>
              <div className="mt-1">
                <input
                  id="password"
                  name="password"
                  type="password"
                  autoComplete="current-password"
                  required
                  className="block w-full appearance-none rounded-md border border-zinc-900/10 bg-white px-3 py-2 shadow-sm shadow-zinc-800/5 placeholder:text-zinc-400 focus:border-teal-500 focus:outline-none focus:ring-4 focus:ring-teal-500/10 dark:border-zinc-700 dark:bg-zinc-700/[0.15] dark:text-zinc-200 dark:placeholder:text-zinc-500 dark:focus:border-teal-400 dark:focus:ring-teal-400/10 sm:text-sm"
                  onChange={(e) => setPassword(e.target.value)}
                />
              </div>
            </div>
            <div className="flex w-full justify-center">
              <Button type="submit" className="w-10/12 px-6 py-4 md:w-5/6">
                Sign In
              </Button>
            </div>
          </form>
        </div>
      </div>
    </div>
  );
}
14ifxucb

14ifxucb1#

为什么无法访问. env变量

您当前正在访问客户端的环境变量。此功能不需要,因此默认情况下不存在,因为客户端上的所有内容对每个人都可见。您可以直接以纯文本形式粘贴API密钥。这是一回事。

Firebase管理员与客户端

firebase有两种选择。要么你使用客户端SDK,那里的API密钥是公开的,并且可以公开显示,没有任何问题。你在firestore控制台中编写自己的规则。(这似乎是你正在使用的)

    • 重要提示**如果您发布应用,则必须将Firestore规则设置为锁定模式,并且只有在您专门为其编写了规则的情况下才允许读/写。

否则你可以使用firebase管理SDK,它应该只在服务器端运行。然后你通过Nextjs API路由与firebase通信。初始化这个应用的API密钥是绝对私有的,并且显示为不公开。如果公开,任何人都可以获得你项目的完全控制权
您可以在这里阅读更多关于firebase admin SDK的信息:https://firebase.google.com/docs/admin/setup

结论

您在客户端使用的是firebase sdk,因此您的API密钥可以安全地暴露。但是不要忘记设置谁可以读/写的规则。如果您的项目在发布时处于"测试模式",任何人都可以获得对您的应用的控制权

相关问题