授权系统设计问题

时间:2009-07-27 23:36:55

标签: security active-directory ldap authorization

我正在努力想出一个很好的方法来进行身份验证和授权。这就是我所拥有的。欢迎提出意见和我希望的意见。

我在mac服务器上有php。 我有用于用户帐户的Microsoft AD。

我在用户登录Intranet时使用LDAP查询AD。

我的设计问题涉及如何处理该AD信息。一位同事建议在AD中使用命名约定来避免使用中间数据库。例如,我有一个网页personnel_payroll.php。我得到了网址,并且URL和AD用户查询了群组personnel_payroll的AD。如果登录用户在该组中,则他们有权查看该页面。我必须为每个页面或至少用户域用户创建一个组以进行通用身份验证。

页面上的控件变得更加棘手。例如,假设页面或网格上有一个按钮,只有经理可以看到这个。我需要在我的AD中将personnel_payroll_myButton作为一个组。如果用户在该组中,则他们会获得该按钮。如果一个页面有几个不同级别的授权,我可以有很多组。

是的,我的AD会很大,但如果我不这样做,那么无论是MySQL(或其他一些数据库),文本文件,httpd.conf等等。

对于传递url或控件名称以及经过身份验证的用户的各种项目,我会使用通用的php函数IsAuthorized。

对于像这样的安全性使用命名约定并使用AD作为存储库,是否存在一些固有的错误?我必须保持在某个地方。为什么不AD?

感谢您的评论。

编辑:你认为这个方案会因为LDAP调用而导致超慢页面吗?

编辑:我不能成为第一个想到这一点的人。对此有任何想法都表示赞赏。

编辑:谢谢大家。对不起,我无法给你更多积分来回答。我不得不选择一个。

5 个答案:

答案 0 :(得分:4)

我想知道是否有不同的表达和存储权限的方式可以更干净,更有效地工作。

大多数应用程序分为功能区域或角色,权限是根据[广泛]区域分配的,而不是每页权限。例如,您可能拥有以下权限:

  • UseApplication
  • CREATEUSER
  • ResetOtherUserPassword
  • ViewPayrollData
  • ModifyPayrollData

或者使用角色,您可以:

  • ApplicationUser
  • ApplicationAdmin
  • PayrollAdmin

角色(可能还有每个功能的权限)可能已经映射到Active Directory中存储的数据,例如现有的AD组/角色。如果没有,它仍然比每页权限更容易维护。权限可以维护为用户组(用户在组中,因此具有权限,或者不是),或者作为自定义属性:

dn: cn=John Doe,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: webAppUser
cn: John Doe
givenName: John
...
myApplicationPermission: UseApplication
myApplicationPermission: ViewPayrollData

这样做的好处是架构更改很少。如果您使用组,AD(以及地球上的所有其他LDAP服务器)已经具有该功能,并且如果您使用这样的自定义属性,则只有一个属性(可能是objectClasswebAppUser在上面的例子中)需要添加。

接下来,您需要决定如何使用数据。一种可能性是在用户登录并在会话中将其存储在Web服务器端时检查用户的权限(找出他们所在的组或他们已授予的权限)。这存在以下问题:权限更改仅在用户登录时生效,而不是立即生效。如果您不希望权限经常更改(或者当用户同时使用系统时),这可能是一种合理的方法。这有各种变化,例如在经过一定时间后重新加载用户的权限。

另一种可能性,但更严重(负面)的性能影响是根据需要检查权限。在这种情况下,您最终会更频繁地访问AD服务器,从而导致负载增加(在Web服务器和AD服务器上),网络流量增加以及延迟/请求时间更长。但您可以确定权限始​​终是最新的。

如果您仍然认为将各个页面和按钮名称作为权限检查的一部分会很有用,那么您可以拥有page / button =>的全局“地图”。权限,并通过它执行所有权限查找。东西(完全未测试,主要是伪代码):

$permMap = array(
    "personnel_payroll" => "ViewPayroll",
    "personnel_payroll_myButton" => "EditPayroll",
    ...
);

function check_permission($elementName) {
    $permissionName = $permMap[$elementName];
    return isUserInLdapGroup($user,$permissionName);
}

答案 1 :(得分:2)

使用AD获取权限的想法没有缺陷,除非您的AD无法扩展。如果使用本地数据库会更快/更可靠/更灵活,那就使用它。

但是,使用命名约定来查找正确的安全角色非常脆弱。您将不可避免地遇到自然映射与实际映射不对应的情况。愚蠢的事情,比如你想要URL是“finbiz”,但它已经在AD中作为“商业金融” - 你复制组并让它们保持同步,或者你在你的应用程序中重新映射...?有时它就像“FinBiz”和“finbiz”一样简单。

