修复由于在文本编辑器中编辑 MySQL 数据库而损坏的序列化数据?

Posted

技术标签:

【中文标题】修复由于在文本编辑器中编辑 MySQL 数据库而损坏的序列化数据?【英文标题】:Fix serialized data broken due to editing MySQL database in a text editor? 【发布时间】:2013-02-14 20:12:42 【问题描述】:

背景:我下载了我的 WordPress 站点数据库的 *.sql 备份,并将旧数据库表前缀的所有实例替换为新实例(例如,从默认 wp_asdfghjkl_)。

我刚刚了解到 WordPress 在数据库中使用了序列化的 php 字符串,而我所做的事情会破坏序列化字符串长度的完整性。

问题是,我在得知这一点之前删除了备份文件(因为我的网站仍然运行良好),并从那以后安装了许多插件。因此,我无法恢复原状,因此我想知道两件事:

    如果可能的话,我该如何解决这个问题?

    这会导致什么样的问题?

(This article 表示,例如,WordPress 博客可能会丢失其设置和小部件。但这似乎并没有发生在我身上,因为我博客的所有设置都完好无损。但我不知道至于内部可能会损坏什么,或者将来会造成什么问题。因此提出了这个问题。)

【问题讨论】:

对于研究人员,请参阅这篇文章以纠正序列化字符串中的错误字节数。 ***.com/a/55074706/2943403 它比 Brandon Elliott 的更容易阅读,比 wsizoo 的更精致,并且比 Benubird 的不耐分号的 sn-p 更健壮。 【参考方案1】:

访问此页面:http://unserialize.onlinephpfunctions.com/

在该页面上,您应该会看到这个示例序列化字符串:a:1:s:4:"Test";s:17:"unserialize here!";。拿一块——s:4:"Test";。这意味着“字符串”,4 个字符,然后是实际的字符串。我很确定您所做的事情导致数字字符计数与字符串不同步。使用上述网站上的工具,例如,如果将“Test”更改为“Tes”,您会看到出现错误。

您需要做的是让这些字符数与您的新字符串匹配。如果您没有损坏任何其他编码(删除冒号或其他内容),那么应该可以解决问题。

【讨论】:

感谢您的解释!发现问题后,我发现此代码再次更正序列化。检查***.com/a/38890855/2323296【参考方案2】:

在尝试将域从 localhost 更改为真实 URL 后,我遇到了同样的问题。经过一番搜索,我在 Wordpress 文档中找到了答案:

https://codex.wordpress.org/Moving_WordPress

我将引用那里写的内容:

为避免该序列化问题,您有以下三种选择:

如果可以,请使用 Better Search Replace 或 Velvet Blues Update URLs 插件 > 访问您的仪表板。 如果您的托管服务提供商(或您)已安装 WP-CLI,请使用 WP-CLI 的搜索替换。 在您的数据库上手动运行搜索和替换查询。注意:仅对 wp_posts 表执行搜索和替换。

我最终使用了 WP-CLI,它能够在不破坏序列化的情况下替换数据库中的内容:http://wp-cli.org/commands/search-replace/

【讨论】:

谢谢。不知道为什么这没有引起更多关注。遇到了同样的问题,最后也使用了WP-CLI。如果您有很多外观要改变,这是最简单的方法。 这正是我一直在寻找的,不确定旧版本,但使用 --precise 标志是关键。【参考方案3】:

我知道这是一个老问题,但我想迟到总比没有好。我最近遇到了这个问题,在继承了一个对序列化数据执行了查找/替换的数据库之后。经过几个小时的研究,我发现这是因为字符串计数关闭了。不幸的是,有太多的数据有很多转义和换行符,在某些情况下我不知道如何计算,而且我有太多的数据,我需要一些自动化的东西。

在此过程中,我偶然发现了这个问题,Benubird 的帖子帮助我走上了正确的道路。他的示例代码不适用于复杂数据的生产使用,其中包含大量特殊字符和 html,嵌套层次非常深,并且无法正确处理某些转义字符和编码。因此,我对其进行了一些修改,并花费了无数小时来解决其他错误,以使我的版本“修复”序列化数据。

