当前位置: 首页>>网站问题>>正文


WordPress匹配带尾随波浪的URL

webfans 网站问题 , 去评论

问题描述

我已经收到了一份漏洞报告(1),似乎暗示Wordpress使用以下代码处理URL的方式可能存在安全问题。扫描仪似乎认为该网站可能正在提供一些目录列表等。

我很惊讶我的网站仍然在这些不同的URL上提供内容,所以我做了一个测试,安装了一个完全空白的WP实例,切换到”Post name”永久链接,并确认是的,任何添加了波形符的URL仍然被解释为没有URL波浪号。

确实,这样的网址:

https://mywordpresssite.com/my-permalink

也可以使用以下URL访问:

https://mywordpresssite.com/my-permalink~
https://mywordpresssite.com/my-permalink~/
https://mywordpresssite.com/my-permalink~~~~~~

我稍微探讨了WP解析固定链接的位置,并在parse_request方法中将其跟踪到class-wp.php,但是没有比这更进一步。

我的问题是,如果这是WP的预期行为,如果是这样,有什么办法我可以关闭它,所以代字号不匹配?为什么WP会将带有波浪号的URL解释为没有它们的URL?

(1)是的,现在我们在英国已经看到了几个主要的黑客和数据泄漏,这是”security”所有人假装他们通过交付我们的开发人员200页扫描报告充分false-positives和一般性问题,如果我们阅读并按照上述报告采取行动,他们对预期一无所知,一切都不会发生。

最佳解决方法

让我们变得简单

如果我理解OP,你的问题是包含波浪号的网址是完全匹配的。

所有其他答案都集中在以下事实:查询的清理会在执行查询之前删除某些字符,但是应该能够防止重写规则在某些情况下不匹配。

它是可行的,不是很容易,但可行。

为什么它匹配,首先?

两个网址如example.com/postnameexample.com/postname~匹配相同重写规则的原因是因为帖子的WP重写规则使用重写标记%postname%,当创建重写规则时,该重写标记被正则表达式([^/]+)替换。

问题是正则表达式([^/]+)也匹配邮政名称postname~,并且由于清理,查询的名称将是postname,结果是有效结果。

这意味着如果我们能够将正则表达式从([^/]+)更改为([^~/]+),则波形符将不再匹配,因此我们会主动阻止匹配帖子名称中包含波浪号的网址。

由于没有规则匹配,因此我认为网址最终将成为404,这应该是预期的行为。

防止匹配

add_rewrite_tag是一个函数,尽管它的名字,可用于更新现有的重写标记,如%postname%

所以,如果我们使用代码:

add_action('init', function() {
  add_rewrite_tag( '%postname%', '([^~/]+)', 'name=' );
});

我们将达到目标,example.com/postname~将不符合example.com/postname的规则。

所以,是的,上面的3行是你需要的唯一代码。

但是,在它工作之前,您需要通过访问后端的永久链接设置页面来刷新重写规则。

请注意,正则表达式([^~/]+)防止波形符在帖子名称中的任何位置,不仅作为尾随字符,而且因为帖子名称由于清理而实际上不能包含波浪号,这应该不是问题。

次佳解决方法

is intended behaviour for WP

是的,如前所述,WP_Query::get_posts()使用sanitize_title_for_query()(使用sanitize_title())来清理单个帖子的帖子名称。

简而言之,在通过sanitize_title_for_query()后,my-permalink === my-permalink~~~作为sanitize_title_for_query()去除了尾随的~~~。您可以通过执行以下操作来测试:

echo  sanitize_title_for_query( 'my-permalink~~~' )

is there any way I can switch this off so tildes are not matched

这不是你可以关掉的东西。在sanitize_title()中有一个名为sanitize_title的过滤器,您可以使用它来改变sanitize_title()的行为,但这几乎总是不是一个好主意。 SQL注入非常严重,因此,由于恶劣的卫生条件,让某些东西漏掉,可能会严重影响您网站的完整性。 “Over sanitation”有时可能是一个痛苦的屁股。

我不确定你的目标是什么,但是我怀疑你可能想要404这些尾随波浪号的单个帖子,用你的话说,“关掉它”。在这个阶段我能想到的唯一方法就是当我们有这些尾随波形时停止主查询。为此,我们可以过滤主查询的posts_where子句。

