Кабинет клиента: документы и корпоративные действия, дистрибутив, обратный канал, защита
Данные - корпоративные действия по бумагам клиента (U_CA_ACTS), готовые выписки (U_OP_INF_EXTRACT), заявки клиента на документ (lk.doc_requests) с оповещением сотрудникам; страница «Документы» - дистрибутив кабинета из настроек ядра: архив с программой, службой, настройками, установщиком и инструкцией; ключ доступа на странице показан замаскированным Связь без публичного адреса у ядра - режим ожидания (BJ_MODE=agent): кабинет копит запросы, ядро само подключается к нему, выполняет их обработчиком /api/lk/* и возвращает ответы; 4 параллельных подключения, тот же ключ модуля в обе стороны, состояние канала в настройках - сквозной тест: настоящий кабинет + настоящий цикл ядра Защита (по итогам аудита) - вход: лимит попыток по адресу и логину, одинаковое время ответа для несуществующего логина, пароль от 10 символов - сессии ядра: на диске отпечаток токена и ссылка на учётную запись, без персональных данных; карточка клиента читается из БД на каждый запрос (блокировка действует сразу); абсолютный срок 24 ч; привязка к браузеру, к сети — по настройке - вход сотрудника в кабинет клиента пишется в lk.audit и поднимает оповещение - кабинет: CSRF на всех формах, /impersonate только POST, куки HttpOnly+Secure+SameSite=Strict, выход гасит все сессии, заголовки CSP/X-Frame-Options/nosniff/HSTS, no-store, таймауты сервера, ограничение тела, recover; сообщения через куку вместо адреса; /health наружу без подробностей - шрифты вшиты в программу: страница не обращается к сторонним сайтам - экранирование LIKE в выборках по номеру договора; ключ и токен без суррогата при отказе rand - исправлено: ошибка входа не показывалась клиенту (шаблон ждал Error, приходил Flash) Тесты: lksecurity_test, lkagent_test, lkdist_test, modules/lk (CSRF, заголовки, режим ожидания), живые lkcoverage/lkunpriced, scripts/lk-sec-smoke.sh — проверка работающего кабинета снаружи. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
# Проверка защиты работающего кабинета снаружи. Ничего не меняет: только запросы к страницам.
|
||||
B=${1:-http://127.0.0.1:8099}
|
||||
pass=0; fail=0
|
||||
ok(){ echo " ✓ $1"; pass=$((pass+1)); }
|
||||
no(){ echo " ✗ $1"; fail=$((fail+1)); }
|
||||
chk(){ if [ "$2" = "$3" ]; then ok "$1"; else no "$1 (получено: $2, ожидали: $3)"; fi }
|
||||
H=$(curl -s -m8 -D- -o /dev/null $B/login)
|
||||
echo "Заголовки защиты:"
|
||||
for h in "X-Frame-Options: DENY" "X-Content-Type-Options: nosniff" "Referrer-Policy: same-origin" "frame-ancestors 'none'" "default-src 'none'" "no-store" "Permissions-Policy"; do
|
||||
echo "$H" | grep -qi -- "$h" && ok "$h" || no "нет: $h"; done
|
||||
echo "Куки:"
|
||||
echo "$H" | grep -i "set-cookie: bj_lk_csrf" | grep -qi "httponly" && ok "токен формы HttpOnly" || no "токен формы без HttpOnly"
|
||||
echo "$H" | grep -i "set-cookie: bj_lk_csrf" | grep -qi "samesite=strict" && ok "SameSite=Strict" || no "нет SameSite=Strict"
|
||||
HS=$(curl -s -m8 -D- -o /dev/null -H "X-Forwarded-Proto: https" -H "Host: lk.example.ru" $B/login)
|
||||
echo "$HS" | grep -i "set-cookie" | grep -qi "secure" && ok "за nginx с TLS куки получают Secure" || no "куки без Secure за TLS"
|
||||
echo "$HS" | grep -qi "strict-transport-security" && ok "HSTS за TLS" || no "нет HSTS за TLS"
|
||||
echo "Сторонние ресурсы:"
|
||||
P=$(curl -s -m8 $B/login)
|
||||
echo "$P" | grep -qiE "https?://[^\"' ]*(googleapis|gstatic|cdn|jsdelivr|unpkg)" && no "страница тянет сторонние ресурсы" || ok "сторонних адресов на странице нет"
|
||||
echo "$P" | grep -qi "<script" && no "на странице есть скрипты" || ok "скриптов на странице входа нет"
|
||||
echo "Страницы без входа:"
|
||||
for p in / /portfolio /orders /order /operations /income /documents /profile /clients /security?isin=RU0009029540; do
|
||||
c=$(curl -s -m8 -o /dev/null -w "%{http_code} %{redirect_url}" $B$p); echo "$c" | grep -q "^303 .*/login" && ok "$p → вход" || no "$p без входа: $c"; done
|
||||
echo "Формы без токена:"
|
||||
chk "POST /order без сессии" "$(curl -s -m8 -o /dev/null -w '%{http_code}' -X POST -d 'isin=RU0009029540' $B/order)" "303"
|
||||
c=$(curl -s -m8 -X POST -d "login=x&password=y" $B/login | grep -c "Форма устарела"); chk "POST /login без токена отклонён" "$c" "1"
|
||||
R=$(curl -s -m8 -D- -o /dev/null -b "bj_lk=abcdef0123456789abcdef0123456789abcdef0123456789" "$B/impersonate?contract=BR-1")
|
||||
echo "$R" | grep -i "^set-cookie: bj_lk=" | grep -qv "abcdef0123456789abcdef0123456789abcdef0123456789" && no "GET /impersonate выдал новую сессию" || ok "GET /impersonate не переключает сессию"
|
||||
echo "$R" | grep -qi "set-cookie: bj_lk_as=." && no "GET /impersonate включил режим просмотра" || ok "GET /impersonate не включает режим просмотра клиента"
|
||||
echo "Служебные точки:"
|
||||
chk "/health без ключа — только признак жизни" "$(curl -s -m8 $B/health)" '{"ok":true}'
|
||||
chk "/agent/tasks в прямом режиме закрыт" "$(curl -s -m8 -o /dev/null -w '%{http_code}' $B/agent/tasks)" "404"
|
||||
b=$(curl -s -m8 -L --path-as-is $B/assets/../lk.go | grep -c "^package lk"); chk "/assets/../lk.go — обход каталога не отдаёт файлы" "$b" "0"
|
||||
b=$(curl -s -m8 --path-as-is "$B/assets/%2e%2e/%2e%2e/etc/passwd" | grep -c "root:"); chk "/assets/%2e%2e/… — файлы системы недоступны" "$b" "0"
|
||||
chk "/assets/ — листинга нет" "$(curl -s -m8 -o /dev/null -w '%{http_code}' $B/assets/)" "404"
|
||||
echo "Размер запроса:"
|
||||
head -c 2500000 /dev/zero | tr '\0' 'a' > big.bin
|
||||
c=$(curl -s -m15 -o /dev/null -w '%{http_code}' -X POST --data-binary @big.bin -H "Content-Type: application/x-www-form-urlencoded" $B/login); rm -f big.bin
|
||||
[ "$c" != "303" ] && ok "тело 2,5 МБ не принято как вход (код $c)" || no "большое тело принято"
|
||||
echo "Подделка сессии:"
|
||||
c=$(curl -s -m8 -o /dev/null -w "%{http_code} %{redirect_url}" -b "bj_lk=0000000000000000000000000000000000000000000000000; bj_lk_admin=1" $B/clients)
|
||||
b=$(curl -s -m8 -b "bj_lk=0000000000000000000000000000000000000000000000000; bj_lk_admin=1" $B/clients | grep -c "Открыть кабинет")
|
||||
chk "кука bj_lk_admin=1 без сессии не даёт списка клиентов" "$b" "0"
|
||||
echo; echo "ИТОГО: пройдено $pass, не пройдено $fail"
|
||||
Reference in New Issue
Block a user