psr7-sessions/storageless 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

psr7-sessions/storageless

Composer 安装命令:

composer require psr7-sessions/storageless

包简介

Storageless PSR-7 Session support

README 文档

README

Mutation testing badge Type Coverage Packagist Packagist

PSR7Session is a PSR-7 and PSR-15 compatible middleware that enables session without I/O usage in PSR-7 based applications.

Proudly brought to you by ocramius, malukenho and lcobucci.

Installation

composer require psr7-sessions/storageless

Usage

You can use the PSR7Sessions\Storageless\Http\SessionMiddleware in any PSR-15 compatible middleware.

In a mezzio/mezzio application, this would look like following:

use Lcobucci\JWT\Configuration as JwtConfig;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
use PSR7Sessions\Storageless\Http\SessionMiddleware;
use PSR7Sessions\Storageless\Http\Configuration as StoragelessConfig;

$app = new \Mezzio\Application(/* ... */);

$app->pipe(new SessionMiddleware(
    StoragelessConfig::fromJwtConfiguration(
        JwtConfig::forSymmetricSigner(
            new Signer\Hmac\Sha256(),
            InMemory::base64Encoded('OpcMuKmoxkhzW0Y1iESpjWwL/D3UBdDauJOe742BJ5Q='), // replace this with a key of your own (see below)
        )
    )
));

After this, you can access the session data inside any middleware that has access to the Psr\Http\Message\ServerRequestInterface attributes:

$app->get('/get', function (ServerRequestInterface $request, ResponseInterface $response) : ResponseInterface {
    $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
    assert($session instanceof \PSR7Sessions\Storageless\Session\SessionInterface);
    $session->set('counter', $session->get('counter', 0) + 1);

    $response
        ->getBody()
        ->write('Counter Value: ' . $session->get('counter'));

    return $response;
});

You can do this also in asynchronous contexts and long-running processes, since no super-globals nor I/O are involved.

It is recommended that you use a key with lots of entropy, preferably generated using a cryptographically secure pseudo-random number generator (CSPRNG). You can use the CryptoKey tool to do this for you.

Note that you can also use asymmetric keys; please refer to lcobucci/jwt documentation:

  1. The Configuration object: https://lcobucci-jwt.readthedocs.io/en/stable/configuration/
  2. Supported algorithms: https://lcobucci-jwt.readthedocs.io/en/stable/supported-algorithms/

Session Hijacking mitigation

To mitigate the risks associated to cookie stealing and thus session hijacking, you can bind the user session to its IP ($_SERVER['REMOTE_ADDR']) and User-Agent ($_SERVER['HTTP_USER_AGENT']) by enabling client fingerprinting:

use PSR7Sessions\Storageless\Http\SessionMiddleware;
use PSR7Sessions\Storageless\Http\Configuration as StoragelessConfig;
use PSR7Sessions\Storageless\Http\ClientFingerprint\Configuration as FingerprintConfig;

$app = new \Mezzio\Application(/* ... */);

$app->pipe(new SessionMiddleware(
    StoragelessConfig::fromJwtConfiguration(/* ... */)
        ->withClientFingerprintConfiguration(
            FingerprintConfig::forIpAndUserAgent()
        )
));

If your PHP service is behind a reverse proxy of yours, you may need to retrieve the client IP from a different source of truth. In such cases you can extract the information you need by writing a custom \PSR7Sessions\Storageless\Http\ClientFingerprint\Source implementation:

use Psr\Http\Message\ServerRequestInterface;
use PSR7Sessions\Storageless\Http\SessionMiddleware;
use PSR7Sessions\Storageless\Http\Configuration as StoragelessConfig;
use PSR7Sessions\Storageless\Http\ClientFingerprint\Configuration as FingerprintConfig;
use PSR7Sessions\Storageless\Http\ClientFingerprint\Source;

$app = new \Mezzio\Application(/* ... */);

$app->pipe(new SessionMiddleware(
    StoragelessConfig::fromJwtConfiguration(/* ... */)
        ->withClientFingerprintConfiguration(
            FingerprintConfig::forSources(new class implements Source{
                 public function extractFrom(ServerRequestInterface $request): string
                 {
                     return $request->getHeaderLine('X-Real-IP');
                 }
            })
        )
));

Examples

Simply browse to the examples directory in your console, then run

php -S localhost:9999 index.php

Then try accessing http://localhost:9999: you should see a counter that increases at every page refresh

WHY?

In most PHP+HTTP related projects, ext/session serves its purpose and allows us to store server-side information by associating a certain identifier to a visiting user-agent.

What is the problem with ext/session?

This is all fair and nice, except for:

  • relying on the $_SESSION superglobal
  • relying on the shutdown handlers in order to "commit" sessions to the storage
  • having a huge limitation of number of active users (due to storage)
  • having a lot of I/O due to storage
  • having serialized data cross different processes (PHP serializes and de-serializes $_SESSION for you, and there are security implications)
  • having to use a centralized storage for setups that scale horizontally
  • having to use sticky sessions (with a "smart" load-balancer) when the storage is not centralized
  • not designed to be used for multiple dispatch cycles

What does this project do?

This project tries to implement storage-less sessions and to mitigate the issues listed above.

Assumptions

  • your sessions are fairly small and contain only few identifiers and some CSRF tokens. Small means < 400 bytes
  • data in your session is JsonSerializable or equivalent
  • data in your session is freely readable by the client

How does it work?

Session data is directly stored inside a session cookie as a JWT token.

This approach is not new, and is commonly used with Bearer tokens in HTTP/REST/OAuth APIs.

In order to guarantee that the session data is not modified, that the client can trust the information and that the expiration date is mutually agreed between server and client, a JWT token is used to transmit the information.

The JWT token is always signed to ensure that the user-agent is never able to manipulate the session. Both symmetric and asymmetric keys are supported for signing/verifying tokens.

Advantages

  • no storage required
  • no sticky sessions required (any server having a copy of the private or public keys can generate sessions or consume them)
  • can transmit cleartext information to the client, allowing it to share some information with the server (a standard example is about sharing the "username" or "user-id" in a given session)
  • can transmit encrypted information to the client, allowing server-only consumption of the information
  • not affected by PHP serialization RCE attacks
  • not limited to PHP process scope: can have many sessions per process
  • no reliance on global state
  • when in a multi-server setup, you may allow read-only access to servers that only have access to public keys, while writes are limited to servers that have access to private keys
  • can be used over multiple dispatch cycles

Configuration options

Please refer to the configuration documentation.

Known limitations

Please refer to the limitations documentation.

Contributing

Please refer to the contributing notes.

License

This project is made public under the MIT LICENSE.

psr7-sessions/storageless 适用场景与选型建议

psr7-sessions/storageless 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 399.76k 次下载、GitHub Stars 达 650, 最近一次更新时间为 2016 年 09 月 23 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 psr7-sessions/storageless 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 psr7-sessions/storageless 我们能提供哪些服务?
定制开发 / 二次开发

基于 psr7-sessions/storageless 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 399.76k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 652
  • 点击次数: 32
  • 依赖项目数: 13
  • 推荐数: 0

GitHub 信息

  • Stars: 650
  • Watchers: 19
  • Forks: 38
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2016-09-23