Laravel 5.6 - Passport JWT httponly cookie SPA 身份验证用于自用 API?

Posted

技术标签:

【中文标题】Laravel 5.6 - Passport JWT httponly cookie SPA 身份验证用于自用 API?【英文标题】:Laravel 5.6 - Passport JWT httponly cookie SPA authentication for self consuming API? 【发布时间】:2019-05-09 17:18:25 【问题描述】:

注意:我在这个问题上获得了 4 个赏金,但以下投票支持的答案都不是这个问题所需的答案。所需的一切都在下面的 Update 3 中,只是寻找 Laravel 代码来实现。


更新 3:此流程图正是我试图完成的流程,以下所有内容都是原始问题,其中包含一些较旧的更新。此流程图总结了所需的一切。

下面流程图中的绿色部分是我知道该怎么做的部分。红色部分及其旁注是我正在寻找使用 Laravel 代码完成的帮助。


我做了很多研究,但在将 Laravel 与 JWT httponly cookie 一起用于自消费 API 时,信息总是很短且不完整(大多数在线教程仅显示 JWT 存储在本地存储中,即不是很安全)。看起来应该使用包含 Passport 的 JWT 的 httponly cookie 来识别 javascript 端的用户,当每个请求发送到服务器以验证用户是他们所说的人时。

还需要一些额外的东西来全面了解如何使这个设置工作,我没有在一个单独的教程中遇到过这些内容:

    Laravel Passport(不是 tymon auth)生成加密的 JWT,并在从 JS 端登录后将其作为 httponly cookie 作为响应发送。使用什么中间件?如果刷新令牌增加了安全性,如何实现? 调用 auth 端点的 JavaScript(例如 axios)api 伪代码,httponly cookie 如何传递给后端,后端如何验证令牌是否有效。 如果单个帐户从多个设备登录,则设备被盗,如何撤消所有经过身份验证的用户设备的访问权限(假设用户从他们控制的已登录设备上更改密码)? Lo​​gin/Register、Logout、Change Password、Forgot Password 控制器方法通常会如何处理令牌的创建/验证/撤销? CSRF 令牌集成。

我希望这个问题的答案可以作为一个易于理解的指南,供未来的读者和那些目前正在努力寻找涵盖上述自用 API 的答案的人使用。

更新 1:

    请注意,我之前尝试过CreateFreshApiToken,但在撤销用户令牌时不起作用(对于上面的第 3 点和第 4 点)。这是基于 laravel 核心开发人员的 this comment,在谈到 CreateFreshApiToken 中间件时:

此中间件创建的 JWT 令牌不会存储在任何地方。他们 不能撤销或“不存在”。他们只是为您提供了一种方式 api 调用通过 laravel_token cookie 进行身份验证。它不是 与访问令牌有关。 另外:您通常不会在发布它们的同一应用程序上使用客户端发布的令牌。您会在第一方或第三方中使用它们 应用程序。要么使用中间件,要么使用客户端颁发的令牌,但不使用 两者同时进行。

所以它似乎能够满足第 3 点和第 4 点来撤销令牌,如果使用 CreateFreshApiToken 中间件是不可能的。

    在客户端,Authorization: Bearer <token> 似乎不是处理安全 httpOnly cookie 的方法。我认为请求/响应应该包含安全的 httpOnly cookie 作为请求/响应标头,就像这样基于 laravel 文档:

当使用这种认证方式时,默认 Laravel JavaScript 脚手架指示 Axios 始终发送 X-CSRF-TOKEN 和 X-Requested-With 标头。

headerswindow.axios.defaults.headers.common = 
    'X-Requested-With': 'XMLHttpRequest',
    'X-CSRF-TOKEN': (csrf_token goes here)
;

这也是我寻找涵盖上述所有要点的解决方案的原因。抱歉,我使用的是 Laravel 5.6 而不是 5.5。

更新 2:

似乎密码授予/刷新令牌授予组合是要走的路。使用 Password Grant/Refresh Token Grant 组合寻找易于遵循的实施指南。

密码授予: 这笔赠款适用于与我们信任的客户打交道时, 就像我们自己网站的移动应用程序一样。在这种情况下,客户端发送 用户到授权服务器的登录凭据和 服务器直接发出访问令牌。

