我在前端使用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.当点击一个按钮时,它会调用handleValidateCSRFToken
。handleValidateCSRFToken
首先调用/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;
一切似乎都工作得很好,但想检查是否有人可以发现任何问题与我所做的,因为这是我第一次不得不实现这一点由我自己。
1条答案
按热度按时间ozxc1zmp1#
Laravel已将CSRF令牌内置到框架中,并可通过以下方式访问请求/会话库:
$request->session()->token()
或使用helper函数:
csrf_token()
我不知道你为什么要自己生成。
您可以像这样添加 meta标签:
<meta name="csrf-token" content="{{ csrf_token() }}">
然后在JS代码中,通过获取 meta标记上的属性值将其附加到请求中。
然后只需使用内置的中间件:应用\Http\中间件\验证证书令牌
您需要确保在登录和注销时重新生成令牌,并且它需要有一个合理的到期日期。
我建议使用框架中内置的这些方法,而不是使用您自己的方法。这样可能会更安全,或者至少同等安全--而且您自己定制的方法似乎没有什么好处。您不必使用Laravel的身份验证来使用已经内置的CSRF保护。