요약
운영하는 사이트가 여러 개로 늘어나면 “관리자로 못 들어가는” 순간이 반드시 온다. 이때 사이트를 하나씩 붙잡고 씨름하면 열 번 헤맨다. 사이트 수가 아니라 계정 체계 수를 세면 대개 서너 번에 끝난다. 실제로 사이트 10개를 점검했더니 계정 체계는 4개뿐이었고, 그중 잠긴 계정은 하나도 없었다. 문제는 분실이 아니라 기록 부재였다.
왜 이 기술인가
사이트를 하나씩 만들다 보면 로그인 방식도 하나씩 붙는다. 어떤 건 통합 로그인을 쓰고, 어떤 건 외부 인증 서비스를 쓰고, 어떤 건 환경변수에 비밀번호 한 줄만 박혀 있다. 만든 사람은 그때그때 알고 있으니 적어두지 않는다. 몇 달 지나면 무엇이 어디에 저장돼 있는지부터 모르는 상태가 된다.
이 상태에서 “비밀번호를 재설정하자”고 덤비면 두 가지 사고가 난다. 멀쩡히 돌아가던 계정의 세션을 전부 날려 다른 사람까지 로그아웃시키거나, 급한 마음에 사용자 삭제 같은 되돌릴 수 없는 버튼을 누른다.
단계별 따라하기
1단계 — 연결된 사이트를 실제로 열거한다
기억에 의존하지 말고 기계로 뽑는다. 허브 페이지의 링크를 긁는 것이 출발점이다.
curl -s https://example.org/ | grep -oE 'href="https?://[^"]+"' | sort -u여기서 끝내면 안 된다. 링크로 노출되지 않는 서브도메인과 서버형 앱이 빠진다. 웹서버 설정 파일이 진짜 지도다. 도메인·포트·프록시 대상이 전부 거기 있다. 배포 스크립트도 함께 본다 — 어떤 저장소가 어디로 배치되는지 적혀 있다.
2단계 — 사이트별 인증 방식을 특정한다
저장소를 전부 내려받을 필요는 없다. 파일 목록만 훑어 인증 관련 파일을 찾고, 걸린 것만 읽는다.
찾을 이름: auth, login, admin, password, user, session
읽을 때 확인할 것은 넷이다.
- 비밀번호를 어떤 방식으로 저장하는가 (해시 종류와 반복 횟수)
- 어느 테이블·어느 파일에 저장되는가
- 세션 쿠키 이름과 적용 도메인 — 도메인이
.example.org처럼 상위로 잡혀 있으면 하위 사이트가 로그인을 공유한다 - 옛 비밀번호 없이 관리자를 만드는 뒷문이 있는가 — 초기 관리자 부트스트랩 경로가 열려 있는 경우가 많다
3단계 — 체계별로 묶는다
여기서 개수가 확 줄어든다. 표로 만들어 사용자와 공유한다.
| 체계 | 저장 위치 | 덮는 사이트 |
|---|---|---|
| 통합 로그인(SSO) | 자체 서버 DB | 허브 외 4개 |
| 외부 인증 서비스 | 그 서비스의 대시보드 | 2개 |
| 환경변수 비밀번호 | 서버 설정 파일 | 1개 |
| 개별 계정 DB | 앱 전용 DB | 1개 |
| 로그인 없음 | — | 나머지 |
4단계 — 재설정하기 전에 로그인을 먼저 시도한다
이 순서가 핵심이다. 실제 사례에서 두 체계 모두 기존 비밀번호가 그대로 맞았다. 외부 인증 서비스에서는 “새 비밀번호는 기존과 달라야 합니다”라는 오류가 떴는데, 이건 실패가 아니라 입력한 값이 현재 비밀번호라는 확인이었다.
먼저 재설정부터 했다면 멀쩡한 계정의 세션을 전부 끊고, 같은 로그인을 쓰던 다른 사이트까지 로그아웃시켰을 것이다.
5단계 — 안 되는 것만 골라 체계별 경로로 복구한다
- 자체 서버 DB — 코드와 똑같은 해시 방식으로 새 값을 만들어 갱신한다. 반복 횟수까지 맞춰야 한다.
- 외부 인증 서비스 — 대개 대시보드에 비밀번호 직접 입력 항목이 없고 메일 발송만 있다. 보내기 전에 리다이렉트 주소 설정을 확인한다(아래 교훈 2).
- 환경변수 비밀번호 — 파일에 쓰고 서비스를 재시작한다. 값은 절대 출력하지 말고 글자수만 찍어 확인한다.
6단계 — 실제 로그인으로 확인하고, 기록을 남긴다
페이지가 열리는지가 아니라 로그인이 되는지로 확인한다. 그리고 반드시 비밀번호 관리자에 등록한다. 이 마지막 단계를 빼면 몇 달 뒤 같은 일이 반복된다.
교훈
하나 — 잠긴 게 아니라 적어두지 않았을 뿐인 경우가 많다. 그러니 “재설정”이 아니라 “확인”부터 시작한다. 재설정은 되돌리기 어렵고 남에게 영향을 준다.
둘 — 계정을 찾다 보면 진짜 문제는 딴 데서 나온다. 이번에도 실제로 고친 것은 잠긴 계정이 아니라 방치된 설정 세 가지였다. 외부 인증 서비스의 리다이렉트 주소가 설치 기본값(http://localhost:3000)으로 남아 있어서, 그동안 발송된 가입 확인 메일과 비밀번호 복구 메일의 링크가 전부 죽어 있었다. 아무도 몰랐던 이유는 단순하다 — 기존 사용자는 아이디·비밀번호로 바로 로그인하니 그 경로를 쓸 일이 없었고, 신규 가입자는 메일이 안 온다고 말해주지 않았다.
셋 — 공개 저장소에 적힌 기본 비밀번호는 비밀번호가 아니다. 환경변수를 설정하지 않으면 코드에 적힌 기본값으로 열리는 앱이 있었다. 그 코드는 공개돼 있었다. 설정 파일 한 줄이 비어 있느냐 아니냐가 공개 여부를 갈랐다.
넷 — 조사 중에 나온 비밀을 그대로 두지 않는다. 확인 과정에서 관리자 키를 화면에 띄우거나 대화에 붙여넣게 되는 일이 생긴다. 그러면 그 값은 죽은 것으로 치고 교체한다. 교체하면 그 키가 박힌 북마크와 스크립트가 함께 죽으니, 무엇이 깨지는지 미리 알린다. 2단계 인증 등록 화면의 QR 코드도 마찬가지다 — 그 그림 자체가 비밀키라서, 화면이 공유됐다면 등록을 취소하고 새로 받아야 한다.
다섯 — 위험한 버튼에는 미리 선을 긋는다. 사용자 삭제, 차단, 2단계 인증 해제 같은 항목은 복구 화면 바로 옆에 있다. 안내할 때 “이건 누르지 마십시오”를 먼저 말해둔다.
참고
- 인증 코드를 읽을 때는 저장소를 클론하지 말고 파일 목록만 훑은 뒤 필요한 파일만 꺼내 읽으면 훨씬 빠르다.
- 스크립트를 서버에서 실행할 때, 스크립트 본문을 표준입력으로 흘려보내면서 그 안에서 비밀번호를 입력받으려 하면 실패한다. 입력 통로가 이미 스크립트에 점유돼 있기 때문이다. 파일로 저장한 뒤 실행한다.
- 비밀번호를 명령줄 인자나 SQL문에 평문으로 넣으면 셸 기록이나 편집기 자동저장에 남는다. 대화형 입력을 쓰고, 불가피했다면 뒤처리까지 안내한다.
문서 정리 = 데카(deka2026)