Open5

ajaxでsession更新が安定しない、livewireで安定する問題

dev63dev63

webkit

そもそもsessionのキャッシュ機能がある
LRUキャッシュなので最近使っているデータだと残っていたから、更新できなかった?

webkit/common.go:2497-2527

func (c *lruSessionCache) Get(sessionKey string) (any, bool) {
	c.Lock()
	defer c.Unlock()

	if elem, ok := c.m[sessionKey]; ok {
		c.q.MoveToFront(elem)
		return elem.Value.(*lruSessionCacheEntry).state, true
	}
	return nil, false
}

// lruClientSessionCache is a ClientSessionCache implementation that
// uses an LRU caching strategy.
type lruClientSessionCache struct {
	lruSessionCache
}

func (c *lruClientSessionCache) Put(sessionKey string, cs *ClientSessionState) {
	c.lruSessionCache.Put(sessionKey, cs)
}

func (c *lruClientSessionCache) Get(sessionKey string) (*ClientSessionState, bool) {
	cs, ok := c.lruSessionCache.Get(sessionKey)
	if !ok {
		return nil, false
	}
	return cs.(*ClientSessionState), true
}

// lruServerSessionCache is a ServerSessionCache implementation that
// uses an LRU caching strategy.
dev63dev63

livewireでsessionを正しく初期化して、ちゃんと動かすための場所

PersistentMiddleware.php:14-23

    protected static $persistentMiddleware = [
        \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
        \Laravel\Jetstream\Http\Middleware\AuthenticateSession::class,
        \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \App\Http\Middleware\RedirectIfAuthenticated::class,
        \Illuminate\Auth\Middleware\Authenticate::class,
        \Illuminate\Auth\Middleware\Authorize::class,
        \App\Http\Middleware\Authenticate::class,
    ];

PersistentMiddleware.php:101-129

protected function makeFakeRequest()
    {
        $originalPath = $this->formatPath($this->path);
        $originalMethod = $this->method;

        $currentPath = $this->formatPath(request()->path());

        // Clone server bag to ensure changes below don't overwrite the original.
        $serverBag = clone request()->server;

        // Replace the Livewire endpoint path with the path from the original request.
        $serverBag->set(
            'REQUEST_URI',
            str_replace($currentPath, $originalPath, $serverBag->get('REQUEST_URI'))
        );

        $serverBag->set('REQUEST_METHOD', $originalMethod);

        /**
         * Make the fake request from the current request with path and method changed so
         * all other request data, such as headers, are available in the fake request,
         * but merge in the new server bag with the updated `REQUEST_URI`.
         */
        $request = request()->duplicate(
            server: $serverBag->all()
        );

        return $request;
    }
  • \Illuminate\Session\Middleware\StartSessionは通常のLaravelリクエストで毎回呼ばれるが、ajaxだと通過していなさそう→根拠(?)

  • livewireはlaravel標準middlewareの \Laravel\Jetstream\Http\Middleware\AuthenticateSession::class に依存しており、これを呼び出すことでsessionを正しく解決

dev63dev63

WebKitSettings.cpp:3187-3212

セッションキャッシュをブラウザの設定で無効にできるかもしれない
→無効にできたらajaxで実験できそう

gboolean webkit_settings_get_enable_page_cache(WebKitSettings* settings)
{
    g_return_val_if_fail(WEBKIT_IS_SETTINGS(settings), FALSE);

    return settings->priv->preferences->usesBackForwardCache();
}

/**
 * webkit_settings_set_enable_page_cache:
 * @settings: a #WebKitSettings
 * @enabled: Value to be set
 *
 * Set the #WebKitSettings:enable-page-cache property.
 */
void webkit_settings_set_enable_page_cache(WebKitSettings* settings, gboolean enabled)
{
    g_return_if_fail(WEBKIT_IS_SETTINGS(settings));

    WebKitSettingsPrivate* priv = settings->priv;
    bool currentValue = priv->preferences->usesBackForwardCache();
    if (currentValue == enabled)
        return;

    priv->preferences->setUsesBackForwardCache(enabled);
    g_object_notify_by_pspec(G_OBJECT(settings), sObjProperties[PROP_ENABLE_PAGE_CACHE]);
}
dev63dev63

deepwikiの説明メモ

AuthenticateSessionミドルウェアについて、AjaxとLivewireでの動作の違いを説明します。

AuthenticateSessionミドルウェアの役割
AuthenticateSessionミドルウェアは、セッションベースの認証でセキュリティを強化するために、ユーザーのパスワードハッシュをセッションに保存し、リクエストごとに検証します。

AuthenticateSession.php:58-64

        if (! $request->session()->has('password_hash_'.$this->auth->getDefaultDriver())) {
            $this->storePasswordHashInSession($request);
        }

        if (! hash_equals($request->session()->get('password_hash_'.$this->auth->getDefaultDriver()), $request->user()->getAuthPassword())) {
            $this->logout($request);
        }

動作メカニズム
パスワードハッシュの検証プロセス
初回アクセス時: セッションにパスワードハッシュが存在しない場合、現在のユーザーのパスワードハッシュをセッションに保存します。 AuthenticateSession.php:58-60

後続リクエスト: セッションに保存されたパスワードハッシュと現在のユーザーのパスワードハッシュを比較し、一致しない場合はログアウトします。 AuthenticateSession.php:62-64

レスポンス後の処理: リクエスト処理完了後、ユーザーが存在する場合は再度パスワードハッシュをセッションに保存します。 AuthenticateSession.php:66-70

        return tap($next($request), function () use ($request) {
            if (! is_null($this->guard()->user())) {
                $this->storePasswordHashInSession($request);
            }
        });

AjaxとLivewireの違い
Ajaxの場合(成功率が低い理由)
AjaxリクエストがAuthenticateSessionミドルウェアを通らない場合:

セッションにパスワードハッシュが保存されない
認証状態の整合性チェックが行われない
セッション管理が不完全になる可能性がある
Livewireの場合(毎回成功する理由)
Livewireは通常のHTTPリクエストとして処理されるため:

AuthenticateSessionミドルウェアが適用される
各リクエストでパスワードハッシュの検証と更新が行われる
セッションの整合性が保たれる
ミドルウェアの設定
AuthenticateSessionはauth.sessionエイリアスで登録されており、 Middleware.php:784 必要に応じてルートやミドルウェアグループに適用できます。

'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,

Notes
AuthenticateSessionミドルウェアは、セッション固定攻撃やパスワード変更後の他デバイスでのセッション無効化などのセキュリティ機能を提供します。Ajaxリクエストでも同様の保護を受けるには、該当するルートにauth.sessionミドルウェアを適用する必要があります。

Wiki pages you might want to explore:

Authentication System (laravel/framework)

dev63dev63

todo: 実験用のrepoで再現してみる