【信息系统安全】RBAC访问控制实验

Posted by 慕念 on December 5, 2021

【实验目的】

​ 访问控制是信息系统安全的重要组成部分。基于角色的访问控制(RBAC)方法通过设置权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限,从而极大地简化了权限的管理并提高了灵活性。通过本次实验,理解和掌握RBAC的原理及设计方法,掌握RBAC中对于主体、角色、访问权限的设计与配置。

【实验内容】

​ 使用RBAC方法,完成针对文件的访问控制管理。需要根据不同用户所充当角色的权限进行相应的访问控制。访问控制操作包括新建、删除、读取、更改、执行等,可以根据自己系统设计需求自行进行定义。

​ 实验要求完成基本的访问控制功能具有:1)角色分级和继承2)角色之间具有约束关系(角色互斥)。也可根据个人需要自行增加特性。

【实验过程】

一、系统设计方法

0、思路

RBAC(Role-Based Access Control),即基于角色的权限控制,通过角色关联用户,角色关联权限的方式间接赋予用户权限。

如图所示,需要定义用户表、角色表、权限表、用户角色关联表和角色权限关联表。

image-20211204162646157

模型设置的背景用的是实验内容中举的例子:

系统中有某个文件夹存有公司的重要生产文件。公司员工A,B可以对其中的文件具有读取,执行的权限;他们的部门经理M1具有读取、删除、执行的权限;公司员工C、D具有这些文件的新建、读取、更改的权限;他们的经理M2具有新建、删除、读取、更改的权限;公司的大boss拥有所有权限。

整个系统存在2种模式,分别是管理员模式用户模式,管理员模式下可以对系统中的增加、删除用户,也可以对用户对应的角色进行增加、删除和修改。

并且在实验的模型中,有以下几条规则:

  • 每个用户都至少有一个角色,每个角色都至少有一个权限

  • 角色间有继承关系,即角色上有了上下级的区别,具体继承关系可以看下图

  • 存在以下互斥条件的约束

    • 基数约束
      • 用户可拥有的角色数目受限:一个用户最多拥有2个角色
      • 角色被分配的用户数量受限:系统中Boss角色只能由1位用户拥有
    • 互斥角色
      • 对于部分角色一个用户在某一次活动中只能被分配其中的一个角色,不能同时获得两个角色的使用权。
      • 体现在这次实验中,只允许一个用户同时是Employee A和Employee B,其他任意两组角色组合都会产生互斥。
      • (实际上RBAC2中的互斥角色应该指各自权限互相制约的两个角色,比如审计活动中的会计和审计员,但是因为实验中的模型较为简单,互斥就体现在上下级角色中。)
    • 先决条件角色约束 :指要想获得较高的权限,要首先拥有低一级的权限。比如说要想把角色修改为Manager A,必须要求用户之前有角色Employee A
    • 运行时互斥 :当一个用户具有两个角色时,在操作中只能激活一个角色

1、初始化用户、角色和权限

首先定义用户表、角色表和权限表:

# 用户表

# 这里为了方便测试,多增加了一个用户a,同时拥有Employee_A和Employee_B的角色

Users = ['A', 'B', 'M1', 'C', 'D', 'M2', 'boss', 'a']

# 角色表

Roles = ["Employee_A", "Manager_A", "Employee_B", "Manager_B", "Boss"]

# 权限表(因为权限不会变动,所以用元组表示)

# 分别对应用户的5种操作:new,delete,read,edit,execute

User_Permissions = ('1', '2', '3', '4', '5')

再利用嵌套列表定义用户-角色关联表,由于每位用户至少一个角色,最多两个角色,所以role1默认有值:

# 用户-角色关联表

# 一个用户最多两个角色,如果只有一个角色,则第二个角色为None

Users_Roles = [['A', "Employee_A", None], ['B', "Employee_A", None], ['M1', "Manager_A", None],
               ['C', "Employee_B", None], ['D', "Employee_B", None], ['M2', "Manager_B", None],
               ['boss', "Boss", None], ['a', "Employee_A", "Employee_B"]]

