网站安全正则表达式处理程序? (PHP)

时间:2012-09-22 03:24:13

标签: php session mod-rewrite redirect login

我工作的公司要求我带一个他们的网站并将其作为另一个网站的子域。然后,他们让我将“登录/注销”会话控制从他们的主域扩展到他们的子域。

完成此操作后,我发现存在控制/管理问题。由于它们具有大量的单个页面,并且由于它们具有广泛的目录结构,因此它们太过涉及为每个页面添加PHP片段以根据登录状态重定向。

这是我的解决方案..请告诉我任何有助于我的问题或其他任何问题。

  1. 我将使用Mod_rewrite将subdomin上的每个请求重定向到特定页面( handler.php?requested_url = )。
  2. 我打算在他们的网站上制作“网站允许/禁止规则”部分。此部分将包含一个文本框,其中包含以下规则:

     +/weather/            ---> will allow anyone access to any url that contains "/weather/" somewhere within it, irregardless of logged-in status.
    
     -/weather/premium/    ---> will only allow access to a url that contains /weather/premium to logged-in users. 
    

    这将输出到存储在文件 rules.php 中的数组,如下所示:

    $ruleList = array(); 
    $ruleList[] = '+/weather/'; 
    $ruleList[] = '-/weather/premium/';
    
  3. handler.php 中,如果用户已登录,我会将其转发到 requested.url 。如果用户没有登录,那么我将首先假设每个页面仅限于未登录的用户。 handler.php 将解析 requested_url 并针对 rules.php 进行检查,以查看是否设置了任何显式权限。然后,如果规则允许非登录访问,它会将用户转发到 requested_url ,否则会将用户发送到登录页面。

  4. 我可以立即看到的一个问题是,假设Mod_rewrite规则会将每个请求发送到 handler.php ,我该如何避免无限循环?

    重定向是否应该通过header("Location: ")以外的某种方法完成?

    编辑:这是我的斗争的更新:

    在顶级域名(example.com)的 .htaccess 文件中,我添加了:

        #Prevent catching requests for the sub1 subdomain
        RewriteCond %{REQUEST_URI} ^sub1\.example\.com
        RewriteRule .* – [L]
    

    然后,在 sub1.example.com 子域的 .htaccess 内,我添加了以下内容:

        IndexIgnore *
    
        RewriteEngine On
        RewriteBase /path/to/base
    
        #Avoid infinite loop on outgoing requests
        RewriteCond %{HTTP_REFERER} !^$
        RewriteCond %{REQUEST_URI} !^$
        RewriteCond %{HTTP_REFERER} !^/?handler.php?$
        RewriteCond %{REQUEST_URI} !^/?handler.php?$
    
    
    
            #Check for cookie. Redirect to handler if not found.  (not yet implemented)                               
            #RewriteCond %{HTTP_COOKIE} !session_id
        RewriteRule (.*)$ handler.php?requested_url=$1 [NC,L,QSA]
    

    这是 handler.php

        <?php
    
            $url = $_REQUEST['requested_url'];
    
            //Check list of permissions. For now just assume permitted.
            $permitted = true;
            if ($url == "") $url = "http://sub1.example.com";   
            if ($permitted)
                header("Location: ".$url);
            header("Location: http://sub1.example.com");        
    
        ?>
    

    我很亲密,我可以品尝它。不幸的是暂时我几乎到处都有“重定向循环”。如果有人能给我一个正确方向的推动,我会很感激!

4 个答案:

答案 0 :(得分:3)

只是一个想法,但也许你不需要与mod_rewrite斗争。如果你想处理PHP中的所有内容,为什么不在你的VHOST中添加一个前置文件?

php_value auto_prepend_file handler.php

它将包含在任何PHP脚本之前,您可以根据需要重定向。

答案 1 :(得分:2)

您是否有理由不想使用apache auth?我认为这样会复杂得多。 http://httpd.apache.org/docs/2.2/howto/auth.html

您可以逐个目录地在虚拟主机中指定访问规则,您的用户信息可以位于平面文件或数据库中。

答案 2 :(得分:2)

