You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

安全的用户输入占位符替换:解决Dehydrate/Rehydrate冲突问题

实现安全的Dehydrate/Rehydrate(脱水/重水合)功能

功能需求

实现Dehydrate(脱水):将用户输入文本中指定的目标字符串替换为占位符;后续执行Rehydrate(重水合),用新字符串替换占位符,还原内容结构。

示例:
原始文本:

Visit [my page](http://example.com/posts/)

脱水时,将$search='http://example.com'替换为%WEBSITE_URL%,得到:

Visit the page [my page](%WEBSITE_URL%/posts/)

重水合时,用$replace='http://another-site.net'替换占位符,得到:

Visit the page [my page](http://another-site.net/posts/)

朴素解决方案的问题

最初直接用字符串替换的实现存在明显漏洞:

$search = 'http://example.com';
$dehydrated = str_replace($search, '%WEBSITE_URL%', $text);

// 重水合反向操作
$replace = 'http://another-site.net';
$rehydrated = str_replace('%WEBSITE_URL%', $replace, $dehydrated);

如果用户输入包含字面量%WEBSITE_URL%,重水合时会被错误替换。比如:
输入文本:

$text = 'Visit [my page](http://example.com/posts/). Placeholders are %WEBSITE_URL%';

错误结果:

Visit [my page](http://another-site.net/posts/). Placeholders are http://another-site.net

正确结果应该保留原生占位符:

Visit [my page](http://another-site.net/posts/). Placeholders are %WEBSITE_URL%

改进方案的局限性

为解决上述问题,改进方案先转义所有%为%%,再替换目标字符串,重水合时用正则匹配未转义的占位符:

// 脱水:先转义%,再替换目标字符串
$search = 'http://example.com';
$dehydrated = str_replace(['%', $search], ['%%', '%WEBSITE_URL%'], $text);

// 重水合:匹配未被转义的%WEBSITE_URL%,再恢复%%为%
$replace = 'http://another-site.net';
$rehydrated = preg_replace('/%WEBSITE_URL%(?!%)/', $replace, $dehydrated);
$rehydrated = str_replace('%%', '%', $rehydrated);

但该方案仍有漏洞:若用户输入%http://example.com%,脱水后会变成%%WEBSITE_URL%%,重水合后会被还原为%http://another-site.net%,而非用户原本输入的%http://example.com%。

最终安全解决方案

要彻底避免冲突,核心思路是让占位符与用户可能输入的内容完全隔离,同时明确区分目标字符串和原生特殊标记。

方案一:唯一占位符+转义机制

使用不易被用户输入的占位符格式,配合转义处理原生占位符:

// 定义唯一占位符,采用双下划线包裹降低冲突概率
define('PLACEHOLDER', '__WEBSITE_URL__');

// 脱水函数
function dehydrate(string $text, string $search): string {
    // 转义文本中已存在的占位符,避免混淆
    $text = str_replace(PLACEHOLDER, '\\' . PLACEHOLDER, $text);
    // 精确匹配目标字符串并替换为占位符(preg_quote处理正则特殊字符)
    $searchPattern = '/' . preg_quote($search, '/') . '/';
    return preg_replace($searchPattern, PLACEHOLDER, $text);
}

// 重水合函数
function rehydrate(string $dehydratedText, string $replace): string {
    // 仅替换未被转义的占位符
    $replacePattern = '/(?<!\\\\)' . preg_quote(PLACEHOLDER, '/') . '/';
    $text = preg_replace($replacePattern, $replace, $dehydratedText);
    // 恢复被转义的原生占位符
    return str_replace('\\' . PLACEHOLDER, PLACEHOLDER, $text);
}

// 测试示例
$text = 'Visit [my page](http://example.com/posts/), %http://example.com%, __WEBSITE_URL__';
$search = 'http://example.com';
$replace = 'http://another-site.net';

$dehydrated = dehydrate($text, $search);
// 脱水结果:Visit [my page](__WEBSITE_URL__/posts/), %__WEBSITE_URL__%, \__WEBSITE_URL__

$rehydrated = rehydrate($dehydrated, $replace);
// 重水合结果:Visit [my page](http://another-site.net/posts/), %http://another-site.net%, __WEBSITE_URL__

该方案通过转义原生占位符、精确匹配未转义的目标占位符,彻底避免误替换。

方案二:随机UUID作为临时占位符

如果要完全消除占位符冲突的可能性,可使用随机UUID作为临时占位符(全局唯一,用户输入不可能匹配):

function dehydrate(string $text, string $search): array {
    $placeholder = 'UUID_' . uuid_create(UUID_TYPE_RANDOM);
    // 转义可能存在的临时占位符(概率极低)
    $text = str_replace($placeholder, '\\' . $placeholder, $text);
    $dehydrated = str_replace($search, $placeholder, $text);
    return [
        'text' => $dehydrated,
        'placeholder' => $placeholder
    ];
}

function rehydrate(string $dehydratedText, string $replace, string $placeholder): string {
    $text = str_replace($placeholder, $replace, $dehydratedText);
    return str_replace('\\' . $placeholder, $placeholder, $text);
}

// 使用方式
$result = dehydrate($text, $search);
$rehydrated = rehydrate($result['text'], $replace, $result['placeholder']);

这种方案从根源上避免了占位符冲突,适合对安全性要求极高的场景。


内容的提问来源于stack exchange,提问作者quantumSoup

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 06:45:03