Javascript 前端授权几乎总是不安全的。Don';你不同意吗?

Javascript 前端授权几乎总是不安全的。Don';你不同意吗?,javascript,google-chrome-extension,authorization,firefox-addon,single-page-application,Javascript,Google Chrome Extension,Authorization,Firefox Addon,Single Page Application,多年前我就开始从事web开发,但我仍然不相信您可以轻松地在前端应用程序或浏览器扩展中实现用户授权 虽然可以实现前端身份验证(cookies、jwt等),并且工作得相当好,但对于授权来说却不是这样。常见的例子是,您希望将某些内容和/或功能的访问限制为仅登录用户 我正在检查许多浏览器扩展和web应用程序,通常会发现类似以下伪代码: if (user.isLogged === true) { // code to show ui components and actions } else {

多年前我就开始从事web开发,但我仍然不相信您可以轻松地在前端应用程序或浏览器扩展中实现用户授权

虽然可以实现前端身份验证(cookies、jwt等),并且工作得相当好,但对于授权来说却不是这样。常见的例子是,您希望将某些内容和/或功能的访问限制为仅登录用户

我正在检查许多浏览器扩展和web应用程序,通常会发现类似以下伪代码:

if (user.isLogged === true) {
    // code to show ui components and actions
} else {
    // code to show ui components and actions
}
这是非常不安全的

例如,这来自chrome web应用商店提供的扩展:

function initApp() {
    firebase
        .auth()
        .onAuthStateChanged(function (user) {
            if (user) {
                // User is signed in. var uid = user.uid; window.location.href = "app.html";
                const uid = user.uid;
                const name = user.displayName;
                getUserData(uid);

                // Plus a lot of other code to show/hide ui components and actions

            } else { }
            document
                .getElementById('quickstart-button')
                .disabled = false;
        });

    document
        .getElementById('quickstart-button')
        .addEventListener('click', startSignIn, false);
}

唯一安全的方法是,只有当用户被授权访问该页面/功能时才动态加载HTML和JS块,服务器根据当前用户的角色决定哪些块服务于当前用户,然后在运行时注入这些块。但是,一方面,spa架构本身是失败的,因为它非常接近于服务于应用程序服务器端(客户端永远无法决定显示什么内容)。它还需要使用JS动态注入HTML标记,这对安全性来说并不理想。另外,这是一个非常重要的实现,因此我猜大部分js应用程序都使用上面所示的模式处理受限内容/区域,基于当前用户的当前内存/存储(或cookie/存储)状态,这是非常不安全的,可以很容易地由最终用户操纵。尤其是在浏览器扩展中,甚至不允许您混淆代码


我错过了什么吗;DR你说得对,不要依赖前端安全。没有。从后端获取机密内容并要求身份验证

你提到水疗,让我们分析一下那里的情况

假设您有一个React应用程序,其主要组件如下所示:

function App() {

  const { loggedIn, isSpecialUser } = useContext(authContext);

  if (loggedIn)
     return <MainPage />;

  if (isSpecialUser)
     return <SuperSecretPage />;

  return <LoginPage />;
}
即使超级机密内容对于所有用户来说都是静态的,也应该通过需要令牌的单独调用获取它。后端必须检查访问权限。如果你只是把你的内容写在那里,每个人都能看到


编辑对OP评论的回复。他写道

恶意用户至少有2个选项:1)更改主应用程序组件的js内容,比方说返回;当用户根本没有登录时;2) 手动更改ajax回调以将其状态设置为超级用户,而不考虑实际响应

对于这两种方法,用户只会破坏客户端渲染,而不会破坏安全性。在任何情况下,客户端都必须进行API调用以接收机密内容:

const { data } = useApi(getSuperSecretContent, token);
useApi
是一个抽象钩子,它使用某种API(例如REST-API)在运行时(ajax)获取内容。第二个参数是令牌。此令牌必须是服务器在登录后发送给用户的某种身份验证令牌。这里的标记是关键的部分(参见OAuth,JWT)。对于要获取超级机密内容的每个ajax调用,必须提供一个有效的令牌。后端必须检查令牌是否有效。当且仅当令牌有效时,后端才会返回超级机密内容

伪代码
useApi

function useApi(apiCall, token) {
  make ajax request {
    endpoint: apiCall,
    authorization header: token
  }
  if success:
     return data
  else if auth error:
     return error
}
伪代码后端:

get request to super secret content:
  if token is not valid:
     return auth error;
  else
     return super secret content
用户无法通过操纵前端状态欺骗后端向其发送超级机密内容


编辑2回应OP对身份验证和授权的困惑

您还必须将令牌用于授权。以JWT为例,令牌本身可以包含关于权限管理的信息,否则您可以从数据库中查询给定用户的访问权限

这必须在@backend-side请求处理期间发生

伪代码后端,例如:

get request to super secret content:
  get access rights for token

  if access rights include super secret resources:
     return super secret content;
  else
     return auth error;

谢谢你的意见。然而,恶意用户至少有两种选择:1)更改主应用程序组件的js内容,比如说
返回当用户根本没有登录时;2) 手动更改ajax回调以将其状态设置为超级用户,而不考虑实际响应。这当然不是针对基本用户的,但这仍然是完全可能的,因为我们正在决定在前面显示什么…@Mauro请查看我的编辑。我想你误解了一个关键部分。您必须动态获取机密内容,并为每个调用提供授权令牌。此令牌不能由用户手工制作(通过数字签名等),感谢您的澄清。我认为这里我们再次混合了身份验证和授权。我知道我们在每次调用时都会传递令牌,因为我们当然不会在服务器上维护任何会话。但是,在您的示例中,{data}包含自定义组件,因此我们需要两个调用,一个用于呈现正确的组件,另一个用于在组件内部获取内容数据。@Mauro我们在这里不混合身份验证和授权。您的后端负责这两个方面。首先,您的客户端进行身份验证并接收身份验证令牌。此令牌可以包含有关权限管理的信息,或者用于获取有关授权的信息。如果客户端随后发送资源请求,则后端必须使用令牌检查是否允许令牌访问请求的资源。对于JWT,有效负载可以包含关于可以访问哪些资源的信息。如果您使用SSR而不是SPA,那么您可以直接在后端处理所有事情。
get request to super secret content:
  get access rights for token

  if access rights include super secret resources:
     return super secret content;
  else
     return auth error;