DigitalPlatform / dp2

Integrated Library System / 图书馆集成系统
http://digitalplatform.github.io/dp2
Apache License 2.0
106 stars 54 forks source link

dp2library 工作人员和读者密码安全性增强 #839

Open DigitalPlatform opened 3 years ago

DigitalPlatform commented 3 years ago

最新版 dp2library 对工作人员账户和读者记录的密码进行了安全性增强。

包括如下方面: 1) 为工作人员账户增加密码失效期机制; 2) 为工作人员账户增加密码健壮性检查机制; 3) 为读者记录增加密码失效期机制; 4) 为读者记录增加密码健壮性检查机制;

DigitalPlatform commented 3 years ago

工作人员账户的增强

在 library.xml 文件中根元素下的 accounts 元素中,可以通过 passwordExpireLength 属性定义工作人员账户的密码失效期长度,用法为

    <accounts passwordExpireLength="90days">

若 passwordExpireLength 属性缺省,表示不使用密码失效期,也就是说工作人员的密码永远不会失效。

accounts 元素的 passwordStyle 属性,用来定义工作人员密码的健壮性规则。目前可用 "style-1" 这一种规则,用法如下:

    <accounts passwordStyle="style-1">

若 passwordStyle 属性缺省,表示不对工作人员密码进行健壮性检查,任何形态的密码都会被接纳。

(2021/7/16 更新) passwordStyle 属性值还可以这样 "style-1,login",表示工作人员身份登录的时候,会自动检查密码健壮性,如果不符合健壮性要求,登录会失败,返回报错信息说需要修改密码然后重新登录。(注:reader,public,opac 这几个特殊账户不进行密码健壮性检查)

library.xml 中 account 元素 password 属性改为 password 元素

为了容纳密码失效期等信息,以前版本的 library.xml 中 account 元素的 password 属性,会被自动升级修改为 password 元素。只要 dp2library 最新版升级,它启动后会去自动修改 password 属性为下级元素。

每当 accounts 元素 passwordExpireLength 属性修改后,dp2library 重新启动时,会自动为 account 元素添加或者去除失效期信息(password/@expire 属性)

工作人员本人修改密码,只调用 ChangeUserPassword() API

而早先的版本要求两步:第一步先用 Login() API,用旧密码登录;然后用 ChangeUserPassword() API 修改密码。

测试建议

1) 最新版 dp2library 启动后会自动把 account 元素的 password 属性改为元素,请观察验证; 2) 在 library.xml 中没有为 accounts 元素配置两个属性的情况下,验证原有的功能是否正常。原有的功能是没有失效期的,也不会对密码进行健壮性检查; 3) 在 library.xml 中为 accounts 元素配置 passwordStyle 属性,然后创建、修改工作人员账户,和修改工作人员密码(包括本人修改和他人强制修改两种)观察是否对密码进行了健壮性检查; 4) 用内务的修改密码窗,用工作人员本人修改密码的方式进行修改,故意把原有密码输入错误。这样操作会报错。连续报错 10 次以后,这个用户会被加入黑名单; 5) 在 library.xml 中为 accounts 元素配置 passwordExpireLength 属性,然后创建工作人员账户,或者修改工作人员账户,检查 library.xml 中的对应 account/password 元素中是否自动被添加了 expire 属性,其值是否正确。另外,制造当前时间超过了失效期的情况,观察登录是否会报错说“密码已经失效” 6) 当工作人员密码已经失效的时候,最新版内务刚启动的时候,登录过程会报错说“密码已经失效”,然后会自动弹出一个修改密码的对话框。检查这个对话框是否可以正确工作。

DigitalPlatform commented 3 years ago

读者记录的增强

在 library.xml 文件中根元素下的 login 元素中,可以通过 patronPasswordExpireLength 属性定义读者记录的密码失效期长度,用法为

    <login patronPasswordExpireLength="90days">

若 patronPasswordExpireLength 属性缺省,表示不使用密码失效期,也就是说读者记录的密码永远不会失效。

login 元素的 patronPasswordStyle 属性,用来定义读者记录的密码的健壮性规则。目前可用 "style-1" 这一种规则,用法如下:

    <login patronPasswordStyle="style-1">

若 patronPasswordStyle 属性缺省,表示不对读者密码进行健壮性检查,任何形态的密码都会被接纳。

(2021/7/16 更新) patronPasswordStyle 属性值还可以这样 "style-1,login",表示读者身份登录的时候,会自动检查密码健壮性,如果不符合健壮性要求,登录会失败,返回报错信息说需要修改密码然后重新登录。

一些说明

读者记录中原本就是用 password 元素来存储密码

这一点和最新版的工作人员的密码存储 XML 结构相似。

当 login 元素 patronPasswordExpireLength 属性修改后,dp2library 重新启动时,并不会自动为读者记录添加或者去除失效期信息(password/@expire 属性)

