当前位置: 首页 > 知识库问答 >
问题:

laravel 5.6-Passport JWT http only cookie SPA认证自消费API?

邵博艺
2023-03-14

注意:我在这个问题上得到了4个赏金,但下面的投票结果都不是这个问题所需要的答案。所需的一切都在下面的更新3中,只是在寻找要实现的Laravel代码。

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

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

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

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

    < li>Laravel Passport(不是tymon auth)生成加密的JWT,并在从JS端登录后作为响应作为httponly cookie发送。用什么中间件?如果刷新令牌增加了更多的安全性,如何实现? < li >调用authendpoint的JavaScript(例如axios)API伪代码,如何将httponly cookie传递到后端,以及后端如何验证令牌是否有效。 < li >如果单个帐户从多台设备登录,那么一台设备被盗,如何撤销所有授权用户设备的访问权限(假设用户确实从他们控制的登录设备上更改了密码)? < li >处理令牌的创建/验证/撤销的登录/注册、注销、更改密码、忘记密码控制器方法通常是什么样的? < li>CSRF令牌集成。

我希望这个问题的答案能为未来的读者和那些目前正在努力寻找自我消费API上涵盖以上几点的答案的读者提供一个易于理解的指南。

更新 1:

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

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

所以它似乎能够迎合第3点和第4点来撤销令牌,如果使用CreateFREhApiToken中间件,则无法做到这一点。

使用这种身份验证方法时,默认的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:

看来密码授予/刷新令牌授予组合是要走的路。使用密码授予/刷新令牌授予组合寻找易于遵循的实施指南。

密码授权:这种授权适用于处理我们信任的客户端,如我们自己网站的移动应用程序。在这种情况下,客户端将用户的登录凭证发送到授权服务器,服务器直接颁发访问令牌。

刷新令牌授权:当服务器发出访问令牌时,它也会为访问令牌设置一个过期。刷新令牌授权用于我们想要在访问令牌过期后刷新访问令牌。在这种情况下,授权服务器将在发出访问令牌的同时发送一个刷新令牌,该令牌可用于请求新的访问令牌。

我正在寻找一个易于实现、直接、全面的答案,使用密码授予/刷新令牌授予组合,该组合涵盖了上述原始 5 点的所有部分,包括 httpOnly 安全 cookie、创建/撤销/刷新令牌、登录 cookie 创建、注销 cookie 撤销、控制器方法、CSRF 等。

共有3个答案

徐俊楚
2023-03-14
    < li>Laravel Passport是PHP联盟OAuth服务器的一个实现 < li >密码授权类型可用于用户名密码验证 < li >记住通过在代理中发出身份验证请求来隐藏您的客户端凭据 < li >将刷新令牌保存在HttpOnly cookie中,以最小化XSS攻击的风险

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

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

鲍钊
2023-03-14

Laravel护照JWT