我认为你的想法有一个循环,因此应用程序中的循环。像这样:

  1. 用户代理请求子域中的旧资源。

  2. 由于UA必须具有有效凭据且旧资源无法验证凭据,因此UA会重定向到处理程序。

  3. 处理程序验证凭据,并将UA重定向回旧资源。

  4. 转到2.

  5. 问题是所请求的资源无法区分步骤1和3中的UA请求,因为区别是由UA的凭据定义的,遗留资源不会对其进行评估。

    从另一个角度来看,这种矛盾也是显而易见的。想象一下你解决循环并将UA重定向到遗留资源 - 例如http://sub1.example.com/foo.php。对于服务器返回资源,它必须意味着没有评估凭证(因为它没有这样做),因此资源实际上是公开的。

    要解决这个问题,你必须通过改变第2步或第3步的规则来打破僵局:

    • 更改步骤2,将凭据评估添加到旧资源的响应中。之前的回答表明auto_prepend_file()正朝着这个方向前进,但仅针对PHP遗留文件 - 图像,HTML等运气不佳。

    • 更改步骤3,找到一种方法来传递遗留资源,而无需UA直接请求资源。一种可能性是让处理程序从文件系统获取资源,并使用readfile()和一些HTTP标头管理将其放在线路上。

    也许这些组合可以帮助您:auto_prepend_file()将身份验证处理应用于旧PHP,readfile()用于非PHP内容。

答案 3 :(得分:1)

您只需要一个简单的验证功能。允许用户访问资源。首先设置上下文:

$rules          = rules_load();
$uri            = $_SERVER['QUERY_STRING'];
$userIsLoggedIn = user_is_logged_in();

接下来我们有了上述验证函数,默认情况下应返回false并将上下文作为参数:

$validation = function (array $rules, $uri, $userIsLoggedIn) {
    $permitted = false;    
    return $permitted;
};

然后逻辑是直截了当的:

if ($validation($rules, $uri, $userIsLoggedIn)) {
    # can pass
    echo "can pass";
} else {
    # login first
    echo "login first";
}

自然已经为您提供“先登录”。精细。我们很快就会更改该功能的参数。让我们看看$rules$uri如何相互支持。

每个规则都可以匹配URI,至少路径可以匹配。让我们分开规则:

 <sign><path>

 sign    := [+-]
 path    := <segment>*/
 segment := /[a-z]+

规则与$uri匹配或不匹配。如果匹配,则符号决定这意味着什么。

实际上, sign 指定了两个组,可以说$uri是否匹配。这又是一个简单的函数,首先是符号过滤,然后按路径过滤。

考虑一个带有三个输入参数的函数,它以数组的形式返回与uri匹配的所有规则的子集:

function ($sign) use ($rules, $uri) {
    return array_reduce($rules, function ($a, $v) use ($rules, $sign, $uri) {
        $v[0] === $sign && false !== strpos($uri, substr($v, 1)) && $a[] = $v;
        return $a;
    }, array());
};

这或多或少是对array_reduce的调用。因此,我们假设这将与名为$match的变量相关联,然后可以将$rules$uri替换为$validation函数的参数。

$validation = function ($match, $userIsLoggedIn) {

所以剩下的部分就是现在只是制定验证条件:

    $permitted = $userIsLoggedIn;

默认情况下,如果用户已登录,则授予权限。仅当用户未登录时,如果 - 组不匹配且+组匹配,我们可以授予权限。出于安全原因, - group应首先覆盖任何+规则:

    $permitted = $userIsLoggedIn ?: !$match('-') && $match('+');

其余的是返回该状态:

    return $permitted;
};

由于这个功能相当简单,我们可以发射它。代码现已完整:

$rules          = rules_load();
$uri            = $_SERVER['QUERY_STRING'];
$userIsLoggedIn = user_is_logged_in();

$match = rules_match($rules, $uri);

$permitted = $userIsLoggedIn ?: !$match('-') && $match('+');

if ($permitted) {
    # can pass
    echo "can pass";
} else {
    # login first
    echo "login first";
}

/**
 */
function rules_match(array $rules, $uri) {
    return function ($sign) use ($rules, $uri) {
        return array_reduce($rules, function ($a, $v) use ($rules, $sign, $uri) {
            $v[0] === $sign && false !== strpos($uri, substr($v, 1)) && $a[] = $v;
            return $a;
        }, array());
    };
}