
如何在 Python 和 Flask 中使用 IP API 查找地理位置?
您是否想过,您的服务器发送的前端未使用的额外数据会发生什么情况?或者,如何处理从后端 API 获得的 JSON 对象中的所有额外信息?
这种情况通常会导致一种称为过度数据暴露的潜在漏洞。最终结果是攻击者可以利用暴露的额外敏感数据对您的用户造成伤害。但过度数据暴露到底是什么?它有什么危害?您如何在前端 React 应用程序中防止它?
在这篇文章中,我将为您解答这些问题。我将通过示例帮助您了解过度数据暴露以及如何在 React 应用程序中防止这种情况发生。
过度数据暴露是一种安全漏洞,当您的前端请求的数据超过预期时就会发生这种情况。在 Web 应用程序中,任何人都可以打开浏览器开发人员工具并检查传入和传出的 HTTP 请求。他们可以看到从这些请求中收到的数据负载。
因此,即使前端不一定使用或消费这些数据,攻击者仍然可以通过 API 调用查看这些数据。如果 API 发送的数据比前端需要的敏感数据多,攻击者就可以利用这些数据对用户造成损害。
这被称为过度数据暴露。
在过度数据暴露中,暴露的敏感数据实际上比任何人可以访问的数据都要多。
现在让我们借助一个例子来进一步理解这一点。
假设您有一家电子商务商店。您还拥有大量定期从您的商店购买商品的用户和潜在购物者。假设为了完成购买,您的用户必须检查他们的信用卡详细信息以及订单详细信息。在“确认订单”页面上,您允许用户查看他们想要购买的商品。
然后,在“结帐”页面上,您会提示他们检查信用卡详细信息。但是,此页面只有一个 REST API 端点。此端点会提供购物车中的商品以及信用卡详细信息。用户流程可能如下所示:
现在想象一下,如果您作为用户可以与朋友分享购物车中的商品。这将为您的“确认订单”页面生成一个可共享的 URL,其中经过身份验证的用户只能查看数据而不能修改数据。听起来很酷。但是,一旦加载此页面,您就会访问 API 端点,该端点还会检索该用户的信用卡详细信息。攻击者可能会窃取此信息,从而对该用户造成潜在损害。
防止过度数据泄露的最直接方法是重构 REST API 端点。如果您知道您的 Web 服务正在发送前端不需要的敏感数据,则需要在后端查询中过滤这些数据。这实际上有两个目的:防止 API 泄露不需要的敏感数据并优化数据库查询。
但是,第一种解决方案纯粹是服务器端的。如果您使用的是第三方 Web 服务,而您无法对其进行太多控制,该怎么办?如何防止基于前端的方法导致过度数据泄露?
您可以使用基于 GraphQL 的 API,而不是使用 REST API。GraphQL 是一种 API 架构,它使前端可以完全控制所需的数据。因此,现在您可以使用这种方法在前端仅请求非敏感数据。让我们看看如何使用 GraphQL 来防止 React 应用程序中的过度获取。
第一步,我们继续通过运行以下命令创建一个新的 React 应用程序:
npx create-react-app react-excessive-data-exposure-app
这应该为我们创建一个全新的 React 项目。接下来,继续编辑App.js文件的内容,如下所示:
import './App.css';
function App() {
return (
<divclassName="App">
<headerclassName="App-header">
<h1>React Excessive Data Exposure with GraphQL</h1>
</header>
</div>
);
}
export default App;
现在让我们在本地运行 React 应用程序。为此,您需要在根目录中运行以下命令:
npm start
然后如果你访问“http://localhost:3000”,你应该会看到以下页面:
为了演示如何使用 GraphQL 克服过度数据暴露问题,我们首先需要一个 GraphQL 服务器或 API。为了简单起见,我们不会从头开始创建自己的 GraphQL API 或服务器。相反,我们将在线使用虚拟模拟 GraphQL API 来快速启动。然后,我们可以从上一节中创建的 React 应用与此 GraphQL API 进行通信,以了解如何防止在前端过度获取数据。
为此,我们将使用GraphQLZero,这是一个免费的虚拟在线 GraphQL API。让我们了解一下此 API 的用例和示例。我们可以使用此工具通过 GraphQL 游乐场直观地测试 API、请求和响应结构。在左侧,您将看到一个可编辑部分。我们可以在这里编写 GraphQL 查询。
将以下查询添加到此部分:
query {
user(id: 1) {
id
username
email
address {
geo {
lat
lng
}
}
}
}
然后点击中间的 大播放按钮。这将在右侧部分显示 JSON 响应:
太棒了!现在我们将通过 React 应用与此 GraphQL API 进行通信。那就开始吧。
在App.js中,我们首先从 React 导入useState钩子,以便存储从 API 调用获得的数据。我们还将导入useEffect钩子,以便我们可以使用钩子中的componentDidMount生命周期等效项在页面加载后立即进行 API 调用。
import {useEffect, useState} from 'react';
接下来,我们将利用useState钩子创建一个状态变量来存储 API 的数据:
const [userData,setUserData]=useState();
之后,我们将在App组件内创建一个局部变量来保存 GraphQL API 的查询。它是一个简单的字符串,包含我们之前测试的精确查询,如下所示:
const QUERY=`{
user(id: 1) {
id
username
email
address {
geo {
lat
lng
}
}
}
}`
之后,我们将创建一个useEffect,并在回调函数内部编写代码,使用 JavaScript 的 Fetch API 向我们的模拟服务器创建 GraphQL API 请求。如下所示:
useEffect(()=>{
fetch("https://graphqlzero.almansi.me/api", {
"method": "POST",
"headers": { "content-type": "application/json" },
"body": JSON.stringify({
query: QUERY
})
}).then(res => res.json())
.then(data=>setUserData(data.data.user))
},[])
由于我们必须传递 QUERY 对象,因此每个 GraphQL API 都是一个 POST 请求,这就是为什么上述代码中的方法为 POST。然后,在请求的正文部分,我们以 JSON 字符串的形式发送 API 调用的查询。最后,我们处理两个.then回调以将响应转换为 JSON,然后将其存储在我们的状态变量中。
接下来,我们将在App.js文件中显示这些数据:
<div className="App">
<header className="App-header">
<h1>React Excessive Data Exposure with GraphQL</h1>
<div className='data'>
<div>Username: <span>{userData?.username}</span> </div>
<br/>
<div>User ID: <span>{userData?.id}</span> </div>
<br/>
<div>Email: <span>{userData?.email}</span> </div>
</div>
</header>
</div>
请注意,我们只对用户名、ID 和电子邮件感兴趣。因此,我们从 API 中提取这些数据,然后将它们呈现在 DOM 上。完成此操作后,您的 React 应用应如下所示:
这很酷。但是让我们检查一下浏览器网络选项卡中的 API 调用。
如果您注意到,我们仅使用电子邮件、用户名和 ID 等一些信息,但我们还会在响应中获取用户的地址。显然,我们在前端的数据使用范围过广。
如果这些数据被攻击者泄露,可能会导致过度的数据泄露。那么我们该如何预防呢?
如果我们使用的是 REST API,那么通过纯前端解决方案来解决这个问题是不可能的。尤其是在这种情况下,由于我们使用的是第三方 API,我们无法控制 API 的结构、它发送的数据等。幸运的是,我们正在使用 GraphQL API,其中前端可以控制它想要从请求中获取的数据。
因此,我们需要做的就是更新查询。更新后的查询如下所示:
const QUERY=`{
user(id: 1) {
id
username
email
}
}`
现在让我们看看从 API 返回了什么数据:
现在我们只会得到用户 ID、用户名和电子邮件。太棒了!我们已防止不必要的和潜在的敏感信息在前端暴露。
我希望我能够帮助您了解过度数据暴露是什么以及如何在 React 应用程序中防止这种情况。如果您使用的是基于 REST 的后端架构,那么解决这个问题的正确方法是重构后端 API,使其仅返回所需的数据。下次见!