这一点要引起注意。当配置参数修改以后,只有当密码被修改的时候,才会按照新的参数来执行。

读者本人修改密码,(不需要先调用 Login() API 登录)直接调用 ChangeReaderPassword() API

而早先的版本要求两步:第一步先用 Login() API,用旧密码登录;然后用 ChangeReaderPassword() API 修改密码。

内务前端修改密码窗做了增强

增加了“读者找回密码”属性页。有两个按钮,第一个按钮是正常的功能,找回的密码直接(以短信方式)发送到读者手机;第二个按钮主要是给程序员调试时候使用,找回的密码(和相关信息)会以一个对话框直接显示在界面上,请慎重使用。

使用得当的情况下,第二个按钮不会有安全性问题,因为这个功能需要当前用户的权限中具有 resetpasswordreturnmessage 这个特殊权限,才被 dp2library 允许操作。这个权限只应给高级管理员的账户配置,不要给一般工作人员账户配置。

内务的登录对话框做了增强

在工具条上增加了一个“改密码”按钮,方便(账户所有者在)登录前修改账户密码。

测试建议

1) 在 library.xml 中没有为 login 元素配置两个属性的情况下,验证原有的功能是否正常。原有的功能是没有失效期的,也不会对密码进行健壮性检查; 2) 在 library.xml 中为 login 元素配置 patronPasswordStyle 属性,然后创建、修改读者记录,和修改读者密码(包括本人修改和工作人员来强制修改两种)观察是否对密码进行了健壮性检查; 3) 用内务的修改密码窗,用读者本人修改密码的方式进行修改,故意把原有密码输入错误。这样操作会报错。连续报错 10 次以后,这个读者会被加入黑名单; 4) 在 library.xml 中为 login 元素配置 patronPasswordExpireLength 属性,然后读者记录,或者修改读者记录,检查读者记录中的对应 account/password 元素中是否自动被添加了 expire 属性,其值是否正确。(注意,在内务读者窗里面无法看到 password 元素,这是因为 dp2library 返回读者记录前过滤了这个元素。要用 dp2rms 前端来进行观察) 另外,制造当前时间超过了失效期的情况,观察读者身份登录是否会报错说“密码已经失效” 5) 当读者密码已经失效的时候,最新版内务刚启动的时候,(用读者身份)登录过程会报错说“密码已经失效”,然后会自动弹出一个修改密码的对话框。检查这个对话框是否可以正确工作。 6) 当读者密码已经失效的时候,用 dp2OPAC 以读者身份登录,登录过程应该报错说“密码已经失效”。

DigitalPlatform commented 3 years ago

一些关于测试的思路和建议

1) 用内务前端以读者身份登录测试读者密码失效的情况,注意每次都不要去修改密码就直接退出。然后多次反复这样登录。每次都应该登录不成功才对。

2) 用内务前端以工作人员身份登录进入后,修改密码窗的第一个属性页是以工作人员身份修改读者密码,此时旧密码 textbox 被禁用。而用内务前端以读者身份登录进入后,修改密码窗的第一个属性页是以读者身份修改读者密码,此时旧密码 textbox 可用。软件这里功能实现有些隐蔽,容易让人以为修改密码窗不允许用读者身份来修改读者密码。

3) 在一台服务器电脑上为 dp2library 创建和配置多个实例。每个实例的 library.xml 配置文件中关于工作人员和读者密码的配置参数都互相不同,需要验证一下这些实例的表现是否符合预期。重点关注,这些实例之间不应该发生错乱的情况(所谓“错乱”就是例如对第一个实例配置了某种特性,没有对第二个实例配置这种特性,但测试发现第二个实例表现出了原本为第一个实例配置的这种特性)

4) 用内务前端的日志窗,检查 setUser 动作的操作日志记录,其 XML 记录中应该是滤除了 password 元素的。由于旧版本采用的是 password 属性(不是元素),所以这里要检查是否忘了滤除 password 元素

5) 工作人员账户权限如果包含 neverexpire,表示这个账户的密码永远不失效。通过内务前端的用户窗修改账户权限的时候,dp2library 会自动检查该账户的密码失效期,如果需要删除失效期或者增加失效期信息,dp2library 会自动对(library.xml 中) account 元素相关部分进行修改。但如果直接用记事本打开 library.xml 修改并保存,就不会有这种自动检查和修改的动作。

6) reader 这个特殊账户,不允许用它直接登录 dp2library 服务器。需要用内务前端来验证一下,用它们登录时候应该报错,说不允许直接登录才对。这个账户主要是提供 dp2library 内部定义读者记录的一些共同的权限之用。

7) public 和 opac 这两个特殊账户,不允许通过正常界面用提供旧密码方式来修改它的密码。这是通过给账户添加了 denychangemypassword 权限实现的效果。但不是绝对不允许修改密码,可以通过超级用户身份给它们强制修改密码。