刷新令牌授予: 当服务器发出访问令牌时,它还会为 访问令牌。当我们想要刷新 访问令牌一旦过期。在这种情况下,授权服务器 在发出访问令牌时会发送一个刷新令牌,可以是 用于请求新的访问令牌。

我正在使用 Password Grant/Refresh Token Grant 组合寻找一个易于实施、直接、全面的答案,该组合使用 httpOnly 安全 cookie 涵盖上述原始 5 点的所有部分,创建/revoking/refreshing 令牌、登录 cookie 创建、注销 cookie 撤销、控制器方法、CSRF 等。

【问题讨论】:

我在 Laravel 中使用了 Luca Desgasperi 的 Passport 和 Oauth2。当涉及到移动应用程序和 Javascript 时,密码授予类型并不安全。只有授权码流程是可以的。但根据数据的敏感性,令牌的持续时间不得超过 2 分钟至 60 分钟或更长时间。 Facebook 令牌现在是 2 分钟 + 1 次使用。使用授权码授权类型,浏览器会询问用户名和密码并请求访问您的应用程序或您自己的网站(默认情况下允许)的权限,然后只发送一个带有过期时间的令牌,该令牌会刷新每个时间 @KeitelDOG 谢谢你的意见,但这个包只升级到 Laravel 5.3,他们也弃用了它,转而支持 Passport。如前所述,我正在寻找涵盖上述所有要点的整体方法/解决方案/实施指南。 我知道,这就是我切换到 Passport 的原因。 Passport 更好,最后一个版本甚至可以根据您的需要控制在特定客户端中撤销用户令牌 3. 您好,在我们的 SPA Laravel 应用程序中,我们使用默认的 Web 身份验证(会话保护)登录用户。在加载 SPA 应用程序之前,我们还生成一个 csrf_token 并将其放置在元数据中。完成这两个操作后,我们使用 CreateFreshApiToken 中间件来确定 SPA 是否需要 now 令牌。最后使用令牌从 api 进行身份验证/授权。这是你想要的吗? 对我来说,这种论点应该在官方文档中处理......如果护照旨在成为处理身份验证的安全方式,则应清楚地解释这种情况。经过几天的研究,我发现几乎没有人以正确的方式处理这个问题。 【参考方案1】: Laravel Passport 是 php League 的 OAuth 服务器的一个实现 密码授权类型可用于用户名+密码认证 请记住通过在代理中发出身份验证请求来隐藏您的客户端凭据 将刷新令牌保存在 HttpOnly cookie 中以最大程度地降低 XSS 攻击的风险

您可以在此处查看更多信息

http://esbenp.github.io/2017/03/19/modern-rest-api-laravel-part-4/

【讨论】:

谢谢你,Alex,但我已经看到了这个教程。我遇到的问题是它说“6。客户端将访问令牌保存在存储中,例如浏览器应用程序将其保存在 localStorage 中”,我只是想让 cookie 自动发回,有点像 laravel_token可以,但是 laravel_token 不能让我们销毁其中的 jwt。因此,只需尝试使其仅与 http cookie 一起使用,否则如果有人可以访问您的本地存储,他们可以获取您的访问令牌,从而使 cookie 无用。此外,本文也没有涉及 CSRF 集成。 是的,我知道你需要这样的东西,没有 Laravel Passport,但你可以看到如何防止网络攻击的例子 我希望有一种更简单的方法来使用 Laravel 5.5 和 Passport 来完成我原始问题中的 5 点,无需本地存储用于 api 调用,只需 cookie + CSRF 集成。我认为应该有更好/更简单的方法来使用 Passport 和 Laravel 5.5 实现这 5 点。我将保持问题的开放性,希望我们能得到解决上述所有问题的答案。我知道很多人都在寻找同样的东西,但一切似乎都过时或没有 100% 完成,并且没有一个来源/指南/laracast/SO 问题显示如何使用 L5.5/Passport 正确/完全 100% 执行此操作. 我认为这是一个好主意,如果您愿意并且其他获得所需的完整信息,那么您是对的。我很乐意为您提供帮助。谢谢【参考方案2】:

我还在我的项目中实施了 Laravel 护照,我想我已经涵盖了你在问题中提到的大部分内容。

    我已使用密码授权来生成访问令牌和刷新令牌。您可以按照these 步骤设置护照并实施护照授予。 在您的登录方法中,您必须验证用户凭据并生成令牌并将 cookie(Attaching cookie to the response) 附加到响应中。如果您需要,我可以为您提供一些示例。 我为 CORS(处理传入的请求标头)添加了两个中间件,并检查传入的访问令牌是否有效,如果无效,则从存储的刷新令牌 (Refreshing token) 生成访问令牌。我可以给你看例子。 登录后,来自客户端的所有请求都应包含授权标头(Authorization: Bearer <token>)。

如果您清楚以上几点,请告诉我。

【讨论】:

我添加了一个更新,我认为更新中的第二点应该发送/接收带有请求/响应标头的安全 httpOnly cookie,而不是 Authorization: Bearer <token>。我也在寻找一种解决方案,它涵盖了上述所有要点,并且可以很好地协同工作。 在您的情况下,您添加了与个人访问令牌一起使用的 CreateFreshApiToken。您无法刷新或撤销个人访问令牌,它们是长期令牌,但您可以调整其到期时间。 在我的第 3 点用例(被盗设备)中,从其他已登录的设备注销用户帐户,这些设备是/曾经活动的,更改密码等......这些都是应用程序服务器的实例应该能够立即撤销对当前登录用户的访问权限,并使他们的令牌无用。这个问题和用这种情况制定解决方案让我很困惑。 如果用户退出或更改了密码,那么您可以选择删除存储的令牌或将撤销标志设置为真。如果您想完全控制您的令牌,我建议您使用密码授予方法。 是的,我同意密码授予方法应该是可行的方法,但我正在寻找一个在解决原始问题的 5 点使用/实现以绘制完整可用指南的同时实现它的答案整个系统如何从服务器到客户端再到服务器。【参考方案3】:

Laravel Passport JWT

    要使用此功能,您需要禁用 cookie 序列化。 Laravel 5.5 存在 cookie 值的序列化/反序列化问题。 您可以在此处阅读更多相关信息 (https://laravel.com/docs/5.5/upgrade)

    确保

    您的刀片模板头中有<meta name="csrf-token" content=" csrf_token() ">

    axios 设置为在每个请求上使用 csrf_token。

resources/assets/js/bootstrap.js 中应该有类似的内容

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
let token = document.head.querySelector('meta[name="csrf-token"]');

if (token) 
  window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
 else 
  console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');

    此处解释了设置身份验证路由 (https://laravel.com/docs/5.5/authentication) 此处解释了设置护照 (https://laravel.com/docs/5.5/passport)。

重要的部分是:

Laravel\Passport\HasApiTokens 特征添加到您的 User 模型中 在config/auth.php 中将api 身份验证保护的driver 选项设置为passport\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, 中间件添加到web 中间件组app/Http/Kernel.php

请注意,您可能可以跳过迁移和创建客户端。

    /login 发出 POST 请求,传递您的凭据。您可以发出 AJAX 请求或普通表单提交。

如果登录请求是 AJAX(使用 axios),则响应数据将是 html,但您感兴趣的是状态码。

axios.get(
  '/login, 
  
    email: 'user@email.com',
    password: 'secret',
  ,
  
    headers: 
      'Accept': 'application/json', // set this header to get json validation errors.
    ,
  ,
).then(response => 
  if (response.status === 200) 
      // the cookie was set in browser
      // the response.data will be HTML string but I don't think you are interested in that
    
    // do something in this case
).catch(error => 
  if (error.response.status === 422) 
    // error.response.data is an object containing validation errors
  
  // do something in this case
);

登录时,服务器通过提供的凭据找到用户,根据用户信息(id,电子邮件...)生成令牌(此令牌不保存在任何地方) 然后服务器返回一个带有加密 cookie 的响应,该 cookie 包含生成的令牌。

    对受保护的路由进行 API 调用。

假设你有一条受保护的路线

Route::get('protected', 'SomeController@protected')->middleware('auth:api');

您可以像往常一样使用 axios 进行 ajax 调用。 cookie 是自动设置的。

axios.get('/api/protected')
  .then(response => 
    // do something with the response
  ).catch(error => 
    // do something with this case of error
  );

当服务器收到调用时解密请求laravel_cookie并获取用户信息(例如:id、email ...) 然后使用该用户信息进行数据库查找以检查用户是否存在。 如果找到该用户,则该用户被授权访问所请求的资源。 否则返回 401。

使 JWT 令牌无效。正如您提到的评论,无需担心这一点,因为此令牌未保存在服务器上的任何位置。

更新

关于第 3 点 Laravel 5.6 Auth 有一个新方法 logoutOtherDevices。您可以从这里了解更多信息 (https://laracasts.com/series/whats-new-in-laravel-5-6/episodes/7) 因为文档很简单。

如果您无法更新您的 Laravel 版本,您可以查看 5.6 中是如何完成的,并为 5.5 构建您自己的实现

您的问题的第 4 点。看看app/Http/Controllers/Auth 中的控制器。

关于 access_tokens 和 refresh_tokens 这是一种完全不同且更复杂的方法。 你可以在网上找到很多教程来解释如何做到这一点。

希望对你有帮助。

附言。祝新年快乐!! :)

【讨论】:

感谢您的回复;在我的问题中解决第 3 点和第 4 点非常重要。如果设备丢失或被盗,我们需要能够撤消访问权限,例如,通过更改密码,这将撤消与用户设备相关联的所有令牌。 CreateFreshApiToken 的问题是我们无法撤销该用户的令牌,因此丢失/被盗的设备将始终处于活动状态,无法撤销。如果您还考虑“从所有设备注销”,则需要相同类型的功能,其中将为给定用户撤销活动令牌。我们该怎么做? 感谢您的更新,我的目标是通过 Passport 身份验证实现最佳安全性。似乎建议使用带有access_tokenrefresh_token 组合的密码授予。这样,即使应用程序管理员也可以撤销用户令牌,立即使他们的下一个请求令牌无用并提示从应用程序注销。您的方法担心的是,如果有人抓住了另一个用户的 CreateFreshApiToken 令牌,现在他们可以永远是他们,对吧?管理员无法撤销该令牌,因此我认为他们可以无限访问。有没有办法处理这种情况? 新年快乐 :) 知道我最后的评论吗? 嘿@Wonka!抱歉我回复慢。我不确定我是否理解您的要求。您想要一个使用一些 oauth 授权(如密码授权)而不是 JWT 的流程?在这种情况下,如果您有更多客户端,您将如何在浏览器上存储客户端 ID 和客户端密码? @Wonka 我不知道如何帮助你,因为我对你的应用了解不多,但也许这篇文章medium.com/lightrail/… 会给你一些提示。【参考方案4】:

我将尝试以通用的方式回答这个问题,以便答案适用于框架、实现和语言,因为所有问题的答案都可以从通用协议或算法规范中得出。

我应该使用哪种 OAuth 2.0 授权类型?

这是要决定的第一件事。对于 SPA,有两种可能的选择:

    授权码授予(推荐,前提是客户端密码存储在服务器端) 资源所有者密码凭据授予

我没有提到隐式授权类型作为选项的原因是:

    缺少通过提供客户端密码和授权码的客户端身份验证步骤。所以安全性较低 访问令牌作为 URL 片段发回(因此令牌不会发送到服务器),该片段将继续保留在浏览器历史记录中 如果发生 XSS 攻击,恶意脚本可以很好地将令牌发送到攻击者控制的远程服务器

(客户端凭据授权类型不在此讨论范围内,因为它在客户端不代表用户执行操作时使用。例如批处理作业)

在授权码授权类型的情况下,授权服务器通常是与资源服务器不同的服务器。最好将授权服务器分开,并将其用作组织内所有 SPA 的通用授权服务器。这始终是推荐的解决方案。

这里(在授权代码授予类型中)流程如下所示:

    用户点击 SPA 登陆页面上的登录按钮 用户被重定向到授权服务器登录页面。客户端 ID 在 URL 查询参数中提供 用户输入他/她的凭据并单击登录按钮。用户名和密码将使用 HTTP POST 发送到授权服务器。凭据应在请求正文或标头中发送,而不是在 URL 中(因为 URL 记录在浏览器历史记录和应用程序服务器中)。此外,应设置正确的缓存 HTTP 标头,以便不缓存凭据:Cache-Control: no-cache, no-storePragma: no-cacheExpires: 0 授权服务器根据用户数据库(例如 LDAP 服务器)对用户进行身份验证,其中用户名和用户密码的散列(Argon2、PBKDF2、Bcrypt 或 Scrypt 等散列算法)使用随机盐存储 认证成功后,授权服务器将根据 URL 查询参数中提供的客户端 ID 从其数据库中检索重定向 URL。重定向 URL 是资源服务器 URL 然后用户将被重定向到资源服务器端点,并在 URL 查询参数中使用授权代码 然后资源服务器将向授权服务器发出 HTTP POST 请求以获取访问令牌。授权码、客户端 ID、客户端密码应该放在请求正文中。 (应使用上述适当的缓存标头) 授权服务器将在响应正文或标头中返回访问令牌和刷新令牌(使用上述适当的缓存标头) 资源服务器现在将通过设置适当的 cookie 将用户(HTTP 响应代码 302)重定向到 SPA URL(将在下面详细说明)

另一方面,对于资源所有者密码凭证授予类型,授权服务器和资源服务器相同。它更易于实施,如果符合要求和实施时间表,也可以使用。

有关资源所有者授权类型的更多详细信息,请参阅我在此 here 上的回答。

在此可能需要注意的是,在 SPA 中,所有受保护的路由只有在调用适当的服务以确保请求中存在有效令牌后才应启用。同样,受保护的 API 也应该有适当的过滤器来验证访问令牌。

为什么不应该将令牌存储在浏览器本地存储或会话存储中?

许多 SPA 会在浏览器本地存储或会话存储中存储访问和/或刷新令牌。我认为我们不应该将令牌存储在这些浏览器存储中的原因是:

    如果发生 XSS,恶意脚本可以轻松地从那里读取令牌并将其发送到远程服务器。从那里开始,远程服务器或攻击者在冒充受害者用户时不会有任何问题。

    localstorage 和 sessionstorage 不跨子域共享。因此,如果我们在不同的子域上运行两个 SPA,我们将无法获得 SSO 功能,因为一个应用程序存储的令牌将无法用于组织内的另一个应用程序

但是,如果令牌仍存储在任何这些浏览器存储中,则必须包含正确的指纹。指纹是加密的强随机字节串。然后,原始字符串的 Base64 字符串将存储在名称前缀为 __Secure-HttpOnlySecureSameSite cookie 中。 DomainPath 属性的正确值。字符串的 SHA256 哈希也将在 JWT 的声明中传递。因此,即使 XSS 攻击将 JWT 访问令牌发送到攻击者控制的远程服务器,它也无法在 cookie 中发送原始字符串,因此服务器可以根据缺少 cookie 拒绝请求。此外,可以通过使用适当的content-security-policy 响应标头进一步缓解 XSS 和脚本注入。

注意:

    SameSite=strict 确保给定的 cookie 不会伴随来自不同站点(AJAX 或通过以下超链接)的请求。简而言之 - 来自与目标站点具有相同“可注册域”的站点的任何请求都将被允许。例如。如果“http://www.example.com”是站点名称,则可注册域是“example.com”。有关详细信息,请参阅参考编号。 3 在下面的最后一节中。因此,它提供了一些针对 CSRF 的保护。但是,这也意味着如果给出的 URL 是论坛,则经过身份验证的用户无法访问该链接。如果这是对应用程序的严重限制,则可以使用SameSite=lax,只要 HTTP 方法是安全的,即允许跨站点请求。 GET、HEAD、OPTIONS 和 TRACE。由于 CSRF 基于 POST、PUT、DELETE 等不安全的方法,lax 仍然提供针对 CSRF 的保护

    要允许cookie在所有请求中传递到“example.com”的任何子域,cookie的域属性应设置为“example.com”

为什么要在 cookie 中存储访问令牌和/或刷新令牌?

    在cookie中存储token时,我们可以设置cookie为securehttpOnly。因此,如果发生 XSS,恶意脚本将无法读取并将其发送到远程服务器。 XSS 仍然可以从用户的浏览器中冒充用户,但是如果浏览器关闭,脚本就不会造成进一步的破坏。 secure 标志确保无法通过不安全的连接发送令牌 - SSL/TLS 是强制性的 例如,将 cookie 中的根域设置为 domain=example.com,可确保 cookie 可跨所有子域访问。因此,组织内的不同应用程序和服务器可以使用相同的令牌。只需登录一次

如何验证令牌?

令牌通常是 JWT 令牌。通常令牌的内容不是秘密的。因此,它们通常不加密。如果需要加密(可能是因为某些敏感信息也在令牌中传递),则有一个单独的规范 JWE。即使不需要加密,我们也需要确保令牌的完整性。没有人(用户或攻击者)应该能够修改令牌。如果他们这样做了,服务器应该能够检测到并拒绝所有使用伪造令牌的请求。为了确保这种完整性,JWT 令牌使用 HmacSHA256 等算法进行数字签名。为了生成此签名,需要一个密钥。授权服务器将拥有并保护该秘密。每当调用授权服务器 api 来验证令牌时,授权服务器都会重新计算传递的令牌上的 HMAC。如果它与输入 HMAC 不匹配,则返回否定响应。 JWT 令牌以 Base64 编码格式返回或存储。

但是,对于资源服务器上的每个 API 调用,授权服务器都不会参与验证令牌。资源服务器可以缓存授权服务器颁发的令牌。资源服务器可以使用内存中的数据网格(即 Redis),或者,如果所有内容都无法存储在 RAM 中,则可以使用基于 LSM 的 DB(即带有 Level DB 的 Riak)来存储令牌。

对于每个 API 调用,资源服务器都会检查其缓存。

    如果缓存中不存在访问令牌,API 应返回适当的响应消息和 401 响应代码,以便 SPA 可以将用户重定向到将请求用户重新登录的适当页面

    如果访问令牌有效但已过期(注意,JWT 令牌通常包含用户名和到期日期等),API 应返回适当的响应消息和 401 响应代码,以便 SPA 可以调用适当的资源服务器 API 使用刷新令牌(带有适当的缓存标头)更新访问令牌。然后,服务器将使用访问令牌、刷新令牌和客户端密码调用授权服务器,并且授权服务器可以返回新的访问和刷新令牌,这些令牌最终会流向 SPA(带有适当的缓存标头)。然后客户端需要重试原始请求。所有这些都将由系统处理,无需用户干预。可以创建一个单独的 cookie 来存储类似于访问令牌的刷新令牌,但具有适当的 Path 属性值,以便刷新令牌不会伴随每个请求,而是仅在更新请求中可用

    如果刷新令牌无效或过期,API 应返回适当的响应消息和 401 响应代码,以便 SPA 可以将用户重定向到要求用户重新登录的适当页面

为什么我们需要两个令牌——访问令牌和刷新令牌?

    访问令牌的有效期通常很短,比如 30 分钟。刷新令牌通常有更长的有效期,比如 6 个月。如果访问令牌以某种方式被泄露,攻击者只有在访问令牌有效的情况下才能冒充受害者用户。由于攻击者没有客户端密码,因此它无法向授权服务器请求新的访问令牌。然而,攻击者可以请求资源服务器进行令牌更新(如上述设置中,更新请求通过资源服务器以避免将客户端密码存储在浏览器中),但考虑到采取的其他步骤,它不太可能,而且服务器可以根据IP地址采取额外的保护措施。

    如果需要,访问令牌的这个短有效期有助于授权服务器撤销客户端颁发的令牌。授权服务器还可以维护已发布令牌的缓存。然后,如果需要,系统管理员可以将某些用户的令牌标记为已撤销。访问令牌过期后,当资源服务器去授权服务器时,用户将被强制重新登录。

CSRF 呢?

    为了保护用户免受 CSRF 的影响,我们可以遵循 Angular 等框架中遵循的方法(如 Angular HttpClient documentation 中所述,服务器必须发送非 HttpOnly cookie(换句话说,一个可读的 cookie ) 包含该特定会话的唯一不可预测值。它应该是加密强的随机值。然后客户端将始终读取 cookie 并在自定义 HTTP 标头中发送该值(GET 和 HEAD 请求除外,它们不应该有任何状态改变逻辑。注意CSRF由于同源策略无法从目标Web应用程序中读取任何内容),以便服务器可以从标头和cookie中验证值。由于跨域表单无法读取cookie或设置自定义标头,在 CSRF 请求的情况下,自定义标头值将丢失,服务器将能够检测到攻击

    为了防止应用程序登录 CSRF,请始终检查 referer 标头并仅在 referer 是受信任域时接受请求。如果referer 标头不存在或未列入白名单的域,则只需拒绝该请求。使用 SSL/TLS 时,通常会出现 referrer。登陆页面(主要是信息性的,不包含登录表单或任何安全内容可能有点放松,并允许缺少referer 标头的请求

    TRACE HTTP 方法应该在服务器中被阻止,因为它可以用来读取httpOnly cookie

    另外,设置标题 Strict-Transport-Security: max-age=<expire-time>; includeSubDomains​只允许安全连接以防止任何中间人覆盖子域中的 CSRF cookie

    另外,应该使用上面提到的SameSite设置

    状态变量(Auth0 使用它)- 客户端将生成并随每个请求传递一个加密的强随机随机数,服务器将连同其响应一起回显该随机数,从而允许客户端验证随机数。在Auth0 doc中有解释

最后,所有通信都必须使用 SSL/TLS - 就目前而言,低于 1.1 的 TLS 版本不符合 PCI/DSS 合规性。应使用适当的密码套件来确保前向保密和经过身份验证的加密。此外,一旦用户明确点击“注销”,访问和刷新令牌就应该被列入黑名单,以防止任何令牌滥用的可能性。

参考文献

    RFC 6749 - OAuth2.0 OWASP JWT Cheat Sheet SameSite Cookie IETF Draft Cookie Prefixes RFC 6265 - Cookie

【讨论】:

感谢您提供的一般概述,因为它是很好的信息而被赞成。不过,我仍在寻找 Laravel 5.6 特定的密码授予实施指南。由于到目前为止没有一个答案涵盖所有内容,只是这里和那里的部分,但不是可实施的密码授权 httpOnly 安全 cookie 实施流程:/ @Saptarshi Basu “由于攻击者没有客户端密码,它无法向授权服务器请求新的访问令牌。”如果可能的话,请您为我详细说明一下。如果攻击者获得刷新令牌,他们不能轻松创建访问令牌吗? @UjjwalJungThapa 通常,在所有实现中,您需要提供客户端密码以及刷新令牌来获取新的访问令牌。请注意,您总是将刷新令牌提供给连接到您的服务的浏览器或设备。任何有权访问该设备的人都可以获得刷新令牌。除非有秘密,否则任何人都可以生成新令牌。 @Saptarshi Basu 你能告诉我你所说的客户端密码是什么意思吗,因为对于 jwt,密钥只存储在服务器上,对吗?我以为客户端只发送刷新令牌。 @UjjwalJungThapa 请参阅 RFC 6749 的第 2.3.1 节(参考部分中给出的链接)。所有资源服务器都需要使用客户端 id 和客户端密码向授权服务器进行身份验证(您可以将其视为资源服务器拥有的用户名和密码)

以上是关于Laravel 5.6 - Passport JWT httponly cookie SPA 身份验证用于自用 API?的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5.6 - 用于自助API的Passport JWT httponly cookie SPA身份验证?

laravel + passport 搭建 api 认证系统 基础应用

每天一点点之laravel框架 - Laravel5.6 + Passport实现Api接口认证

Laravel 5.6 护照未经身份验证

laravel Passport - 创建 REST API 用户认证以及Dingo/Api v2.0+Passport实现api认证

Laravel 5.6 护照 API 身份验证在获取请求中不起作用