umm v0.71.0
v0.71.0 — 문장이 아닌 부분
결정의 일부는 문장이 아닙니다. 화이트보드 사진, 지금 이야기 중인 화면의 캡처, 손으로 그린
그림. 그동안 그것들은 나머지 전부를 담고 있는 공간 밖에 있었습니다.
이 기능의 위험은 저장이 아니라 돌려주는 쪽입니다
이 바이트는 사람이 올린 것이고 앱과 같은 출처에서 돌려줍니다. 브라우저가 이걸
그림이 아니라 문서로 렌더링하기로 하면, 그건 저장형 XSS입니다.
세 가지가 막습니다. 어느 것도 업로드가 자기에 대해 한 말이 아닙니다.
1. 형식은 저장할 때 바이트를 읽어서 정합니다
→ PNG · JPEG · GIF · WebP 만 저장 (SVG는 XML이고 스크립트를 담습니다)
2. 응답에 자기 몫의 CSP: default-src 'none'; sandbox
→ 문서로 취급되도록 유도되더라도 그 문서가 할 수 있는 게 없습니다
3. nosniff + inline 이미지로만, 페이지로는 절대
파일 이름은 이름표일 뿐입니다. 형식을 정하지 않고, 경로로도 쓰지 않습니다.
시험이 엉뚱한 이유로 통과하고 있었습니다
권한 시험은 이랬습니다 — 낯선 사람이 남의 생각에 그림을 붙이려 하면 거절돼야 한다.
통과했습니다. 변형으로 권한 조건을 통째로 지웠는데 여전히 통과했습니다.
낯선 사람의 uuid가 실재하지 않는 계정이라, 권한이 아니라 uploaded_by 외래키가 막고
있었습니다. 권한 검사가 아예 없어도 통과하는 시험이었습니다.
접근 권한만 없는 진짜 계정으로 바꾸니 변형이 잡힙니다.
전역 1MB 상한에 걸릴 뻔했습니다
모든 요청 본문이 미들웨어에서 1MB로 잘립니다. 사진은 그보다 큽니다.
바깥에서 이미 감싼 리더를 안에서 더 크게 감싸도 상한은 안 올라갑니다. 핸들러에서
고칠 수 있는 문제가 아니라 미들웨어에서 결정해야 했습니다. 그대로 뒀다면 1MB에서
자기가 한 일과 무관한 오류를 받았을 겁니다.
시험이 실제로 1MB를 넘는 PNG를 만들어 확인합니다. 처음엔 규칙적인 픽셀로 만들었더니
압축돼서 165KB가 됐고 — 시험이 스스로 그렇다고 말하고 멈췄습니다. 진짜 난수로
바꿨습니다.
왜 PostgreSQL인가
umm은 바이너리 하나와 PostgreSQL 하나로 배포되고, 오프라인 이미지가 곧 제품입니다.
버킷을 더하면 가볍게 쓸 대부분의 설치에까지 운영 의존성이 하나 늘어납니다.
대신 이렇게 됩니다: 백업 하나에 전부 들어가고, 생각과 그림이 한 트랜잭션에서 지워지고,
메모에 이미 걸린 접근 규칙이 그림에도 두 번째 구현 없이 적용됩니다.
비용은 실재하고 일부러 묶었습니다 — 그림만, 한 장 5MB, 생각 하나에 8장. 캔버스가
조용히 파일 서버가 되지 않도록.
확인한 것
✓ 붙이고 · 목록에 나오고 · 바이트가 그대로 돌아옴
✓ HTML을 .png로 위장 → 거절, SVG → 거절, 진짜 PNG를 .txt로 → 통과
✓ JPEG · GIF 도 실제로 저장됨 (그림 시험이지 "PNG만 되는" 시험이 아님)
✓ 5MB 초과 거절, 생각당 8장 초과 거절 — 자르지 않고 한도를 알려 줌
✓ 접근 없는 실재 계정은 붙이지도 읽지도 지우지도 못함
✓ 생각을 지우면 그림도 목록에서 사라지고 읽히지 않음
✓ 1MB 넘는 진짜 사진이 업로드됨 (전역 상한을 실제로 넘김)
✓ 응답: image/png · nosniff · default-src 'none'; sandbox · inline
✓ 브라우저: 고르고 · 붙고 · 새로 고쳐도 있고 · 그림으로 돌아옴
| 바꾼 것 | 잡은 것 |
|---|---|
| 업로드가 말한 형식을 믿음 | HTML·SVG가 그림으로 저장됨 |
| SVG 허용 | 스크립트를 담은 "그림" |
| 크기 상한 제거 | 상한이 없어짐 |
| 생각당 장수 제한 제거 | 제한이 없어짐 |
| 붙이기 권한 검사 제거 | (처음엔 못 잡음 → 시험을 고침) |
| 읽기 권한 검사 제거 | 남의 공간 그림을 읽음 |
| 그림 응답의 CSP 제거 | 페이지 정책으로 서빙됨 |
| 업로드 경로의 상한 예외 제거 | 1MB에서 실패 |
여덟 개 전부 검출(하나는 시험을 고친 뒤). nosniff 를 핸들러에서 빼는 변형은 잡히지
않았고, 그건 전역 미들웨어가 이미 넣기 때문입니다 — 핸들러의 그 줄은 이중 방어라고
주석에 적었습니다.
안 한 것
내보내기에는 그림이 실리지 않습니다. 마크다운 백업에 base64로 넣으면 파일이 감당 못 할
크기가 됩니다. 조용히 빼지 않고 여기 적어 둡니다 — 자기 백업으로 공간을 되돌리면 글자와
자리와 연결은 돌아오고 그림은 돌아오지 않습니다.
전체 검증: go test -p 1 ./internal/... ./cmd/...(통합 포함) · go vet · gofmt ·
변형 8종 검출(store 6 · http 2, 하나는 시험을 고친 뒤) · vitest 156개 ·
attachments e2e 2개(실제 바이너리·실제 번들) · oxlint(종료 코드) · Prettier · tsc ·
i18n 992키 · OpenAPI 드리프트 · PWA precache
What's Changed
Full Changelog: v0.70.0...v0.71.0