其中 public 账户主要是提供给 dp2OPAC 模块以访客身份访问 dp2library 之用。它是默认密码为空。倘若系统允许提供旧密码(空)的方式来修改密码,那么密码可能会被人恶作剧修改,修改后 dp2OPAC 模块就没法使用这个账户登录了。这显然要防范。

opac 账户主要是提供给 dp2OPAC 模块的一些管理功能访问 dp2library 之用。它是默认在安装界面设置了强密码的,并且密码会让 dp2OPAC 模块记住。因此这个密码要保持稳定不变,不然到时候会出现 dp2OPAC 功能无法实现的问题,并且重新让 dp2OPAC 记住修改后的密码的管理步骤也相当复杂,所以应尽量避免改密码。

DigitalPlatform commented 3 years ago

library.xml 中几个 xxxExpireLength 属性值的格式

多少天,可以用 “90days” 这样的格式,或者 "90d"; 小时,用 "1hours", 或者 "1hrs",或者 "1h"; 分钟,用 "30minutes", 或者 "30mins", 或者 "30m"; 秒,用 "10seconds", 或者 “10secs”, 或者 "10s"。

参考相关代码片段:

            const string Days = @"(d(ays?)?)";
            const string Hours = @"(h((ours?)|(rs?))?)";
            const string Minutes = @"(m((inutes?)|(ins?))?)";
            const string Seconds = @"(s((econds?)|(ecs?))?)";
DigitalPlatform commented 3 years ago

工作人员账户权限中 neverexpire 和 denychangemypassword 组合使用

这里探讨一下当 library.xml 中定义了工作人员账户有失效期参数的情况,两种权限组合使用的问题。

library.xml 配置文件中 accounts/@passwordExpireLength 参数定义了工作人员账户的密码失效前长度。

neverexpire 是用于禁止该账户的密码被设置失效期而发生失效。denychangemypassword 是用于禁止账户持有者自己通过正常界面修改密码。所谓”账户持有者“是指”知道这个账户当前密码的人“。

假设 accounts/@passwordExpireLength 参数定义了密码失效,那么工作人员账户密码会在失效期到达以后失效。如果这个账户的权限中含有 denychangemypassword(一般这样创建的目的是为了防止账户持有者自己修改密码),那么账户持有者在密码失效后,却无法自己用正常界面修改密码。这样账户持有者就再也无法用这个账户登录了。这是一种比较奇怪的情况。

不过,依然可以呼叫超级用户来强制修改这个账户的密码,当账户持有者知道新密码后,还是可以继续使用这个账户登录的。

如何避免上述奇怪的情况呢?方法就是可以把 neverexpire 和 denychangemypassword 都定义上,那么这个账户的密码就永远不会失效了,同时,账户持有者自己无法修改这个密码。

DigitalPlatform commented 3 years ago

读者记录中密码失效期的测试建议

当读者记录创建和修改密码的时候,dp2library 应正确设置读者记录 password 元素的 expire 属性。

原理:library.xml 配置文件中的 login/@patronPasswordExpireLength 属性定义了读者记录密码的失效前长度,但同时读者记录里面的 rights 元素(加上 reader 账户的 rights)也需要考虑进去,当这个合成的 rights 里面包含了 neverexpire 权限的时候,无论 library.xml 中怎样配置参数,读者记录的 password 都是不应该设置失效期的。

下面尝试为这个因素计划一系列针对性的测试项目。

1 利用内务的读者窗创建一条新的读者记录(注意,这又分为读者记录中提供出生日期和不提供出生日期两种情况)。 1.1 创建前,reader 账户权限中具有 neverexpire,(并且即将创建的读者记录的权限字段中不包含 neverexpire) 1.2 创建前,reader 账户权限中具有 neverexpire,并且即将创建的读者记录的权限字段中也包含 neverexpire 1.3 创建前,reader 账户权限中不具有 neverexpire,并且即将创建的读者记录的权限字段中包含 neverexpire

测试的结果(读者记录 XML)需要用 dp2rms 前端来观察

2 利用内务的修改密码窗修改一条已经存在的读者记录的密码(注意,这又氛围知道旧密码的修改,和不知道旧密码然后用工作人员身份强制修改,两种情况) 2.1 修改前,reader 账户权限中具有 neverexpire,(并且即将被修改密码的读者记录的权限字段中不包含 neverexpire) 2.2 修改前,reader 账户权限中具有 neverexpire,并且即将被修改密码的读者记录的权限字段中也包含 neverexpire 2.3 修改前,reader 账户权限中不具有 neverexpire,并且即将被修改密码的读者记录的权限字段中包含 neverexpire

3 从卡中心同步读者记录到 dp2library,这种情况需要建立相应的环境才能测试。排列组合方法和上面介绍的相同