Безопасность: fail-closed auth (H1), доверенный прокси для IP (H2), CSRF на /order (M2)
По результатам security-аудита (docs, run-1). Три верифицированные находки. H1 (HIGH) — fail-open аутентификация. loadUsers() при ошибке чтения/парсинга users.json молча возвращал пустой стор → authOpen() считал это первичной настройкой → после удаления first-run файла весь /admin отдавался без входа. Теперь: постоянный маркер configured (пишется при создании первого админа и бэкфиллится при загрузке валидного стора); authOpen() открыт только на реально свежей системе; при пустом/битом сторе на настроенной системе — 503 и блокировка до восстановления файла (authLockedOut). Тест TestAuthFailClosed (4 сценария). H2 (HIGH) — интернет-слой доверял клиентскому X-Forwarded-For/X-Real-IP, что позволяло обойти лимит входа по IP и травить аудит. Теперь заголовкам доверяем только от доверенного прокси (по умолчанию loopback; расширяется BJ_TRUSTED_PROXIES), из XFF берём правый элемент. Исправлено и в Go-модуле (modules/lk/security.go), и в PHP-ретрансляторе. Тесты TestClientIPTrust (Go) и прогон логики PHP. M2 (MEDIUM) — POST /order (поручение на списание бумаг) не проверял CSRF, в отличие от /login и /impersonate. Добавлен csrfOK (форма уже несла токен). Тест TestOrderCSRF. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4YVo4UMopqT5DzicqpPjL
This commit is contained in:
@@ -614,3 +614,26 @@ func TestChartAndProfile(t *testing.T) {
|
||||
t.Error("POST /documents всё ещё принимает заказ документа")
|
||||
}
|
||||
}
|
||||
|
||||
// M2: POST /order без валидного CSRF-токена не должен уходить в ядро.
|
||||
func TestOrderCSRF(t *testing.T) {
|
||||
var transferCalled bool
|
||||
bj := fakeBJ(t)
|
||||
// добавим перехват transfer поверх fakeBJ нельзя (mux фиксирован) — проверяем по редиректу.
|
||||
s, err := New(Config{BJURL: bj.URL, BJKey: "lk-key", Title: "Кабинет"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h := s.Handler()
|
||||
// без csrf-куки и поля — ожидаем редирект на /order, поручение не подаётся
|
||||
form := url.Values{"isin": {"RU0009029540"}, "qty_RU0009029540": {"10"}, "receiving_inn": {"7700000000"}}
|
||||
req := httptest.NewRequest(http.MethodPost, "/order", strings.NewReader(form.Encode()))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.AddCookie(&http.Cookie{Name: "bj_lk", Value: "sess-1"})
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusSeeOther || rec.Header().Get("Location") != "/order" {
|
||||
t.Errorf("POST /order без CSRF: ожидали редирект на /order, got %d %s", rec.Code, rec.Header().Get("Location"))
|
||||
}
|
||||
_ = transferCalled
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user