再定义角色-权限关联表,同样是嵌套列表。这里为了体现角色的分级和继承,先定义Boss,再从Boss的基础上删减权限:

# 角色-权限关联

# 在RBAC中,角色是权限的一个集合,子角色可以继承父角色的所有权限,并可在此基础上做删减

# Boss具有所有权限,是根节点

Boss = list(User_Permissions)

# Manager_A和Manager_B都继承Boss

Manager_A = copy.copy(Boss)
Manager_A.remove('1')
Manager_A.remove('4')

Manager_B = copy.copy(Boss)
Manager_B.remove('5')

# Employee_A继承Manager_A

Employee_A = copy.copy(Manager_A)
Employee_A.remove('2')

# Employee_B继承Manager_B

Employee_B = copy.copy(Manager_B)
Employee_B.remove('2')

# 角色-权限表

Roles_Permissions = [Employee_A, Manager_A, Employee_B, Manager_B, Boss]

2、管理员模式

首先进入系统,输入用户名,如果为admin则进入管理员模式,调用admin_action()

if __name__ == "__main__":
    while 1:
        print("====================================")
        print("Welcome to the RBAC system!")
        name = input(">>Enter your name: ")
        # 说明是管理员登录,动态配置用户-角色
        
        if name == "admin":
            admin_action()

进入admin_action()后,会显示admin的操作菜单,共有6种操作:增加用户、删除用户、增加用户角色、删除用户角色、修改用户角色和退出。选择对应的选项进入相应的函数,如果输入了其他内容,则判断输入的内容不合法,重新显示菜单输入选项。

如果输入f,则管理员退出登录,重新回到进入系统的界面。

def admin_menu():
    print("================================")
    print(">>admin's menu:")
    print("a. Add User")
    print("b. Delete User")
    print("c. Add User's Role")
    print("d. Delete User's Role")
    print("e. Edit User's Role")
    print("f. Log out")

    while 1:
        admin_menu()
        option = input(">>Hello,admin! Enter your option: ")
        if option == 'f':
            print(">>Goodbye,admin!\n")
            break

        name = input(">>Please input the user's name:")
        # 新建用户
        
        # 只有新建用户要求输入的用户名不存在,其他的操作都要求用户名合法,所以单独处理
        
        if option == 'a':
            add_user(name)
            continue

        try:
            i = Users.index(name)
        except:
            # 进入异常处理说明找不到该用户,用户名不合法
            
            print("× The user " + name + " doesn't exist.")
            continue
        print("√ The user is found.")

        # 删除用户
        
        if option == 'b':
            delete_user(name)

        # 增加用户角色
        
        elif option == 'c':
            add_user_role(name, i)

        # 减少用户角色
        
        elif option == 'd':
            delete_user_role(name, i)

        # 修改用户角色
        
        elif option == 'e':
            edit_user_role(name, i)

        else:
            print("× Sorry,your operation is invalid.\n")

运行截图:

image-20211205003148132

image-20211205011316718

接下来依次来看管理员的处理操作:

①增加用户add_user

首先在用户表中用输入的用户名进行搜索,如果该用户已存在,则无法新建,直接返回。

然后要求输入新用户的角色,并检查输入是否合法,如果两个角色输入都合法,则分配角色。

def add_user(new_name):
    # 在用户表中搜索该用户
    
    num = Users.count(new_name)
    if num > 0:  # 说明已有该用户
        
        print("× The user " + new_name + " already exists.")
        return
    else:
        print("√ The user name can be used.")
        # 输入角色
        
        print(">>Roles in the system: Employee_A, Manager_A, Employee_B, Manager_B, Boss")
        new_role1 = int(input(">>Please choose " + new_name + "'s role (input from 1~5):"))
        new_role2 = int(input(">>Another role: (input from 0~5, 0 means None):"))
        # 检查输入是否合法
        
        if 1 <= new_role1 <= 5 and 0 <= new_role2 <= 5:
            role_1 = Roles[new_role1 - 1]
            print("√ " + new_name + "'s role1: " + role_1)
            if new_role2 == 0:
                role_2 = None
                print("√ " + new_name + " only has one role.")
            else:
                role_2 = Roles[new_role2 - 1]
                print("√ " + new_name + "'s role2: " + role_2)
        else:
            print("× Sorry,your input is invalid.\n")
            return

