Add verified administrator controls and upload abuse limits

This commit is contained in:
2026-10-14 20:37:55 +09:00
parent c3573b605c
commit 3786baf60c
23 changed files with 1941 additions and 22 deletions

View File

@@ -203,6 +203,9 @@ https://hop.noname.company
| `USER_STORAGE_LIMIT_MB=2048` | 로그인 계정 **개인별** 보관함 2GiB. 임시 저장·받은 파일 합산, 같은 원본은 한 계정에서 한 번 계산 |
| `GUEST_STORAGE_LIMIT_MB=2048` | 비로그인 기기 세션이 서버에 올리는 파일 총량 2GiB. 로그인 후 기기 간 전송도 해당 기기 세션을 사용 |
| `TOTAL_GUEST_STORAGE_LIMIT_MB=20480` | 서버 전체 익명 업로드 원본의 합계 20GiB |
| `TOTAL_STORAGE_LIMIT_MB=20480` | 로그인·비로그인 전체 원본과 유효 업로드 예약 합계 20GiB. 관리자 설정의 초기값 |
| `MAX_CONCURRENT_UPLOADS=4` | 동시에 처리할 업로드 HTTP 요청·파일 저장 작업의 상한. 관리자 설정의 초기값 |
| `AUDIT_RETENTION_DAYS=30` | 관리자 설정 변경·원본 삭제 기록의 보관 일수 |
| `MIN_FREE_DISK_MB=5120` | NAS에 최소 5GiB 여유 공간 확보 |
| `DEFAULT_FILE_RETENTION_DAYS=7` | 정상 저장한 파일 최대 7일 후 삭제 |
| `ORPHAN_UPLOAD_RETENTION_HOURS=24` | 중단·방치한 업로드 조각 정리 기준 24시간 |
@@ -228,7 +231,7 @@ NAS 파일 관리자에서 **공유 폴더 → docker → projects → apps →
앱 시작 시 기존 평면 `originals` 및 `completed`의 파일을 DB에 기록된 업로드 소유자 폴더로 이동하고 확장자도 적용합니다. 과거 기기 전송의 업로드 소유자가 기기 세션으로만 기록되어 있다면 해당 `guest-기기세션ID` 폴더로 이동하며 로그인 계정을 추정하지 않습니다. 파일 ID·다운로드 주소·만료일·사용량과 기존 자료는 유지합니다. 진행 중인 파일 읽기가 있으면 이동을 미루고 DB 변경 실패 시 원래 경로로 복원합니다. 이동 중 종료되면 숨김 폴더 `.file-moves`의 기록으로 경로와 DB를 맞춥니다. 빈 기존 `completed` 폴더만 제거하며 새 `completed` 폴더는 만들지 않습니다. 저장소 초기화·Compose 수정·폴더 수동 생성은 필요하지 않습니다.
각 사용자 폴더의 `owner-info.json`에서 업로드 요청 시 확인한 국가·지역·도시·접속 IP와 업로드 주체를 확인합니다. 최대 최근 20개·기본 7일의 기록이며 마지막 원본과 대기 업로드가 모두 사라지면 정보 파일도 정리합니다. 좌표·세션 쿠키·CSRF·파일 본문은 저장하지 않습니다. 일반 사용자의 API·보관함·공개 URL로 제공하지 않습니다. 외부 GeoIP 조회 없이 기본 제공 값만 사용하며 미제공 지역과 과거 자료는 확인 불가입니다.
각 사용자 폴더의 `owner-info.json`에서 업로드 요청 시 확인한 국가·지역·도시·접속 IP와 업로드 주체를 확인합니다. 최대 최근 20개·기본 7일의 기록이며 마지막 원본과 대기 업로드가 모두 사라지면 정보 파일과 빈 사용자 폴더도 정리합니다. 좌표·세션 쿠키·CSRF·파일 본문은 저장하지 않습니다. 일반 사용자의 API·보관함·공개 URL로 제공하지 않습니다. 외부 GeoIP 조회 없이 기본 제공 값만 사용하며 미제공 지역과 과거 자료는 확인 불가입니다.
지역은 `CLIENT_IP_SOURCE=cloudflare`일 때 검증된 인접 프록시에서 온 Cloudflare 헤더만 사용합니다. 국가 정보는 IP geolocation, 도시 정보는 **Cloudflare → Rules → Settings/Managed Transforms → Add visitor location headers**를 활성화하면 전달받을 수 있습니다. 현재 대시보드 메뉴가 다르면 [Cloudflare 공식 설정 문서](https://developers.cloudflare.com/rules/transform/managed-transforms/reference/#add-visitor-location-headers)를 참고하세요. CF 좌표·우편번호는 무시합니다. VPN·통신사망 등으로 실제 거주지와 다를 수 있습니다. 악용 대응용 관리자 화면·국가/IP 차단은 별도로 관리하며 실제 규칙은 운영자가 지정합니다.
@@ -237,7 +240,7 @@ NAS 파일 관리자에서 **공유 폴더 → docker → projects → apps →
- **다운로드 실패·수신자 이탈:** 이미 저장된 원본은 바로 삭제되지 않습니다. 받은 항목에 저장된 파일은 로그인 후 만료 전에 다시 다운로드할 수 있습니다. 개인 용량 초과로 받은 항목에 저장되지 않았거나 비로그인으로 받은 경우, 사이트를 떠난 뒤 이전 다운로드를 다시 찾는 UI는 제공하지 않습니다. 이때는 다시 전송하는 방법을 사용합니다. 다운로드 시작 표시는 기기에 저장이 끝났다는 확인을 의미하지 않습니다.
- **업로드 도중 이탈:** 완료되지 않은 업로드는 받은 항목에 생성되지 않습니다. 취소된 조각은 제거하며, 남은 미완료·고아 업로드는 기본 24시간 후 정리합니다. 브라우저를 떠난 뒤 자동 전송·자동 다운로드 재개는 보장하지 않습니다.
- **만료·삭제:** 기본 보관 기간은 항목별 7일이며, 정리 작업은 약 1분 간격입니다. 같은 원본을 사용하는 유효 접근 권한이 하나라도 남아 있으면 유지합니다. 모두 만료·삭제되면 삭제 실행 취소 시간 이후 원본을 제거합니다. 재배포·재시작만으로 파일이 삭제되지는 않습니다.
- **관리자 접근:** NAS·Docker 볼륨과 DB를 읽을 권한이 있는 관리자는 파일과 연결 정보를 확인할 수 있습니다. 종단 간 암호화나 파일 자체 암호화는 적용하지 않습니다. 앱에는 관리자가 모든 사용자 파일을 열람하는 별도 화면이 없으며, 일반 계정은 허용된 본인 항목만 조회할 수 있습니다.
- **관리자 접근:** NAS·Docker 볼륨과 DB를 읽을 권한이 있는 관리자는 파일과 연결 정보를 확인할 수 있습니다. 종단 간 암호화나 파일 자체 암호화는 적용하지 않습니다. 앱의 서비스 관리 화면에서는 인증된 `hello@noname.company`만 파일 메타데이터와 최근 지역/IP를 조회·원본 삭제할 수 있습니다. 파일 내용을 내려받는 관리자 API는 제공하지 않으며 일반 계정은 허용된 본인 항목만 조회할 수 있습니다.
개인 한도가 차도 **수신·다운로드는 계속**합니다. 새로 받은 파일은 받은 항목에 저장하지 않고 짧은 안내를 표시합니다. 직접 저장은 용량 부족 시 거절합니다. 링크·메모는 파일 용량 합계에 포함하지 않습니다. 기존 10GiB 기본값은 설정 스크립트가 2GiB로 바꾸며, 다른 값으로 조정한 설정과 기존 자료는 유지합니다.
@@ -471,3 +474,26 @@ sed -i -e 's/^GUEST_REQUESTS_PER_MINUTE=5$/GUEST_REQUESTS_PER_MINUTE=30/' -e 's/
수신 요청은 파일·링크·텍스트 아이콘과 제목으로 구분하고 수락/거절 버튼은 2:1입니다. 차단은 오른쪽 하단의 작은 텍스트를 누른 뒤 확인해야 실행됩니다. **푸터의 내 기기 설정 → 차단한 기기 관리 → 차단 해제**로 해제합니다. 차단은 현재 브라우저의 기기 세션에 저장되며 로그인 여부와 별개입니다. 해제해도 이전 요청을 다시 수락하는 방식으로 복구하지 않으므로 발신자가 새로 전송해야 합니다. 상대가 나를 별도로 차단했다면 내 차단 해제만으로 상대의 차단을 풀 수 없습니다.
수신 파일 용량은 0.1MB 이상부터 MB, 1GB 이상부터 GB로 표시합니다. 그보다 작은 파일은 KB 또는 ‘1 KB 미만’이며, 파일 한 개의 용량은 파일명 옆에 한 번만 표시합니다. 단위는 MB=1,000,000바이트, GB=1,000,000,000바이트입니다. 서버의 MiB/GiB 기준 저장·파일 한도는 유지합니다.
## 관리자 화면과 업로드 보호
이메일 인증을 완료한 `hello@noname.company` 계정으로 로그인한 뒤 **더보기 → 관리자**를 선택합니다. 별도의 관리자 비밀번호나 환경변수 없이 이 정확한 활성·인증 계정만 권한을 갖습니다. 서버에서도 권한을 검사하며 다른 일반 계정에는 메뉴와 API를 제공하지 않습니다.
저장 파일의 업로드 주체·최근 제공 지역/IP·만료일, 전체 사용량·예약량·디스크 여유와 현재 업로드를 확인할 수 있습니다. 원본 삭제는 확인창을 거치며 모든 사용자의 연결된 항목도 무효화합니다. 이미 응답 중인 다운로드까지 회수하는 기능은 아닙니다. 관리 화면의 지역은 해당 업로드 주체의 최근 기록이며 모든 파일마다 정확한 위치를 추적한 정보는 아닙니다.
**접속 · 용량 설정**에서 동시 업로드 1~16개와 전체 파일 저장 한도 1~1024GiB를 변경합니다. 기본은 4개·20GiB이고, 업로드 요청은 같은 계정/기기 주체당 동시에 최대 2개만 처리합니다. 기존 개인·기기 용량 제한, 파일당 크기 제한, 최소 5GiB 디스크 여유와 전송 요청 제한도 유지합니다. 업로드 본문을 읽기 전에 서버 전체 동시 요청 제한을 적용하고, 들어오는 HTTP 요청에 5분 타임아웃을 적용합니다. 큰 파일은 기존 8MiB 조각 전송을 사용합니다.
**신규 업로드 중지**는 새 파일 전송·임시 저장을 거절합니다. 중지 전에 수락한 파일, 이미 저장된 원본의 재전송·다운로드, 링크·텍스트 전송은 계속됩니다. 한도를 낮춰도 기존 자료를 자동 삭제하지 않습니다.
국가 차단은 `CN, RU`처럼 두 자리 코드를 입력하고, IP는 `203.0.113.10`, `198.51.100.0/24`, IPv6 주소/대역을 쉼표나 줄바꿈으로 구분합니다. 비어 있으면 차단하지 않습니다. 저장 즉시 일치하는 새 HTTP·Socket.IO 접속을 거절하고, 진행 중 업로드와 기기 연결도 종료합니다. `CLIENT_IP_SOURCE`와 `TRUSTED_PROXY_CIDRS`로 검증한 원본 IP를 사용하며 원본 IP가 없으면 직접 연결 주소를 기준으로 합니다. 국가 정보는 검증된 Cloudflare 전달 값만 사용하므로 미제공 국가는 국가 규칙으로 차단하지 않습니다. 현재 관리자의 IP/국가·인접 연결을 차단하는 규칙은 저장할 수 없습니다. 차단 해제는 목록에서 해당 규칙을 지우고 다시 저장합니다. 잘못된 규칙으로 관리자가 차단되면 기존 NAS 접근 권한으로 DB의 `admin_settings`를 복구해야 하므로 실제 규칙을 확인한 뒤 적용합니다.
관리 설정은 DB의 `admin_settings`, 변경·원본 삭제 이력은 `admin_audit`에 보관합니다. 이력은 기본 30일이며 설정은 재시작 뒤 유지됩니다. 한 번 저장한 관리 설정은 `.env` 초기값보다 우선하고 이후에도 관리자 화면에서 수정합니다. 화면은 수동 새로고침만 사용하며 상시 목록 조회를 하지 않습니다.
이번 변경은 관리 테이블만 추가하며 기존 계정·자료를 초기화하지 않습니다. `docker-compose.yaml`과 볼륨·포트·네트워크·`pull_policy: build`는 변경하지 않습니다. 새 환경변수를 추가하지 않아도 기본값으로 작동합니다. 평소와 같이 다음 명령으로 적용합니다.
```sh
git pull --ff-only origin main
sudo docker compose -f docker-compose.yaml up -d --build
```
앱의 업로드 제한과 차단은 저장 공간·동시 작업을 보호하지만 앱에 도달하기 전의 대규모 트래픽 공격까지 해결하지는 않습니다. 필요하면 Cloudflare 등 앞단의 접근·요청 제한도 함께 적용합니다.