MRBS 1.12.2 等保2.0二级整改完整提交
包含:登录失败锁定、90天密码有效期、30分钟会话超时、 强制改密、登录审计日志、屏幕水印、企业背景图、 备案信息固定底部、favicon、JS空集合保护、 会话过期体验优化(403 JSON)、display_errors 关闭、 固定 key 根治 Integrity check failed 等全部改动 注意:config.inc.php/.htaccess/.user.ini 含敏感信息, 通过 .gitignore 排除,勿推送到公开仓库。
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace MRBS;
|
||||
|
||||
use MRBS\Audit;
|
||||
use MRBS\Form\Element;
|
||||
use MRBS\Form\ElementFieldset;
|
||||
use MRBS\Form\ElementP;
|
||||
use MRBS\Form\FieldDiv;
|
||||
use MRBS\Form\FieldInputPassword;
|
||||
use MRBS\Form\FieldInputSubmit;
|
||||
use MRBS\Form\Form;
|
||||
|
||||
require "defaultincludes.inc";
|
||||
|
||||
|
||||
// ===== 等保2.0二级整改:自助修改密码页 =====
|
||||
// - 仅限已登录用户(未登录自动引导到登录页,登录后回到本页)
|
||||
// - 校验当前密码 → 新密码复杂度($pwd_policy)→ 两次一致 → 新旧不同
|
||||
// - 成功后写 password_changed_at(供 90 天有效期计算)并清除强制改密标记
|
||||
// - 所有成功/失败动作写入安全审计日志(Audit)
|
||||
|
||||
|
||||
function generate_change_password_form(?string $error = null, string $target_url = 'index.php') : void
|
||||
{
|
||||
global $pwd_policy;
|
||||
|
||||
$form = new Form(Form::METHOD_POST);
|
||||
$form->setAttributes(array(
|
||||
'class' => 'standard',
|
||||
'id' => 'change_password',
|
||||
'action' => multisite('change_password.php')
|
||||
));
|
||||
|
||||
$form->addHiddenInputs(array(
|
||||
'action' => 'change_password',
|
||||
'target_url' => $target_url
|
||||
));
|
||||
|
||||
$fieldset = new ElementFieldset();
|
||||
$fieldset->addLegend(get_vocab('change_password'));
|
||||
|
||||
// 顶部提示 / 错误消息
|
||||
$field = new FieldDiv();
|
||||
$p = new ElementP();
|
||||
|
||||
if (isset($error))
|
||||
{
|
||||
switch ($error)
|
||||
{
|
||||
case 'old_pwd_invalid':
|
||||
$p->setText(get_vocab('old_pwd_invalid'));
|
||||
break;
|
||||
case 'pwd_not_match':
|
||||
$p->setText(get_vocab('passwords_not_eq'));
|
||||
break;
|
||||
case 'pwd_same':
|
||||
$p->setText(get_vocab('pwd_same_as_old'));
|
||||
break;
|
||||
case 'pwd_invalid':
|
||||
$p->setText(get_vocab('password_invalid'));
|
||||
break;
|
||||
default:
|
||||
$p->setText(get_vocab('unknown_user'));
|
||||
break;
|
||||
}
|
||||
$p->setAttribute('class', 'error');
|
||||
$field->addControlElement($p);
|
||||
|
||||
// 策略不满足时列出具体规则
|
||||
if (($error == 'pwd_invalid') && isset($pwd_policy))
|
||||
{
|
||||
$ul = new Element('ul');
|
||||
$ul->setAttribute('class', 'error');
|
||||
foreach ($pwd_policy as $rule => $value)
|
||||
{
|
||||
if ($value != 0)
|
||||
{
|
||||
$li = new Element('li');
|
||||
$li->setText(get_vocab('policy_' . $rule, $value));
|
||||
$ul->addElement($li);
|
||||
}
|
||||
}
|
||||
$field->addControlElement($ul);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 提示(强制改密或常规自助修改)
|
||||
$p->setText(get_vocab('pwd_expired_msg'));
|
||||
$field->addControlElement($p);
|
||||
}
|
||||
$fieldset->addElement($field);
|
||||
|
||||
// 当前密码
|
||||
$field = new FieldInputPassword();
|
||||
$field->setLabel(get_vocab('current_password'))
|
||||
->setControlAttributes(array('id' => 'password_old',
|
||||
'name' => 'password_old',
|
||||
'autocomplete' => 'current-password',
|
||||
'required' => true,
|
||||
'autofocus' => true));
|
||||
$fieldset->addElement($field);
|
||||
|
||||
// 新密码(输入两次)
|
||||
$labels = array(get_vocab('new_password'), get_vocab('confirm_password'));
|
||||
for ($i = 0; $i < 2; $i++)
|
||||
{
|
||||
$field = new FieldInputPassword();
|
||||
$field->setLabel($labels[$i])
|
||||
->setControlAttributes(array('id' => "password$i",
|
||||
'name' => "password$i",
|
||||
'autocomplete' => 'new-password',
|
||||
'required' => true));
|
||||
$fieldset->addElement($field);
|
||||
}
|
||||
|
||||
// 口令策略说明
|
||||
if (isset($pwd_policy))
|
||||
{
|
||||
$field = new FieldDiv();
|
||||
$p = new ElementP();
|
||||
$p->setText(get_vocab('pwd_must_contain'));
|
||||
$field->addControlElement($p);
|
||||
$ul = new Element('ul');
|
||||
$ul->setAttribute('id', 'pwd_policy');
|
||||
foreach ($pwd_policy as $rule => $value)
|
||||
{
|
||||
if ($value != 0)
|
||||
{
|
||||
$li = new Element('li');
|
||||
$li->setText(get_vocab('policy_' . $rule, $value));
|
||||
$ul->addElement($li);
|
||||
}
|
||||
}
|
||||
$field->addControlElement($ul);
|
||||
$fieldset->addElement($field);
|
||||
}
|
||||
|
||||
$form->addElement($fieldset);
|
||||
|
||||
// 提交按钮
|
||||
$fieldset = new ElementFieldset();
|
||||
$field = new FieldInputSubmit();
|
||||
$field->setControlAttributes(array('value' => get_vocab('change_password')));
|
||||
$fieldset->addElement($field);
|
||||
$form->addElement($fieldset);
|
||||
|
||||
$form->render();
|
||||
}
|
||||
|
||||
|
||||
function generate_change_password_success(string $target_url) : void
|
||||
{
|
||||
echo "<h2>" . get_vocab('change_password') . "</h2>\n";
|
||||
echo "<p class=\"notice\">" . get_vocab('password_changed') . "</p>\n";
|
||||
echo '<p><a href="' . htmlspecialchars(multisite($target_url)) . '">' . get_vocab('back') . "</a></p>\n";
|
||||
}
|
||||
|
||||
|
||||
// ===== 主流程 =====
|
||||
|
||||
// 必须是已登录用户
|
||||
$mrbs_user = session()->getCurrentUser();
|
||||
if (!isset($mrbs_user))
|
||||
{
|
||||
// 未登录:引导到登录页,登录成功后回到本页
|
||||
session()->authGet(null, 'change_password.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// 跳转目标(仅允许站内相对 URL)
|
||||
$target_url = get_form_var('target_url', 'url_local', null, INPUT_GET);
|
||||
if (!isset($target_url) || ($target_url == ''))
|
||||
{
|
||||
$target_url = 'index.php';
|
||||
}
|
||||
// 防止把改密页自身作为跳转目标(避免循环)
|
||||
if ($target_url == 'change_password.php')
|
||||
{
|
||||
$target_url = 'index.php';
|
||||
}
|
||||
|
||||
// 处理提交(action 只从 POST 读取,且必须通过 CSRF 校验)
|
||||
$action = get_form_var('action', 'string', null, INPUT_POST);
|
||||
|
||||
if (isset($action) && ($action == 'change_password'))
|
||||
{
|
||||
Form::checkToken();
|
||||
|
||||
$old_password = get_form_var('password_old', 'string', null, INPUT_POST);
|
||||
$password0 = get_form_var('password0', 'string', null, INPUT_POST);
|
||||
$password1 = get_form_var('password1', 'string', null, INPUT_POST);
|
||||
|
||||
$post_target = get_form_var('target_url', 'url_local', null, INPUT_POST);
|
||||
if (isset($post_target) && ($post_target != ''))
|
||||
{
|
||||
$target_url = $post_target;
|
||||
}
|
||||
|
||||
$error = null;
|
||||
|
||||
// 1. 校验当前密码
|
||||
// (注意:validateUser 失败会计入失败次数;连续 5 次错误当前账号将被临时锁定,
|
||||
// 与登录通道行为一致,属预期安全设计)
|
||||
if (($old_password === null) || ($old_password === '') ||
|
||||
!auth()->validateUser($mrbs_user->username, $old_password))
|
||||
{
|
||||
$error = 'old_pwd_invalid';
|
||||
Audit::log('PWD_CHANGE_FAIL', $mrbs_user->username, 'old password incorrect');
|
||||
}
|
||||
// 2. 两次输入一致
|
||||
elseif ($password0 !== $password1)
|
||||
{
|
||||
$error = 'pwd_not_match';
|
||||
}
|
||||
// 3. 符合复杂度策略
|
||||
elseif (($password0 === null) || ($password0 === '') ||
|
||||
!auth()->validatePassword($password0))
|
||||
{
|
||||
$error = 'pwd_invalid';
|
||||
}
|
||||
// 4. 新旧密码不同
|
||||
elseif ($password0 === $old_password)
|
||||
{
|
||||
$error = 'pwd_same';
|
||||
}
|
||||
else
|
||||
{
|
||||
// 成功:更新口令并记录修改时间
|
||||
auth()->updatePassword($mrbs_user->username, $password0);
|
||||
Audit::log('PWD_CHANGE', $mrbs_user->username, 'self-service change');
|
||||
|
||||
// 清除“强制改密”标记(须在 session_write_close 前完成)
|
||||
unset($_SESSION['mrbs_force_pwd_change']);
|
||||
session_write_close();
|
||||
|
||||
location_header('change_password.php?result=ok&target_url=' . urlencode($target_url));
|
||||
exit;
|
||||
}
|
||||
|
||||
// 校验失败:回到表单显示错误(PRG 模式,防止表单重复提交)
|
||||
location_header('change_password.php?error=' . urlencode($error) . '&target_url=' . urlencode($target_url));
|
||||
exit;
|
||||
}
|
||||
|
||||
// ===== 渲染页面 =====
|
||||
$context = array(
|
||||
'view' => $view,
|
||||
'view_all' => $view_all,
|
||||
'year' => $year,
|
||||
'month' => $month,
|
||||
'day' => $day,
|
||||
'area' => isset($area) ? $area : null,
|
||||
'room' => isset($room) ? $room : null
|
||||
);
|
||||
|
||||
print_header($context);
|
||||
|
||||
$result = get_form_var('result', 'string', null, INPUT_GET);
|
||||
$error = get_form_var('error', 'string', null, INPUT_GET);
|
||||
|
||||
echo "<div class=\"contents\">\n";
|
||||
|
||||
if (isset($result) && ($result == 'ok'))
|
||||
{
|
||||
generate_change_password_success($target_url);
|
||||
}
|
||||
else
|
||||
{
|
||||
generate_change_password_form($error, $target_url);
|
||||
}
|
||||
|
||||
echo "</div>\n";
|
||||
|
||||
print_footer();
|
||||
Reference in New Issue
Block a user