由于规则的设定,这里还需要通过check_role_mutexcheck_role_number检查互斥,如果不存在互斥则在用户表和用户-角色关联表中都增加新用户,否则报错:

        # 检查互斥
    
        if check_role_mutex(role_1, role_2) and check_role_number(role_1):
            # 在Users中增加用户
            
            Users.append(new_name)
            # 在Users_Roles中增加用户和角色的关联
            
            Users_Roles.append([new_name, role_1, role_2])
            print("√ User " + new_name + " is added successfully.")
            print(Users)
            print(Users_Roles)
        else:
            print("× Sorry,the roles are mutually exclusive.\n")

​ 详细来看两个check函数:

check_role_mutex()用来检查角色互斥
# 检查角色互斥

# 一个用户同时担任员工&经理,员工&boss,经理&boss,经理A&经理B是非法的

# 即如果一个用户拥有两个角色,只能是员工A和员工B

# 这里也顺便避免了role1和role2产生重复

def check_role_mutex(role_1, role_2):
    # role2 is None说明只有一个角色,不存在角色互斥问题
    
    if role_2 is None or (role_1 == "Employee_A" and role_2 == "Employee_B") or (
            role_1 == "Employee_B" and role_2 == "Employee_A"):
        return True
    else:
        return False
check_role_number()用来检查基数约束
# 基数约束

# Boss只能有一个人,其他角色无人数约束

# 而且由于会先检查角色互斥,只可能role1=boss,role2=None,所以只检查role1

def check_role_number(role_1):
    if role_1 == "Boss":
        # 检查当前是否存在Boss
        
        for i in range(len(Users_Roles)):
            for j in range(len(Users_Roles[i])):
                if Users_Roles[i][j] == "Boss":
                    return False
    return True

运行截图:

image-20211205004745581

image-20211205004903624

image-20211205005054413

②删除用户delete_user

因为不需要检查互斥,删除用户的操作比较简单,分别从用户表Users和用户-角色关联表Users_Roles中删除对应用户即可。

从列表中删除对应值需要用remove(),删除对应索引则用pop()

def delete_user(delete_name):
    # 从Users删除用户
    
    Users.remove(delete_name)
    # 从Users_Roles中删除用户
    
    print(">>Deleting...")
    # 记录下用户在Users_Roles中的索引
    
    for i in range(len(Users_Roles)):
        if Users_Roles[i][0] == delete_name:
            del_i = i
    # 删除
    
    Users_Roles.pop(del_i)
    print("√ User " + delete_name + " is deleted successfully.\n")
    print(Users)
    print(Users_Roles)

运行截图:

image-20211205005937301

③增加用户的角色add_user_role

函数的参数是用户名和在用户表中的索引,在整个系统中:用户表中的索引=用户-角色关联表中的索引。

如果该用户已经存在两个角色,则不能再增加角色,直接返回;如果只有一个角色,需要检查要增加的role2与现有角色role1的冲突。

def add_user_role(add_name, index):
    # 如果该用户已经存在两个角色,不能再增加角色
    
    if Users_Roles[index][2] is not None:
        print("× The user " + add_name + " already has two roles. Add Failed.\n")
        return
    
    # 该用户只有role1时,询问要增加哪个角色
    
    print(">>" + add_name + " already has Role: " + Users_Roles[index][1])
    print(">>Roles in the system: Employee_A, Manager_A, Employee_B, Manager_B, Boss")
    i = int(input(">>Which one to add? (input a number from 1~5)\n>>"))

    # 检查角色互斥
    
    # 这里不需要检查基数互斥,因为这里隐含的规则是角色Boss只能出现在role1
    
    # 因为不可能用户在有role1的情况下增加Boss角色,违反角色互斥
    
    if check_role_mutex(Users_Roles[index][1], Roles[i - 1]):
        Users_Roles[index][2] = Roles[i - 1]
        print("√ User " + add_name + "'s new role is added successfully.\n")
        print(Users_Roles)
    else:
        print("× Sorry,the roles are mutually exclusive.\n")

