getmilpa/live-web

GitHub: getmilpa/live-web

为 Milpa Live 组件生命周期提供 HTML 渲染器和安全加固的 HTTP 实时交互端点的 PHP Web 框架。

Stars: 0 | Forks: 0

Milpa

# Milpa Live Web [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/getmilpa/live-web/actions/workflows/ci.yml) [![Packagist](https://img.shields.io/packagist/v/milpa/live-web.svg)](https://packagist.org/packages/milpa/live-web) [![PHP](https://img.shields.io/badge/php-%E2%89%A5%208.3-777bb4.svg)](https://www.php.net/) [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE) [![Docs](https://img.shields.io/badge/docs-API%20reference-blue.svg)](https://getmilpa.github.io/live-web/) `milpa/live` 定义了与渲染目标无关的组件生命周期(挂载 / 处理 / 渲染、契约、数据源)。`milpa/live-web` 则使该生命周期可以通过 HTTP 访问,并能在浏览器中渲染:它包含用于表单、看板和自动补全组件系列的 HTML 渲染器,一个零依赖的 XHTML 状态/交互传输编解码器,以及安全组件包 —— 包含 HMAC 状态签名、CSRF 和单次使用 nonce/重放保护 —— `LiveEndpoint` 将这些组合成一个坚固的 HTTP 入口点。 ## 安装 ``` composer require milpa/live-web ``` ## 它是什么 - **`LiveEndpoint`** —— HTTP 实时循环终端。验证请求方法、CSRF token 和已签名的状态信封;根据组件自身的契约对请求的操作进行授权;分发至组件的 `handle()`;并返回全新渲染的 HTML 以及一个新的已签名状态信封供客户端保留。每一个常规失败(错误的方法、缺少字段、无效签名、重放、未授权的操作)都会返回类型化的错误响应 —— 它绝不会因为预期的错误输入而抛出异常。 - **HTML 渲染器** —— `AutocompleteHtmlRenderer`、`FormPrimitiveHtmlRenderer`(`input`、`textarea`、`select`、`checkbox`)和 `DashboardHtmlRenderer`(外壳、侧边栏、顶栏、网格、面板、指标卡片、数据表等)各自通过 `@milpa/design` 系统将组件的状态转换为绑定 Alpine 的 HTML。`XhtmlComponentCompiler` 允许你直接在标记中编写 `` 组件树,并通过相同的渲染器流水线对其进行编译。 - **传输** —— `XhtmlStateTransferCodec` 将组件的状态/交互编码为带有 base64 JSON payload 的单一 `` / `` XHTML 元素。它本身不携带任何安全保证 —— 它是 `$inner` 编解码器,由 `SignedXhtmlStateTransferCodec` 进行包装。 - **安全** —— `HmacStateSigner`、`HmacCsrfGuard`、`FileNonceStore`、`SignedXhtmlStateTransferCodec`、`ContractInteractionAuthorizer` 和 `AllowListCorsPolicy` 是 `LiveEndpoint` 旨在信任的、具体的生产级类 —— 请参阅下方的[安全](#security)。 - **`AlpineRuntimeAdapter`** —— 内置渲染器所目标的 `Milpa\Live\Contracts\Client\ClientRuntimeAdapterInterface` 实现;它使用 `data-milpa-*` 属性标记根元素,并描述 Alpine runtime 脚本所使用的启动 payload/asset。 ## 快速示例 连接真正的安全类并通过 `LiveEndpoint` 发起一次交互 —— 这是 `tests/Http/LiveEndpointTest.php` 中执行测试的精简版本,可直接针对此包自身的 `vendor/` 运行: ``` use Milpa\Live\Adapters\Alpine\AlpineRuntimeAdapter; use Milpa\Live\Components\Autocomplete\AutocompleteComponent; use Milpa\Live\DataSource\ArrayDataSource; use Milpa\Live\DataSource\InMemoryDataSourceRegistry; use Milpa\Live\Http\LiveEndpoint; use Milpa\Live\Http\LiveHttpRequest; use Milpa\Live\Rendering\AutocompleteHtmlRenderer; use Milpa\Live\Runtime\InMemoryComponentRegistry; use Milpa\Live\Security\ContractInteractionAuthorizer; use Milpa\Live\Security\FileNonceStore; use Milpa\Live\Security\HmacCsrfGuard; use Milpa\Live\Security\HmacStateSigner; use Milpa\Live\Security\SignedXhtmlStateTransferCodec; use Milpa\Live\Transport\XhtmlStateTransferCodec; use Milpa\Live\ValueObjects\ComponentContext; // A real component (milpa/live) over a real data source. $sources = new InMemoryDataSourceRegistry(); $sources->register(new ArrayDataSource('customers.search', [ ['value' => 'acme', 'label' => 'Acme Studio', 'search' => 'agency design'], ['value' => 'milpa', 'label' => 'Milpa Labs', 'search' => 'framework components'], ])); $components = new InMemoryComponentRegistry(); $components->register('autocomplete', new AutocompleteComponent($sources)); // The real security wiring: HMAC-signed state + single-use replay nonce + CSRF. $codec = new SignedXhtmlStateTransferCodec( new XhtmlStateTransferCodec(), new HmacStateSigner($_ENV['LIVE_STATE_SECRET']), new FileNonceStore(sys_get_temp_dir() . '/milpa-live-nonces.json'), ); $csrf = new HmacCsrfGuard($_ENV['LIVE_CSRF_SECRET']); $endpoint = new LiveEndpoint( components: $components, codec: $codec, authorizer: new ContractInteractionAuthorizer($components), csrf: $csrf, route: '/live/autocomplete', renderers: ['autocomplete' => new AutocompleteHtmlRenderer(new AlpineRuntimeAdapter(), $codec)], ); // Mount the initial state (server-rendered on the page) and issue a CSRF token for the session. $context = new ComponentContext('customer-picker', route: '/autocomplete-demo'); $state = $components->get('autocomplete')->mount(['name' => 'customer', 'source' => 'customers.search'], $context); $sessionId = 'demo-session'; // however your app tracks sessions (e.g. the PHP session id) $csrfToken = $csrf->issueToken($sessionId, '/live/autocomplete'); $envelope = $codec->encodeState($state); // embed both in the SSR'd page // The client echoes $envelope + $csrfToken back on every interaction. $response = $endpoint->handle(new LiveHttpRequest( method: 'POST', action: 'search', stateEnvelope: $envelope, payload: ['query' => 'mil'], sessionId: $sessionId, csrfToken: $csrfToken, )); $response->status; // 200 $response->body['data']; // ['items' => [['value' => 'milpa', ...]]] $response->body['html']; // freshly rendered … $response->body['state']; // a freshly signed envelope for the next round-trip ``` ## 安全 `LiveEndpoint` 的信任模型,用一句话来概括:**客户端无法持有签名密钥,因此它从不自己构建状态信封** —— 它只会逐字节地回显该服务器签名并交给它的最后一个 `` 信封(首先嵌入在 SSR 页面中,然后在每次响应时刷新)。验证该信封就是证明它未被篡改的手段: - **篡改 → 拒绝。** 对已签名信封的任何更改 —— 组件 ID、状态 payload 或某个 claim —— 都会无法通过 HMAC-SHA256 验证,并且 `LiveEndpoint` 会在请求到达组件的 `handle()` 之前返回 `400 invalid_signature`。 - **重放 → 409,不会静默重用。** 当 `FileNonceStore`(或另一个 `NonceStoreInterface`)连接到 `SignedXhtmlStateTransferCodec` 时,每个签名都带有一个单次使用的 nonce;第二次解码完全相同的已签名信封会抛出 `ReplayedNonceException`,并且 `LiveEndpoint` 会响应 `409 replay_detected` —— 这是与服务器当前状态的冲突,而不是权限失败,因为该请求在第一次时是真正真实的。 - **CSRF 是一个独立且分离的关卡。** `HmacCsrfGuard` 将 token 绑定到为其签发的精确 `sessionId`/`route` 对;为一个 session 或 route 签发的 token 永远无法在另一个上验证通过,并且 CSRF 失败(`403 csrf`)会在状态信封被解码之前进行检查。 - **授权基于契约,而非环境。** `ContractInteractionAuthorizer` 只允许组件自身契约声明的操作,检查状态的所有者主体(如果有)是否与调用者匹配,并要求派生的 `milpa:component:{name}:{action}` scope —— 伪造看似合理操作名称的攻击者仍然只会得到 `403 action_not_allowed`。 所有这些都不是你必须记住的可选配置:`LiveEndpoint::handle()` 按顺序运行 方法 → CSRF → 签名/重放 → 授权,并将每个失败转换为相应的 HTTP 状态码,而不是抛出异常。 **`@milpa/design` 拓扑注意事项。** HTML 渲染器的 CSS 来自于 `Milpa\Live\Support\MilpaDesign`,它会在你项目根目录下的 `node_modules/@milpa/design` 解析 `@milpa/design` npm 包。如果你的布局不同(例如 monorepo、没有运行 `npm install` 的实验室检出代码),请将 `MILPA_DESIGN_PATH` 设置为 design 包的目录 —— 它优先于 npm 相对查找,并且会被 `MilpaDesign` 的每个方法优先检查。 ## 包含内容 | Namespace | 提供的内容 | |-----------|------------------| | `Milpa\Live\Http` | `LiveEndpoint` —— 坚固的 HTTP 实时循环入口点 | | `Milpa\Live\Security` | `HmacStateSigner`、`HmacCsrfGuard`、`FileNonceStore`、`SignedXhtmlStateTransferCodec`、`ContractInteractionAuthorizer`、`AllowListCorsPolicy`、`StaticBearerTokenVerifier` | | `Milpa\Live\Transport` | `XhtmlStateTransferCodec` —— 未签名的内部传输编解码器 | | `Milpa\Live\Rendering` | `AutocompleteHtmlRenderer`、`FormPrimitiveHtmlRenderer`、`DashboardHtmlRenderer`、`LatteTemplateRenderer`、`XhtmlComponentCompiler` | | `Milpa\Live\Adapters\Alpine` | `AlpineRuntimeAdapter` —— 内置的 `ClientRuntimeAdapterInterface` | | `Milpa\Live\Support` | `Html`(转义助手)、`MilpaDesign`(设计系统路径解析) | | `Milpa\Live\Contracts\*` | `Security`、`Rendering` 和 `Transport` 接合处 —— `CsrfGuardInterface`、`StateSignerInterface`、`NonceStoreInterface`、`StateTransferCodecInterface`、`ComponentRendererInterface`、`MarkupCompilerInterface`、`TemplateRendererInterface`、`CorsPolicyInterface`、`InteractionAuthorizerInterface`、`TokenVerifierInterface` | | `Milpa\Live\ValueObjects` | `StateSignature`、`AuthorizationResult`、`CorsDecision` | 每个公共符号都带有 DocBlock。 ## 环境要求 - PHP **≥ 8.3** 且安装了 **`ext-dom`** 扩展 - [`milpa/core`](https://packagist.org/packages/milpa/core) **^0.6** - [`milpa/live`](https://packagist.org/packages/milpa/live) **^0.1** ## 文档 **完整 API 参考:[getmilpa.github.io/live-web](https://getmilpa.github.io/live-web/)** — 直接从源代码 DocBlocks 生成,并配以 Milpa 设计系统。 ## 许可证 [Apache-2.0](LICENSE) © Rodrigo Vicente - TeamX Agency. Milpa 由 **[Rodrigo Vicente - TeamX Agency](https://teamx.agency/?utm_source=github&utm_medium=readme&utm_campaign=milpa&utm_content=live-web)** 设计、构建和维护。
标签:CSRF防护, ffuf, OpenVAS, PHP, Syscall, Web安全加固, Web开发, 前端渲染, 组件库