보안
우리의 보안 모델을 한 문장으로: 서버에는 공격할 것이 아무것도 없습니다. 아래의 모든 내용은 DevTools에서 직접 확인할 수 있습니다.
아키텍처
Abundera QR은 Cloudflare Pages에서 제공되는 정적 단일 페이지 애플리케이션입니다. 애플리케이션 서버, 데이터베이스, 사용자 계정, 인증, API 엔드포인트, 사용자 데이터를 처리하는 백엔드 코드 경로가 전혀 없습니다. QR 생성, 인코딩, 스캔, 렌더링의 모든 작업은 브라우저 내에서 완전히 실행됩니다.
위협 모델
우리는 사용자 데이터를 수집, 저장, 전송하지 않기 때문에, 가장 일반적인 웹 앱 위협인 자격 증명 도용, 데이터베이스 침해, 세션 하이재킹, 서버 측 주입 등은 해당되지 않습니다. 나머지 공격 표면은 출처에서 제공되는 정적 자산 번들(HTML, CSS, JavaScript)입니다. 우리는 다음을 가정합니다:
- 출처 침해: 공격자가 번들된 자산 중 하나를 악성으로 교체하는 경우. 아래의 CSP가 피해 범위를 제한하며, 캐시 버스터 토큰으로 알려진 정상 버전으로 롤백하기 쉽습니다.
- 사용자 제공 입력: QR 페이로드, vCard 이름, WiFi 비밀번호, 일괄 CSV 행, 스캔된 이미지 내용. 모든 입력 경로는 DOM에 다시 렌더링될 때 신뢰할 수 없는 것으로 처리됩니다.
- 사용자 제공 이미지: vCard 사진 URL 및 로고 업로드. 이미지는 캔버스에 렌더링되며, 원시 마크업으로 DOM에 삽입되지 않습니다.
- 타사 브라우저 확장 프로그램: 범위 외. 브라우저의 확장 프로그램이 모든 페이지를 수정할 권한이 있다면 우리 페이지도 수정할 수 있습니다. 페이지가 수정되지 않은 상태로 로드될 때 우리의 보장이 유효합니다.
콘텐츠 보안 정책 — 지시문별
현재 정책(모든 요청의 응답 헤더에서 확인 가능):
Content-Security-Policy:
default-src 'self';
script-src 'self' 'wasm-unsafe-eval';
worker-src 'self' blob:;
style-src 'self' 'unsafe-inline';
font-src 'self';
img-src 'self' data: blob: https:;
connect-src 'self' https:;
frame-ancestors 'none';
base-uri 'self';
form-action 'self'각 지시문이 허용하는 것과 타협하는 부분:
default-src 'self'— 기본 바닥. 명시적으로 완화하지 않은 것은 동일 출처로 유지됩니다.script-src 'self' 'wasm-unsafe-eval'— 인라인<script>없음,eval()없음.'wasm-unsafe-eval'은 QR 인코더 라이브러리의 WebAssembly 컴파일 경로에 필요하며, 전통적인eval()은 허용하지 않습니다. 사전 배포 검사에서 모든 HTML 페이지의 인라인 스크립트를 스캔하여 빌드를 실패시킵니다.style-src 'self' 'unsafe-inline'— 실제 양보입니다. QR 미리보기가 개별 모듈의 스타일 속성에 픽셀 수준 색상을 인라인으로 계산합니다. 해시 기반 허용 목록도 가능하지만 배포 없이 스타일을 업데이트하면 실패합니다. 타협: 약간 약한 스타일 정책을 수용하지만, 스타일은 어차피 데이터를 유출할 수 없습니다(CSS에는connect-src도달 범위가 없음).img-src 'self' data: blob: https:— 인라인 QR 렌더링을 위한data:, 내보내기 다운로드 URL을 위한blob:, 사용자 제공 vCard 사진 URL을 위한https:. 사용자 제공 URL은 실행되지 않고 렌더링만 됩니다.connect-src 'self' https:— fetch()는 우리 출처와 사용자 시작 HTTPS 가져오기(예: 사진 URL 스캔)로 제한됩니다. DNS/WebSocket/비콘을 통한 임의 호스트로의 유출을 방지합니다.frame-ancestors 'none'— 다른 사이트가 우리를 임베드할 수 없습니다. 클릭재킹을 방지합니다.base-uri 'self'— 악성으로 삽입된<base>태그가 상대 URL을 공격자 출처로 리디렉션할 수 없습니다.form-action 'self'— 삽입된 폼은 우리 출처에만 다시 게시할 수 있습니다. 서버 측으로 데이터를 전송하는 폼이 없으므로 이것은 이중 안전 장치입니다.
/bio/*(사용자 제공 아바타를 위해 완화된 img-src)와 /embed/*(의도적 임베딩을 위해 완화된 frame-ancestors)에는 다른 CSP 정책이 적용됩니다. 두 가지 모두 site/_headers에 문서화되어 있습니다.
전송 및 프레이밍 헤더
- HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload— 1년, 모든 서브도메인, HSTS 프리로드 목록 자격. 적합한 브라우저는 HTTP 다운그레이드를 거부합니다. - X-Frame-Options: DENY — CSP
frame-ancestors와 중복되지만 구형 브라우저 지원을 위해 유지됩니다. - X-Content-Type-Options: nosniff — MIME 혼동 공격을 방지합니다.
- Referrer-Policy: strict-origin-when-cross-origin — 아웃바운드 링크 클릭은 경로가 아닌 출처만 노출합니다.
- Permissions-Policy:
camera=(self), microphone=(), geolocation=()— 카메라는 자체 스캐너에만 허용되며, 마이크와 위치는 임베드된 경우에도 명시적으로 거부됩니다.
서비스 워커
서비스 워커(site/sw.js)는 동일 출처 자산만 캐시합니다. fetch 핸들러는 교차 출처 요청과 GET이 아닌 메서드를 명시적으로 거부합니다. GitHub에서 로직을 확인할 수 있습니다. 캐시 쓰기는 event.waitUntil()로 래핑되어 내비게이션 중간에 삭제되지 않습니다.
입력 살균
사용자 입력을 받는 모든 렌더링 경로는 신뢰할 수 없는 텍스트로 처리합니다:
- QR 페이로드 미리보기는
innerHTML이 아닌textContent를 사용합니다. - 공유 대상(클립보드,
navigator.share())은 사용자 텍스트를 마크업이 아닌 문자열로 전달합니다. - SVG 내보내기는 인코더 라이브러리에서 생성되며, 사용자 콘텐츠는 SVG 요소로 삽입되지 않고
<image>xlink:href에 base64로 인코딩됩니다. - 인쇄 미리보기는
document.write()가 아닌 blob URL을 사용합니다. - localStorage 파싱은
try/catch로 래핑되어, 손상된 항목은 라이브 코드 경로로 예외를 풀지 않고 신선한 빈 기본값을 생성합니다. - 사용자 제공 URL이 "링크 열기" 버튼으로 표시될 때
http(s)://로 제한되며,javascript:및data:스키마는 거부됩니다.
교차 출처 이미지 가져오기
사용자가 vCard 사진 또는 로고로 https: URL을 붙여넣으면, 브라우저는 CORS와 CSP의 img-src 허용 목록을 적용하여 가져옵니다. 이미지는 캔버스에 렌더링됩니다. 라이브 DOM이 되지 않고, 코드로 실행되지 않으며, 우리 출처에 도달하지 않습니다. 가져오기는 브라우저 → 원격 이미지이고 결과는 클라이언트 측에서 페인팅됩니다. 원격 이미지 URL을 제어하는 공격자는 URL이 로드되었다는 것(자체 서버의 로그 줄)을 추적할 수 있지만 우리 페이지에서 아무것도 유출할 수 없습니다.
서브리소스 무결성(SRI)
우리가 제공하는 모든 JavaScript와 CSS는 동일 출처입니다. 타사 스크립트나 스타일시트를 로드하지 않으므로 SRI 해시가 적용되지 않습니다. 타사 자산을 로드하게 되면 SRI integrity 속성을 함께 제공하고 이 페이지에 해시 업데이트 프로세스를 문서화하겠습니다.
취약점 보고
코드, 배포, 또는 제공하는 종속성에서 Abundera QR에 영향을 미치는 보안 문제를 발견했다면 security@abundera.ai로 비공개 보고해 주세요. 72시간 이내에 분류하는 것을 목표로 합니다. /.well-known/security.txt 파일의 연락처를 통해서도 연락하실 수 있습니다.
버그 바운티 없음(아직)
현재 유료 바운티를 제공하지 않지만, 확인된 모든 유효한 보고는 변경 로그에 기재되고 공개 감사를 받습니다.
위의 내용 확인
이 페이지의 모든 주장은 우리를 신뢰하지 않고도 브라우저 DevTools에서 직접 확인할 수 있습니다:
- CSP: DevTools → 네트워크 →
/→ 헤더.Content-Security-Policy응답 헤더를 읽으세요. - HSTS + 보안 헤더: 같은 위치 —
Strict-Transport-Security,X-Frame-Options등이 모두 표시됩니다. - 아웃바운드 호출 없음: DevTools → 네트워크 → Fetch/XHR. QR을 생성하세요. 수가 0으로 유지되는 것을 확인하세요.
- 서비스 워커 범위: DevTools → 애플리케이션 → 서비스 워커. 스크립트 소스와 캐시된 자산 목록을 확인하세요.
- 쿠키 없음: DevTools → 애플리케이션 → 쿠키. 비어 있습니다.
- 전체 안내: 매니페스토의 인증 섹션.
연락처
보안 공개: security@abundera.ai