>

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

    查明

    >

  • 您有

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

    资源/资产/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');
    }
    

    重要部件包括:

      < li >将< code > Laravel \ Passport \ HasApiTokens 特征添加到您的< code >用户模型中 < li >在< code > config/auth . PHP 中将< code>api身份验证防护的< code >驱动程序选项设置为< code>passport < li >将< code > \ Laravel \ Passport \ Http \ Middleware \ CreateFreshApiToken::class,中间件添加到< code>app/Http/Kernel.php中的< code>web中间件组

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

    如果登录请求是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。

    假设您有一条受保护的路由

    路由::get('protected', 'SomeController@protected')-

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

    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有一个新的方法<code>logout OtherDevices</code>。你可以从这里了解更多(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,这是一种完全不同且更复杂的方法。你可以在网上找到很多解释如何做的教程。

    希望有帮助。

    祝您新年快乐!!:)

  • 易刚捷
    2023-03-14

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

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

    1. 授权代码授予(建议,前提是客户端机密存储在服务器端)
    2. 资源所有者密码凭据授予

    我没有将隐式授予类型作为选项的原因是:

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

    (客户端凭证授权类型不在此讨论范围内,因为它是在客户端不代表用户时使用的。例如对于批处理作业)

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

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

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

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

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

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

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

    > < li>

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

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

    然而,如果令牌仍然存储在这些浏览器存储器中的任何一个中,则必须包括正确的指纹。指纹是加密的强随机字节串。然后,原始字符串的Base64字符串将存储在名称前缀为< code>__Secure-的< code>HttpOnly、< code>Secure、< code>SameSite cookie中。< code >域和< code >路径属性的正确值。字符串的SHA256散列也将在JWT的声明中传递。因此,即使XSS攻击将JWT访问令牌发送到攻击者控制的远程服务器,它也无法发送cookie中的原始字符串,因此服务器可以基于cookie的缺失拒绝请求。此外,通过使用适当的< code > content-security-policy 响应头,可以进一步减少XSS和脚本注入。

    注:

    >

  • SameSite=严格确保给定的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”

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

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

    然而,对于资源服务器上的每个API调用,授权服务器不参与验证令牌。资源服务器可以缓存授权服务器颁发的令牌。资源服务器可以使用内存中的数据网格(即Redis),或者,如果不能将所有内容存储在RAM中,则使用基于LSM的数据库(即Riak with层DB)来存储令牌。

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

    >

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

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

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

    >

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

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

    >

  • 为了保护用户免受CSRF的侵害,我们可以遵循Angular等框架中遵循的方法(如Angular HttpClient文档中所述,服务器必须发送一个非HttpOnly cookie(换句话说,可读cookie),其中包含该特定会话的唯一不可预测值。它应该是一个加密强度高的随机值。然后,客户端将始终读取cookie并在自定义HTTP标头中发送值(GET除外)

    要保护应用程序免受登录 CSRF 的影响,请始终检查引用标头,并仅在引用是受信任域时才接受请求。如果引用标头不存在或未列入白名单的域,只需拒绝请求即可。使用SSL/TLS时,通常存在反向链接。登陆页面(主要是信息性的,不包含登录表单或任何安全内容)可能很少放松,并允许缺少引用标头的请求

    应在服务器中阻止< code>TRACE HTTP方法,因为这可用于读取< code>httpOnly cookie

    另外,设置头< code > Strict-Transport-Security:max-age =

    此外,应使用如上所述的SameSite设置

    状态变量(Auth0使用它)-客户端将生成并与每个请求一起传递一个加密强随机随机数,服务器将回显该随机数及其响应,允许客户端验证随机数。它在Auth0文档中进行了解释

    最后,SSL/TLS对于所有通信都是强制性的-从今天起,低于1.1的TLS版本不符合PCI/DSS要求。应使用适当的密码套件来确保前向保密和经过验证的加密。此外,一旦用户明确单击“注销”,访问和刷新令牌就应被列入黑名单,以防止任何令牌滥用的可能性。

    1. RFC 6749-OAuth2.0
    2. OWASP JWT备忘单
    3. SameSite Cookie IETF草稿
    4. Cookie前缀
    5. RFC 6265-Cookie

  •  类似资料:
    • 我希望你一切都好。我挣扎了几天,因为Laravel护照,试图用javascript/vuejs消耗我自己的api。最后一个晚上,我几乎阅读了所有关于我问题的帖子,但我找不到任何解决办法。我希望你能帮助我,在这一点上谢谢你。 我已经建立了一个新的laravel应用程序,并安装了Laravel Passport,就像在Laravel 5.8(https://laravel.com/docs/5.8/p

    • 我实现了一个Java使用者,它使用来自Kafka主题的消息,然后将这些消息与POST请求一起发送到REST API。 假设一个消息已经被使用,但是Java类未能到达REST API。消息将永远不会被传递,但它将被标记为已消耗。处理这类案件最好的办法是什么?当且仅当来自REST API的响应成功时,我能以某种方式确认消息吗?

    • 我需要使用consume process Product模式来处理Kafka消息,并已使用Kafka事务管理器配置了Spring Kafka侦听器容器,还设置了事务id前缀以启用Kafka事务。我正在使用批处理的ack模式,并试图了解在这种模式下,在事务中何时提交偏移量。文档似乎表明,一旦使用了轮询中的所有记录,ack模式批提交偏移量——在事务上下文中也是这样吗,即每个轮询1个事务? 或者,在使用

    • D: \软件\Kafka\Kafka2.10-0.10.0.1\bin\windows 我使用上面的命令来消费消息,有什么我错过的吗?帮助我: 这个 那些是生产者和消费者......

    • Django 自带的认证系统足够应付大多数情况,但你或许不打算使用现成的认证系统。定制自己的项目的权限系统需要了解哪些一些关键点,即Django中哪些部分是能够扩展或替换的。这个文档提供了如何定制权限系统的细节。 “认证”后端 在以下情形时可被扩展:当一个 User 模型对象带有用户名和密码时,且需要有别于 Django 默认的认证功能。 你可为你的模型提供可通过 Django 权限系统检查的 定

    • 面试题 如何保证消息不被重复消费?或者说,如何保证消息消费的幂等性? 面试官心理分析 其实这是很常见的一个问题,这俩问题基本可以连起来问。既然是消费消息,那肯定要考虑会不会重复消费?能不能避免重复消费?或者重复消费了也别造成系统异常可以吗?这个是 MQ 领域的基本问题,其实本质上还是问你使用消息队列如何保证幂等性,这个是你架构里要考虑的一个问题。 面试题剖析 回答这个问题,首先你别听到重复消息这个