// do some DB query here
while($res = db_fetch($qry))
    $str = $res->data;
    $sCount=1; // don't try to count manually, which can be inaccurate; let serialize do its thing
    $newstring = unserialize($str);
    if(!$newstring) 
        preg_match_all('/s:([0-9]+):"(.*?)"(?=;)/su',$str,$m);
#           preg_match_all("/s:([0-9]+):(\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\")(?=;)/u",$str,$m); // alternate: almost works but leave quotes in $m[2] output
#           print_r($m); exit;
        foreach($m[1] as $k => $len) 
            /*** Possibly specific to my case: Spyropress Builder in WordPress ***/
            $m_clean = str_replace('\"','"',$m[2][$k]); // convert escaped double quotes so that HTML will render properly
            // if newline is present, it will output directly in the HTML
            // nl2br won't work here (must find literally; not with double quotes!)
            $m_clean = str_replace('\n', '<br />', $m_clean); 
            $m_clean = nl2br($m_clean);  // but we DO need to convert actual newlines also
            /*********************************************************************/
            if($sCount)
                $m_new = $m[0][$k].';'; // we must account for the missing semi-colon not captured in regex!
                // NOTE: If we don't flush the buffers, things like <img src="http://whatever" can be replaced with <img src="//whatever" and break the serialize count!!!                  
                ob_end_flush(); // not sure why this is necessary but cost me 5 hours!!
                $m_ser = serialize($m_clean);
                if($m_new != $m_ser) 
                    print "Replacing: $m_new\n";
                    print "With: $m_ser\n";
                    $str = str_replace($m_new, $m_ser, $str);
                
            
            else
                $m_len = (strlen($m[2][$k]) - substr_count($m[2][$k],'\n'));
                if($len != $m_len) 
                    $newstr='s:'.$m_len.':"'.$m[2][$k].'"';
                    echo "Replacing: $m[0][$k]\n";
                    echo "With: $newstr\n\n";
                    $str = str_replace($m_new, $newstr, $str);
                
            
        
        print_r($str); // this is your FIXED serialized data!! Yay!
    

关于我的更改的一些令人讨厌的解释:

我发现尝试以 Benubird 的代码为基础对大型数据集进行计数过于不准确,因此我最终只使用序列化来确保计数准确无误。 我避免使用 try/catch,因为在我的情况下,try 会成功,但只是返回一个空字符串。因此,我改为检查空数据。 我尝试了许多正则表达式,但只有 Benubird 上的一个 mod 可以准确处理所有情况。具体来说,我必须修改检查“;”的部分因为它会在 CSS 上匹配,例如 "width:100%; height:25px;"并打破了输出。所以,我使用积极的前瞻来匹配“;”在双引号集之外。 我的案例有很多换行符、HTML 和转义的双引号,所以我不得不添加一个块来清理它们。 有一些奇怪的情况,数据会被正则表达式错误地替换,然后序列化也会错误地计算它。我在任何网站上都没有找到任何帮助解决此问题的方法,最后认为这可能与缓存或类似的东西有关,并尝试刷新输出缓冲区 (ob_end_flush()),这很有效,谢天谢地!

希望这对某人有所帮助...花了我将近 20 个小时,包括研究和处理奇怪的问题! :)

【讨论】:

您能否为 Windows 制作一个可执行文件,该文件具有在 SQL 文件中搜索和替换字符串的选项?或者至少是一个 PHP 文件,您可以在其中指定源文件、目标文件、搜索字符串和替换字符串。 这个怎么用? 我的 IDE 说 $m_new 没有为字符串定义 $str = str_replace($m_new, $newstr, $str);【参考方案4】:

此脚本 (https://interconnectit.com/products/search-and-replace-for-wordpress-databases/) 可以帮助在任何地方使用正确的 URL 更新 sql 数据库,而不会遇到序列化数据问题,因为它会更新“字符数”,这可能会在出现序列化数据时使您的 URL 不同步。

步骤如下:

    如果您已经导入了一个混乱的数据库(小部件不是 工作,主题选项不存在等),只需删除该数据库 使用 PhpMyAdmin。也就是说,删除上面的所有内容。然后导出并 手头有一个未经编辑的旧数据库转储。

    现在您必须将(未编辑的)旧数据库导入 新创建的一个。您可以通过导入或复制来执行此操作 来自 PhpMyAdmin 的数据库。请注意,到目前为止,我们还没有做任何 搜索和替换呢;我们只有一个旧的数据库内容和 用自己的用户名和密码构建一个新的数据库。此时您的网站可能无法访问。

    确保您的 WordPress 文件刚刚上传到 服务器上的正确文件夹,然后编辑您的 wp-config.php 以使其 连接新数据库。 将脚本上传到“秘密”文件夹 - 只是为了安全 原因 - 与 wp-admin、wp-content 和 wp-includes 处于同一级别。搜索后不要忘记将其全部删除 替换已经发生,因为您冒着提供数据库详细信息的风险 对整个互联网开放。 现在将浏览器指向秘密文件夹,然后使用脚本即可 界面。这是非常不言自明的。使用后,我们继续 将其从服务器中彻底删除。

这应该可以正确更新您的数据库,而不会出现任何序列化数据问题:新的 URL 将在各处设置,序列化数据字符数将相应更新。

将传递小部件和主题设置 - WordPress 中使用序列化数据的两个典型位置。

已完成并经过测试的解决方案!

【讨论】:

【参考方案5】:

如果错误是由于字符串长度不正确(我经常看到的),那么您应该能够修改此脚本来修复它:

foreach($strings as $key => $str)

    try 
        unserialize($str);
     catch(exception $e) 
        preg_match_all('#s:([0-9]+):"([^;]+)"#',$str,$m);
        foreach($m[1] as $k => $len) 
            if($len != strlen($m[2][$k])) 
                $newstr='s:'.strlen($m[2][$k]).':"'.$m[2][$k].'"';
                echo "len mismatch: $m[0][$k]\n";
                echo "should be:    $newstr\n\n";
                $strings[$key] = str_replace($m[0][$k], $newstr, $str);
            
        
    

【讨论】:

【参考方案6】:

我个人不喜欢在 PHP 中工作,也不喜欢将我的数据库凭据放在公共文件中。我创建了一个 ruby​​ 脚本来修复可以在本地运行的序列化:

https://github.com/wsizoo/wordpress-fix-serialization

上下文编辑: 我通过首先通过正则表达式识别序列化,然后重新计算包含的数据字符串的字节大小来解决序列化问题。

$content_to_fix.gsub!(/s:([0-9]+):\"((.|\n)*?)\";/) "s:#$2.bytesize:\"#$2\";"

然后我通过转义的 sql 更新查询更新指定的数据。

escaped_fix_content = client.escape($fixed_content)

query = client.query("UPDATE #$table SET #$column = '#escaped_fix_content' WHERE #$column_identifier LIKE '#$column_identifier_value'")

【讨论】:

你能总结一下方法并在这里发布一些代码吗?链接本身不是答案,它们可能会中断或删除其内容。 谢谢@brichins!对此仍然很陌生...已编辑以提供上下文。

以上是关于修复由于在文本编辑器中编辑 MySQL 数据库而损坏的序列化数据?的主要内容,如果未能解决你的问题,请参考以下文章

网站漏洞修复之vim文本编辑BUG分析与修复方案

PHP:我利用富文本编辑器Ueditor编辑了一些内容,这些内容存储在Mysql中...

在 PHP 和 Mysql 中同时更新图像和文本

2022-10-08(Discuz漏洞FCKeditor文本编辑器漏洞ZooKeeper 未授权访问Memcahe 未授权访问)

这个神器,以为是文本编辑器,其实它是 MySQL 客户端,以为是 MySQL 客户端,其实它是 Redis 客户端

如何修复:此表不包含唯一列。网格编辑、复选框、编辑、复制和删除功能不可用