发布于 2026-01-06 0 阅读
0

我在实现 React Hook 时犯了个错误,导致后端拒绝服务攻击。

我在实现 React Hook 时犯了个错误,导致后端拒绝服务攻击。

在 Firebase 端点上使用 React 的 useEffect hook 看似简单,却在几分钟内意外地用完了我每天 5 万次的读取配额。

function Page() {
  const [meetings, setMeetings] = useState([]);
  useEffect(() => {
    return firebase.firestore().collection('/meetings').onSnapshot(query => {
      setMeeting( query.docs.map(m => m.data()) );
    });
  });

  return (
    <ul>
      {meetings.map(m => <li>{m.title}</li>}
    </ul>
  )
}

由于该效果不依赖于任何状态改变,所以我省略了第二个参数。但我没有意识到的是,每次setMeeting调用该函数时,函数体都会再次执行,导致数据获取陷入循环。

是的。Firebase 的免费套餐每天提供 5 万次读取的配额,而我在几分钟的开发工作中就超过了这个限额。追踪问题来源也相当麻烦。当我意识到 Chrome 开发者工具中的“网络”选项卡正在向 Firebase 发送大量请求时,我不得不赶紧切换到性能分析选项卡并切换到“离线模式”。然后我才有时间查看其中一个请求的有效负载,并弄清楚它请求的是什么数据。

解决方法很简单,只需添加一个空方括号,表示这应该只运行一次,就像 componentDidMount 和 componentWillUnmount 一样(firebase 调用返回的是我们想要卸载的监听器)。

useEffect(() => {
  // return firebase...
}, []) // this guy

不过,在修复问题之后,我停下来思考了一下其根本原因。

  1. 在效果钩子中,第二个参数很容易被忽略。

  2. Firebase/Firestore 没有服务器端限速机制。因此,任何恶意用户或代码漏洞都可能导致免费用户服务中断,或者向付费用户收取高额费用。甚至连 Google 控制台中的配额管理工具都无法直观地显示哪个端点正在遭受攻击,以及攻击的具体时间。

  3. 当您在 Firebase 中的使用配额超出限制时,您甚至无法访问管理面板。

  4. 错误处理机制无法捕获这类问题。

祝您编程愉快!


参考

  • 照片由 Andrew Gaines 拍摄,来自 Unsplash
  • Firebase 是一种后端即服务 (BaaS),为实时数据存储解决方案提供慷慨的免费套餐。
文章来源:https://dev.to/adamwknox/i-made-a-mistake-implementing-a-react-hook-and-got-a-denial-of-service-from-my-backend-5cji