运行截图:

image-20211205010856363

image-20211205011032816

image-20211205011124058

④删除用户的角色delete_user_role

同样,函数的参数是用户名和在用户表中的索引。

通过该用户找到对应的角色,如果该用户只有一个角色,无法删除,因为每个用户至少拥有一个角色;如果用户有两个角色,询问要删除哪一个,需要注意的是:删除一个角色后要补上None,维护用户-角色关联表。

def delete_user_role(delete_name, index):
    # 如果该用户只有一个角色,无法删除
    
    if Users_Roles[index][2] is None:
        print("× The user " + delete_name + " only has one role. Delete Failed.\n")
        return
    # 如果有两个角色,询问要删除哪一个
    
    print(">>The user has two roles, which one to delete?")
    i = int(input(">>" + Users_Roles[index][1] + " or " + Users_Roles[index][2] + "?(input 1 or 2)\n>>"))
    Users_Roles[index].pop(i)
    # 因为删掉了一个,要补上一个NULL
    
    Users_Roles[index].append(None)
    print(Users_Roles)
    print("√ User " + delete_name + "'s role is deleted successfully.\n")

运行截图:

image-20211205011810230

image-20211205011906560

⑤修改用户的角色edit_user_role

同样,函数的参数是用户名和在用户表中的索引。

如果用户只有一个角色,那只能修改role1;如果用户有两个角色,询问要修改哪一个。根据获得的i,记录当前要被替换的cur_role和该用户的另一个角色another_role,并选择要修改成的角色edit_role。这里需要检查先决条件角色约束基数约束互斥角色,只有满足这三个约束条件才能成功修改角色:

def edit_user_role(edit_name, index):
    # 如果该用户只有一个角色role1,只要修改role1
    
    if Users_Roles[index][2] is None:
        print(">>The only role " + edit_name + " has: " + Users_Roles[index][1])
        i = 1

    # 如果有两个角色,询问要修改哪一个
    
    else:
        print(">>The user has two roles, which one to delete?")
        i = int(input(">>" + Users_Roles[index][1] + " or " + Users_Roles[index][2] + "?(input 1 or 2)\n>>"))

    cur_role = Users_Roles[index][i]
    if i == 1:
        another_role = Users_Roles[index][2]
    elif i == 2:
        another_role = Users_Roles[index][1]

    print(">>Roles in the system: Employee_A, Manager_A, Employee_B, Manager_B, Boss")
    edit_i = int(input(">>Choose which role? (input a number from 1~5)\n>>"))
    edit_role = Roles[edit_i - 1]
    if check_role_mutex(edit_role, another_role) and check_role_number(edit_role) and (
            check_prerequisite(cur_role, edit_role) or check_prerequisite(another_role, edit_role)):
        Users_Roles[index][i] = edit_role
        print("√ Edit role successfully.\n")
        print(Users_Roles)
    else:
        print("× The Operation is invalid.")
check_prerequisite()检查先决条件角色

​ 具体来说,只有当目前的角色是Employee_A/B,要修改的角色是Manager_A/B,或者目前角色是Manager_A/B,要修改的条件是Boss时,先决条件角色才成立。

# 检查先决条件

def check_prerequisite(cur_role, edit_role):
    if edit_role == "Boss" and (cur_role == "Manager_B" or cur_role == "Manager_A"):
        return True
    elif (edit_role == "Manager_A" and cur_role != "Employee_A") or (
            edit_role == "Manager_B" and cur_role != "Employee_B"):
        return False
    else:
        return True

运行截图:

image-20211205013417399

image-20211205013520627

image-20211205013616139

image-20211205013710491

3、用户模式

当用户进入系统后,需要输入用户名,在用户表中查找该用户,如果找不到说明输入错误,重新回到一开始的系统。

如果输入的用户名合法,则根据索引取出对应的角色,如果该用户有两个角色,由于存在运行时互斥,只允许用户选择一个角色用以后续的操作。