过滤器

注意:我只考虑了正常的单一帖子,而不是静态的首页或附件,您可以扩展过滤器以包含此内容

add_filter( 'posts_where', function ( $where, \WP_Query $q )
{
    // Only apply the filter on the main query
    if ( !$q->is_main_query() )
        return $where;

    // Only apply the filter on singular posts
    if ( !$q->is_singular() )
        return $where;

    // We are on a singular page, lets get the singular post name
    $name = sanitize_title_for_query( $q->query_vars['name'] );

    // Suppose $name is empty, like on ugly permalinks, lets bail and let WorPress handle it from here
    if ( !$name )
        return $where;

    // Get the single post URL
    $single_post_url = home_url( add_query_arg( [] ) );
    $parsed_url      = parse_url( $single_post_url );

    // Explode the url and return the page name from the path
    $exploded_pieces = explode( '/',  $parsed_url['path'] );
    $exploded_pieces = array_reverse( $exploded_pieces );

    // Loop through the pieces and return the part holding the pagename
    $raw_name = '';
    foreach ( $exploded_pieces as $piece ) {
        if ( false !== strpos( $piece, $name ) ) {
            $raw_name = $piece;

            break;
        }
    }

    // If $raw_name is empty, we have a serious stuff-up, lets bail and let WordPress handle this mess
    if ( !$raw_name )
        return $where;

    /**
     * All we need to do now is to match $name against $raw_name. If these two don't match,
     * we most probably have some extra crap in the post name/URL. We need to 404, even if the
     * the sanitized version of $raw_name would match $name. 
     */
    if ( $raw_name === $name )
        return $where;

    // $raw_name !== $name, lets halt the main query and 404
    $where .= " AND 0=1 ";

    // Remove the redirect_canonical action so we do not get redirected to the correct URL due to the 404
    remove_action( 'template_redirect', 'redirect_canonical' );

    return $where;
}, 10, 2 );

几点注意事项

当我们有像https://mywordpresssite.com/my-permalink~~~~~~这样的URL时,上面的过滤器将返回404页面。但是,您可以通过从过滤器中删除remove_action( 'template_redirect', 'redirect_canonical' );,让查询自动重定向到https://mywordpresssite.com/my-permalink并显示由redirect_canonical()引起的单个帖子,该帖子挂钩到template_redirect,后者处理WordPress生成的404的重定向

第三种解决方法

是的,似乎很奇怪,我们应该有相同的匹配:

example.tld/2016/03/29/test/

例如

example.tld/2016/03/29/..!!$$~~test~~!!$$../

为什么这是可能的,似乎是WP_Query::get_posts()方法的this part

if ( '' != $q['name'] ) {
    $q['name'] = sanitize_title_for_query( $q['name'] );

其中sanitize_title_for_query()定义为:

function sanitize_title_for_query( $title ) {
        return sanitize_title( $title, '', 'query' );
}

应该可以使用sanitize_title过滤器进行更严格的操作,但是根据负责此处卫生的sanitize_title_with_dashes覆盖默认输出可能不是一个好主意。如果没有关于此行为的当前状态,则应考虑创建故障单而不是更改故障单。

Update

我想知道我们是否可以使用sanitize_title_for_query()清除当前路径中的噪音并在必要时重定向到已清理的URL?

这是您可以在测试网站上玩的演示,并根据您的需求进行调整:

/**
 * DEMO: Remove noise from url and redirect to the cleaned version if needed 
 */
add_action( 'init', function( )
{
    // Only for the front-end
    if( is_admin() )
        return;

    // Get current url
    $url = home_url( add_query_arg( [] ) );

    // Let's clean the current path with sanitize_title_for_query()
    $parse = parse_url( $url );
    $parts = explode( '/',  $parse['path'] );
    $parts = array_map( 'sanitize_title_for_query', $parts );   
    $path_clean = join( '/', $parts );
    $url_clean = home_url( $path_clean );
    if( ! empty( $parse['query'] ) )
        $url_clean .= '?' . $parse['query'];

    // Only redirect if the current url is noisy
    if( $url === $url_clean )
        return;
    wp_safe_redirect( esc_url_raw( $url_clean ) );
    exit;
} );

甚至可以更好地直接使用sanitize_title_with_dashes()来避免过滤器并替换:

$parts = array_map( 'sanitize_title_for_query', $parts );

有:

foreach( $parts as &$part )
{
    $part = sanitize_title_with_dashes( $part, '', 'query' );
}

ps:我想我学会了这个技巧,从@gmazzap获取当前路径的空add_query_arg( [] ) ;-)这也是Codex中的noted。再次感谢@gmazzap提醒您在显示add_query_arg( [] )esc_url_raw()的输出时使用esc_url()。重定向它。检查以前的Codex参考。