IMO,最好避免开始出现这类问题,例如,使用“185”而不是“finbiz”或“business-finance”或其他一些你可以更多控制的密钥。

无论如何获得您的权限,如果最终必须缓存它,您将不得不处理陈旧的缓存数据。

如果你必须使用ldap,那么创建权限ou(或任何与“schema”等效的AD等价物)更有意义,这样你就可以将任意实体映射到这些权限。缓存那些数据,你应该没问题。

编辑:

部分问题似乎是避免使用中间数据库 - 为什么不将中介作为主要数据?定期将本地权限数据库同步到AD(通过挂钩或轮询),您可以避免两个重要问题:1)脆弱的命名约定,2)外部数据源关闭。

答案 2 :(得分:1)

你会以这种方式使用非常慢的页面(听起来像每次用户导航时都要重新查询AD LDAP以找出他能做什么),除非你实现某种缓存,但是您可能会遇到易失性权限问题(撤消/添加AD权限,而您不知道它)。

我保留权限并且这样保持独立,不使用AD作为存储库来管理您的应用程序特定授权。而是使用一个单独的存储提供程序,它将根据需要更容易维护和扩展。

答案 3 :(得分:1)

  

使用安全命名约定是否存在内在错误   这个并使用AD作为该存储库?我必须保持在某个地方。为什么不AD?

逻辑上,在LDAP / AD中使用组进行授权正是它的设计目的。特定用户的LDAP查询应该相当快。

实际上,AD可能无法预测数据更改在服务器之间复制需要多长时间。如果有一天你的应用程序最终在一个大型森林中,域控制器分布在整个非洲大陆,你真的会后悔把细粒度的数据放到那里。说真的,对于我曾经合作过的一些客户来说,复制这些东西可能需要一个小时。神奇的情况出现在服务器重启后神奇地开始工作的事情。

可以使用“myapp-users”,“manager”,“payroll”类型组的目录。尽量避免重复和浪费的LDAP查找。

如果您使用的是Windows,则有一种可能性是在本地磁盘上为每个授权项创建一个小文件。这为您提供了“安全对象”。然后,您的应用可以模拟用户并尝试打开该文件。这充分利用了MS多年来在优化这些方面的巨大投资。也许你可以在Mac上以某种方式做到这一点。

另外,查看Apache的mod_auth_ldap。据说它支持“可以通过使用LDAP过滤器表示策略来实现复杂的授权策略。”

我不知道你的应用程序做了什么,它没有使用某种数据库的东西。对于你没有采取简单的方法有好处!这里有一个带有JSON的平面文本文件可以有很长的路要走。

答案 4 :(得分:0)

您所描述的似乎是一个伴随身份验证的访问控制列表(ACL),因为您将超越“群组”到该群组中的特定操作。要创建一个没有与您的身份验证方法分开的数据库的ACL,我建议您查看Zend Framework for PHP,特别是ACL module

在您的AD设置中,将用户分配到群组(您提及“管理员”,您可能拥有“用户”,“管理员”,可能还有某些部门特定的群组,以及如果用户不是小组的一部分)。 Zend ACL模块允许您定义“资源”(与示例中的页面名称相关)以及这些资源中的“操作”。然后将这些作为对象保存在会话中,可以参考该对象以确定用户是否具有访问权限。例如:

<?php
$acl = new Zend_Acl();
$acl->addRole(new Zend_Acl_Role('public'));
$acl->addRole(new Zend_Acl_Role('users'));
$acl->addRole(new Zend_Acl_Role('manager'), 'users');

$acl->add(new Zend_Acl_Resource('about')); // Public 'about us' page
$acl->add(new Zend_Acl_Resource('personnel_payroll')); // Payroll page from original post

$acl->allow('users', null, 'view'); // 'users' can view all pages
$acl->allow('public', 'about', 'view'); // 'public' can only view about page
$acl->allow('managers', 'personnel_payroll', 'edit'); // 'managers' can edit the personnel_payroll page, and can view it since they inherit permissions of the 'users' group

// Query the ACL:
echo $acl->isAllowed('public', 'personnel_payroll', 'view') ? "allowed" : "denied"; // denied
echo $acl->isAllowed('users', 'personnel_payroll', 'view') ? "allowed" : "denied"; // allowed
echo $acl->isAllowed('users', 'personnel_payroll', 'edit') ? "allowed" : "denied"; // denied
echo $acl->isAllowed('managers', 'personnel_payroll', 'edit') ? "allowed" : "denied"; // allowed
?>

将ACL与AD分离的好处是,随着代码的更改(以及各个区域内可能的“操作”),授予对它们的访问权限位于同一位置,而不是必须管理AD服务器进行更改。并且您正在使用现有的(稳定的)框架,因此您不必使用自己的isAuthorized函数重新发明轮子。