if __name__ == "__main__":
    while 1:
        print("====================================")
        print("Welcome to the RBAC system!")
        name = input(">>Enter your name: ")
        # 说明是管理员登录,动态配置用户-角色
        
        if name == "admin":
            admin_action()
        else:
            # 查找该用户,如果找不到会产生异常,说明该用户不合法
            
            try:
                index = Users.index(name)
            except:
                print("× The user is invalid.\n× Please retry!\n")
                continue

            # role1不可能是None,只需要比较role2
            
            # role2 is None说明只有一个角色
            
            if Users_Roles[index][2] is None:
                role = Users_Roles[index][1]
            else:
                # role2不是None,说明有两个角色
                
                # 运行时互斥
                
                # 允许一个用户具有两个角色的成员资格,但在运行中不可同时激活这两个角色
                
                i = int(input(
                    ">>Which capacity to operate?\n>>" + Users_Roles[index][1] + " or " + Users_Roles[index][2] + "? (input 1 or 2)\n>>"))
                try:
                    role = Users_Roles[index][i]
                except:
                    # 说明输入非法
                    
                    print("× Sorry,the input is invalid.\n× Please retry!\n")
                    continue

当用户成功登陆后,显示用户菜单,然后根据用户-角色关联表中取出的role到角色-权限关联表中找到该角色详细的权限,然后检查用户的操作是否有权限,如果有则成功执行,如果没有则报错。

            print("\n√ " + name + " Log in successfully.")
            while 1:
                user_menu()
                option = input(">>Enter your option: ")
                if option == '6':
                    print(">>Goodbye," + name + "!\n")
                    break

                # role是用户-角色关联表中取出的角色
                
                # 需要到角色-权限关联表中找到该角色详细的权限
                
                for i in range(len(Roles_Permissions)):
                    if Roles[i] == role:
                        role = Roles_Permissions[i]
                        break

                # 检查用户的操作是否有权限
                if option in role:
                    user_execute(option)
                else:
                    print("× Sorry,the operation is invalid.\n× Please retry!\n")

用户菜单:

def user_menu():
    print(">>User's menu:")
    print("1. New file")
    print("2. Delete file")
    print("3. Read file")
    print("4. Edit file")
    print("5. Execute file")
    print("6. Log out")

如果用户的操作有权限,则执行相应操作:

def user_execute(option):
    if option == '1':
        open('a_new_file.txt', 'w')
        print("√ New a file (a_new_file.txt) successfully.")
    elif option == '2':
        os.remove('a_file_to_remove.txt')
        print('√ Delete a file (a_file_to_remove.txt) successfully.')
    elif option == '3':
        f = open('read_me.txt', 'r')
        file_context = f.read()
        print("√ Read a file (read_me.txt).")
        print(">>File context: " + file_context)
        f.close()
    elif option == '4':
        f = open('edit_me.txt', 'w')
        f.write("I am writing the file.")
        print("√ Edit a file.")
        f.close()
    elif option == '5':
        os.system('python hello_world.py')
        print("√ Execute a file (hello_world.py) successfully.\n")

运行截图:

image-20211205020146165

image-20211205020232916

2、对RBAC模型及设计的理解

RBAC的基本思想是通过将权限授予角色而不是直接授予主体,主体通过角色分派来得到客体操作权限从而实现授权。由于角色在系统中具有相对于主体的稳定性,并具有更为直观的理解,实现了用户和权限的逻辑分离,便于管理权限,大大减少系统安全管理员的工作复杂性和工作量。

相较于直接给用户分配权限,RBAC简化了用户和权限的关系,扩展性强、易于维护,更适合用户数量、角色类型多的平台。

RBAC支持3个安全原则:最小权限原则、责任分离原则和数据抽象原则。最小权限原则是指将角色配置成其完成任务所需的最小权限集合;责任分离原则是指可以通过调用相互独立互斥的角色来共同完成敏感的任务;数据抽象原则是指可以通过权限的抽象来体现一些抽象权限。在实验中主要体现了最小权限原则,后两者没有明显地体现。

但是RBAC模型还是存在缺点的,比如说:没有提供操作顺序的控制机制,所以很难适应对操作次序有严格要求的系统