第四种方法

让我解释一下WordPress对请求的处理,以及一种改变WordPress行为以相应地实现目标的方法。

解析请求

当WordPress收到请求时,它会启动一个解析请求并将其转换为页面的过程。当调用WordPress主查询方法WP::main()时,此过程的核心就开始了。正确识别时,此函数在parse_request()(在includes/class-wp.php中)解析查询。在那里,WordPress尝试将URL与其中一个rewrite rules匹配。匹配URL时,它会创建URL部分的查询字符串,并使用urlencode()对这些部分(两个斜杠之间的所有内容)进行编码,以防止特殊字符(如&)弄乱查询字符串。这些编码字符可能会让您认为问题存在于那里,但在解析查询字符串时它们实际上已转换为相应的”real”字符。

运行与请求关联的查询

在WordPress解析了URL之后,它会设置主查询类WP_Query,这是在WP类的相同main()方法中完成的。 WP_Query的牛肉可以在其get_posts()方法中找到,其中所有查询参数都被解析和清理,并且构建(并最终运行)实际的SQL查询。

在此方法中,在第2730行,执行以下代码:

$q['name'] = sanitize_title_for_query( $q['name'] );

这清理了从posts表中获取它的帖子。在循环内输出调试信息表明这是问题所在的位置:您的帖子名称my-permalink~被转换为my-permalink,然后用于从数据库中获取帖子。

帖子标题消毒功能

功能sanitize_title_for_query使用适当的参数调用sanitize_title,继续清理标题。现在这个函数的核心是应用sanitize_title过滤器:

$title = apply_filters( 'sanitize_title', $title, $raw_title, $context );

在本机WordPress中,此过滤器附加了一个函数:sanitize_title_with_dashes。我已经详细介绍了这个函数的功能,which can be found here。在此函数中,导致问题的行是

$title = preg_replace('/[^%a-z0-9 _-]/', '', $title);

除了字母数字字符,空格,连字符和下划线外,此行还会删除所有字符。

解决你的问题

因此,基本上有一种方法可以解决您的问题:从过滤器中删除sanitize_title_with_dashes函数并将其替换为您自己的函数。这实际上并不难,但是:

  1. 当WordPress更改清理标题的内部过程时,这将对您的网站产生重大影响。

  2. 连接到此过滤器的其他插件可能无法正确处理新功能。

  3. 最重要的是:WordPress通过以下行直接在SQL查询中使用sanitize_title函数的结果:

    $where .= " AND $wpdb->posts.post_name = '" . $q['name'] . "'";
    

    如果您考虑更改过滤器,请确保在查询中使用之前正确转义标题!

结论:就安全性而言,解决问题并不是必需的,但如果您想这样做,请将sanitize_title_with_dashes替换为您自己的功能,并注意SQL转义。

注意,所有文件名和行号都与WordPress 4.4.2文件相对应。

第五种方法

有些人已经解释了这个问题,所以我只想发布一个替代解决方案。应该是漂亮的self-explanatory。

add_action( 'template_redirect', function() {
    global $wp;

    if ( ! is_singular() || empty( $wp->query_vars['name'] ) )
        return;

    if ( $wp->query_vars['name'] != get_query_var( 'name' ) ) {
        die( wp_redirect( get_permalink(), 301 ) );
        // or 404, or 403, or whatever you want.
    }
});

你必须为分层帖子类型做一些不同的事情,因为WP_Query将通过wp_basename运行pagename然后清理它,因此query_vars['pagename']get_query_var('pagename')将不匹配儿童,因为后者将不包含父部分。

我希望redirect_canonical只是照顾这个垃圾。

参考资料

本文由朵颐IT整理自网络, 文章地址: https://duoyit.com/article/2700.html,转载请务必附带本地址声明。