php 如何使用Laravel和React实现自定义XSRF保护

jgzswidk  于 2022-12-10  发布在  PHP
关注(0)|答案(1)|浏览(130)

我在前端使用Next.js,使用Laravel作为API来创建一个购物应用程序。我使用Shopify's PHP API library来处理身份验证(OAuth),所以我没有使用Laravel的内置auth。我只需要为某些路径添加XSRF保护,比如/login,但是我想检查一下我是否安全地这样做了。下面是我到目前为止所做的工作:

拉瑞维尔API

1.在app/Http/Kernal.php中,我添加了Laravel的会话中间件,为每个访问API路径的用户存储一个会话:

protected $middlewareGroups = [
        'web' => [
            // ...
        ],

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, // I Uncommented this
            \Illuminate\Session\Middleware\StartSession::class, // I added this
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

这会成功储存laravel_session Cookie。
1.在routes/api.php中,我创建了一个路由来生成一个随机令牌,将其存储在用户的会话中,重新生成用户的会话ID(我相信这可以防止会话固定?),并为我的所有子域设置一个XSRF-TOKEN cookie(httpOnly=false,以便稍后可以使用JS附加到header)。

// Generate xsrf token and store in user's session (laravel_session)
Route::get('/xsrf-token', function(Request $request) {
   $xsrfToken = md5(uniqid(mt_rand(), true));
   $request->session()->put('XSRF', $xsrfToken);
   $request->session()->regenerate();
   if(!$request->session()->get('XSRF')) {
        return response()->json(['message' => 'Could not set XSRF token.', 403]);
   }
   return response()->json(['message', 'success'], 200)->withCookie(
        'XSRF-TOKEN', $xsrfToken, 120, '/', config('session')['domain'], true, false
   );
});

1.然后我创建了一个中间件函数来检查令牌是否有效。如果在头中发送的令牌(通过React前端中的Js附加)等于存储在会话中的令牌,则请求有效,我们可以继续。

public function handle(Request $request, Closure $next)
    {
        $headerXsrfToken = request()->header('x-xsrf-token');
        $sessionXsrfToken = request()->session()->get('XSRF');
        if(
            !$headerXsrfToken ||
            !$sessionXsrfToken ||
            strcmp($headerXsrfToken, $sessionXsrfToken) !== 0
        ) {
            return response()->json(['message' => 'Invalid or missing XSRF token'], 403);
        }

        $request->session()->forget('XSRF');
        $response = $next($request);
        return $response->withoutCookie('XSRF-TOKEN');
    }

1.创建一个路由以测试此功能:

Route::get('test-xsrf', function() {
    return response('XSRF token is valid!!');
})->middleware('xsrf.check');

下一个.js前端

1.当点击一个按钮时,它会调用handleValidateCSRFTokenhandleValidateCSRFToken首先调用/api/xsrf-token/api/xsrf-token设置XSRF-TOKEN cookie。然后我们向受保护的路由发出请求(axios自动将XSRF-TOKEN cookie值附加到x-xsrf-token头中)。

import axios from "axios";

const API_URL = "https://devapi.customerlift.app";

const Test = () => {
  const handleValidateCSRFToken = async () => {
    // Get csrf token cookies
    const resA = await axios.get(`${API_URL}/api/xsrf-token`, {
      withCredentials: true, // Allow cookies to be sent and set
    });
    console.log(resA);

    // Make request to xsrf-token-protected route (axios automatically sets x-xsrf-token header from the XSRF-TOKEN cookie)
    const resB = await axios.get(`${API_URL}/api/test-xsrf`, {
      withCredentials: true,
    });
    console.log(resB.data);
  };
  return (
    <button
      className="m-5 bg-blue-500 text-white p-3"
      onClick={handleValidateCSRFToken}
    >
      Validate XSRF Token
    </button>
  );
};

export default Test;

一切似乎都工作得很好,但想检查是否有人可以发现任何问题与我所做的,因为这是我第一次不得不实现这一点由我自己。

ozxc1zmp

ozxc1zmp1#

Laravel已将CSRF令牌内置到框架中,并可通过以下方式访问请求/会话库:
$request->session()->token()
或使用helper函数:
csrf_token()
我不知道你为什么要自己生成。
您可以像这样添加 meta标签:
<meta name="csrf-token" content="{{ csrf_token() }}">
然后在JS代码中,通过获取 meta标记上的属性值将其附加到请求中。
然后只需使用内置的中间件:应用\Http\中间件\验证证书令牌
您需要确保在登录和注销时重新生成令牌,并且它需要有一个合理的到期日期。
我建议使用框架中内置的这些方法,而不是使用您自己的方法。这样可能会更安全,或者至少同等安全--而且您自己定制的方法似乎没有什么好处。您不必使用Laravel的身份验证来使用已经内置的CSRF保护。

相关问题