修复认证挑战与可信代理安全边界(07-17-auth-challenge-trusted-proxy-hardening)

这个提交包含在:
2026-07-17 14:24:58 +08:00
父节点 bbb5c89825
当前提交 6ddedf1e0e
共修改 31 个文件,包含 3801 行新增和 419 行删除
+45 -15
查看文件
@@ -7,6 +7,15 @@ ADMIN_IDENTIFIER="${ADMIN_IDENTIFIER:[email protected]}"
ADMIN_PASSWORD="${ADMIN_PASSWORD:-AdminPassword1234}"
LOGIN_FAIL_MAX_ATTEMPTS="${LOGIN_FAIL_MAX_ATTEMPTS:-${AUTH_LOGIN_FAIL_MAX_ATTEMPTS:-5}}"
RUN_AUTH_POSTGRES_CONCURRENCY="${RUN_AUTH_POSTGRES_CONCURRENCY:-0}"
case "$RUN_AUTH_POSTGRES_CONCURRENCY" in
0|1) ;;
*)
echo "[AUTH-REGRESSION][FAIL] invalid_concurrency_mode" >&2
exit 1
;;
esac
if ! command -v curl >/dev/null 2>&1; then
echo "[ERROR] curl not found" >&2
@@ -18,6 +27,23 @@ if ! command -v jq >/dev/null 2>&1; then
exit 1
fi
if ! command -v node >/dev/null 2>&1; then
echo "[ERROR] node not found" >&2
exit 1
fi
SERVER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if ! node -e '
try {
require(process.argv[1]).validateAuthRegressionEnvironment(process.env);
} catch {
process.exit(1);
}
' "$SERVER_DIR/src/scripts/check-auth-postgres-concurrency.js"; then
echo "[AUTH-REGRESSION][FAIL] environment_guard_rejected" >&2
exit 1
fi
TMP_DIR="$(mktemp -d)"
cleanup() {
rm -rf "$TMP_DIR"
@@ -34,12 +60,8 @@ assert_status() {
local actual="$1"
local expected="$2"
local name="$3"
local body_file="${4:-}"
if [[ "$actual" != "$expected" ]]; then
echo "[ERROR] $name expected HTTP $expected but got $actual" >&2
if [[ -n "$body_file" && -f "$body_file" ]]; then
cat "$body_file" >&2
fi
exit 1
fi
}
@@ -52,7 +74,6 @@ assert_error_code() {
actual_code="$(jq -r '.error.code // empty' "$file_path")"
if [[ "$actual_code" != "$expected_code" ]]; then
echo "[ERROR] $name expected error code $expected_code but got $actual_code" >&2
cat "$file_path" >&2
exit 1
fi
}
@@ -80,7 +101,7 @@ ADMIN_TOKEN="$(extract_json_field "$admin_login_body" '.data.accessToken')"
echo "[2/8] Weak password should be rejected"
weak_employee_no="W$(date +%s)$RANDOM"
weak_employee_email="weak_${weak_employee_no}@hyinda.com"
weak_employee_email="weak_${weak_employee_no,,}@hyinda.com"
weak_employee_phone="139${RANDOM}${RANDOM}"
weak_create_body="$TMP_DIR/weak-create.json"
weak_create_status="$(curl -sS -o "$weak_create_body" -w "%{http_code}" \
@@ -91,14 +112,13 @@ weak_create_status="$(curl -sS -o "$weak_create_body" -w "%{http_code}" \
assert_status "$weak_create_status" "400" "weak password rejected" "$weak_create_body"
assert_error_code "$weak_create_body" "VALID_001" "weak password rejected"
if ! jq -e '.error.details.password | type == "array" and (map(tostring) | join(" ") | contains("常见"))' "$weak_create_body" >/dev/null; then
echo "[ERROR] weak password error message missing" >&2
cat "$weak_create_body" >&2
echo "[ERROR] weak password validation category missing" >&2
exit 1
fi
echo "[3/8] Create employee (initial password => mustChangePassword)"
employee_no="A$(date +%s)$RANDOM"
employee_email="auth_${employee_no}@hyinda.com"
employee_email="auth_${employee_no,,}@hyinda.com"
employee_phone="138${RANDOM}${RANDOM}"
INITIAL_PASSWORD="InitA1$(date +%s%N)Xy"
create_body="$TMP_DIR/create.json"
@@ -115,7 +135,6 @@ login_status_1="$(login "$employee_email" "$INITIAL_PASSWORD" "${DEVICE_FINGERPR
assert_status "$login_status_1" "200" "initial login" "$login_body_1"
if [[ "$(jq -r '.data.mustChangePassword' "$login_body_1")" != "true" ]]; then
echo "[ERROR] expected mustChangePassword=true on initial login" >&2
cat "$login_body_1" >&2
exit 1
fi
ACCESS_TOKEN_1="$(extract_json_field "$login_body_1" '.data.accessToken')"
@@ -133,7 +152,6 @@ assert_status "$change_status" "200" "change password" "$change_body"
ACCESS_TOKEN_2="$(extract_json_field "$change_body" '.data.accessToken')"
if [[ "$(jq -r '.data.mustChangePassword' "$change_body")" != "false" ]]; then
echo "[ERROR] expected mustChangePassword=false after change" >&2
cat "$change_body" >&2
exit 1
fi
@@ -165,7 +183,6 @@ login_status_2="$(login "$employee_email" "$ONE_TIME_PASSWORD" "${DEVICE_FINGERP
assert_status "$login_status_2" "200" "login with reset password" "$login_body_2"
if [[ "$(jq -r '.data.mustChangePassword' "$login_body_2")" != "true" ]]; then
echo "[ERROR] expected mustChangePassword=true after admin reset" >&2
cat "$login_body_2" >&2
exit 1
fi
@@ -183,7 +200,6 @@ for i in $(seq 1 "$LOGIN_FAIL_MAX_ATTEMPTS"); do
assert_error_code "$attempt_body" "AUTH_002" "lock attempt $i triggers lock"
if [[ "$(jq -r '.error.details.lockedUntil // empty' "$attempt_body")" == "" ]]; then
echo "[ERROR] expected lockedUntil in AUTH_002 details" >&2
cat "$attempt_body" >&2
exit 1
fi
locked=1
@@ -211,5 +227,19 @@ post_unlock_login_body="$TMP_DIR/post-unlock-login.json"
post_unlock_login_status="$(login "$employee_email" "$ONE_TIME_PASSWORD" "${DEVICE_FINGERPRINT}-post-unlock" "$post_unlock_login_body")"
assert_status "$post_unlock_login_status" "200" "login after unlock" "$post_unlock_login_body"
echo "[8/8] Done"
echo "[OK] Auth security regression passed"
if [[ "$RUN_AUTH_POSTGRES_CONCURRENCY" == "1" ]]; then
echo "[8/9] PostgreSQL auth challenge concurrency"
AUTH_SECURITY_EMPLOYEE_ID="$EMPLOYEE_ID" \
AUTH_SECURITY_EMPLOYEE_EMAIL="$employee_email" \
AUTH_SECURITY_CURRENT_PASSWORD="$ONE_TIME_PASSWORD" \
node src/scripts/check-auth-postgres-concurrency.js
else
echo "[8/9] PostgreSQL auth challenge concurrency [SKIP: explicit opt-in required]"
fi
echo "[9/9] Done"
if [[ "$RUN_AUTH_POSTGRES_CONCURRENCY" == "1" ]]; then
echo "[AUTH-REGRESSION][PASS] FULL"
else
echo "[AUTH-REGRESSION][PARTIAL] BASE_ONLY"
fi