【问题标题】:Authorization System Design Question授权系统设计问题
【发布时间】:2009-07-27 23:36:55
【问题描述】:

我正在尝试提出一种进行身份验证和授权的好方法。这就是我所拥有的。欢迎评论和我的期望。

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

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

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

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

是的,我的 AD 会很大,但如果我不这样做,其他东西会,无论是 MySQL(或其他一些 db)、文本文件、httpd.conf 等等。

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

使用这样的安全命名约定并将 AD 用作该存储库是否存在固有的错误?我必须保留在某个地方。为什么不是广告?

感谢cmets。

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

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

编辑:谢谢大家。很抱歉,我不能给大家更多的分数来回答。我必须选择一个。

【问题讨论】:

    标签: security active-directory ldap authorization


    【解决方案1】:

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

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

    • 使用应用程序
    • 创建用户
    • 重置OtherUserPassword
    • 查看薪资数据
    • 修改薪资数据

    或者使用角色,您可以:

    • 应用用户
    • 应用程序管理员
    • 薪资管理

    角色(可能还有每个功能的权限)可能已经映射到存储在 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 ) 需要添加。

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

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

    如果您仍然认为将单独的页面和按钮名称作为权限检查的一部分会很有用,您可以拥有页面/按钮 => 权限的全局“映射”,并通过以下方式进行所有权限查找那。一些东西(完全未经测试,大部分是伪代码):

    $permMap = array(
        "personnel_payroll" => "ViewPayroll",
        "personnel_payroll_myButton" => "EditPayroll",
        ...
    );
    
    function check_permission($elementName) {
        $permissionName = $permMap[$elementName];
        return isUserInLdapGroup($user,$permissionName);
    }
    

    【讨论】:

      【解决方案2】:

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

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

      IMO,最好一开始就避免此类问题,例如,使用组“185”而不是“finbiz”或“business-finance”,或者您可以控制更多的其他键。

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

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

      编辑:

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

      【讨论】:

        【解决方案3】:

        这样你的页面会非常慢(在我看来,每次用户导航以确定他能做什么时,你都会重新查询 AD LDAP),除非你实现了某种缓存,但随后您可能会遇到不稳定的权限问题(在您不知道的情况下撤销/添加了 AD 权限)。

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

        【讨论】:

          【解决方案4】:

          使用命名约定来保证安全性是否存在本质上的问题,例如 这个并使用 AD 作为存储库?我必须保留在某个地方。为什么不是广告?

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

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

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

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

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

          我不知道你的应用做了什么,它不使用某种数据库来存储东西。对你有好处,因为你不走捷径!这就是带有 JSON 的平面文本文件可以大有帮助的地方。

          【讨论】:

            【解决方案5】:

            您所描述的似乎是伴随身份验证的访问控制列表 (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 函数重新发明轮子。

            【讨论】:

              猜你喜欢
              • 2015-10-26
              • 2020-05-02
              • 2011-05-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-04-16
              • 2011-06-22
              相关资源
              最近更新 更多