보안

QuoteBill이 계정과 문서를 지키는 방법: 행 단위 보안, 해시로만 저장하는 키와 링크, 기록이 남는 관리자 열람, 전자계약 감사 기록의 연결 구조.

이 페이지가 다루는 것

이 페이지는 QuoteBill의 코드와 데이터베이스가 계정과 문서를 지키기 위해 실제로 하는 일을 적은 것입니다. 보장이 아니라 조치를 설명합니다. 결함이 없는 서비스는 없으며, 이 페이지는 인증서도 감사 결과도 아닙니다. 기능별로 무엇을 얼마나 저장하는지는 개인정보 처리방침에 있고, 전자계약의 신뢰 관련 내용은 별도 페이지에 있습니다.

누가 내 데이터를 읽을 수 있나

저장한 문서와 회사 설정은 데이터베이스의 행 단위 보안으로 보호됩니다. 브라우저에서는 로그인한 소유자만 읽고 바꿀 수 있습니다.

AI 앱에 발급된 로그인 토큰은 제한 규칙에 따라 이 표들에서 거절되므로, 연결한 AI 앱은 저장된 문서, 계약서, 파일을 열 수 없습니다. QuoteBill MCP 서버 자체도 공개 정보와 계산만 쓰며 회원 데이터를 읽지 않습니다.

방문 기록, 일별 합계, 요청 횟수 기록처럼 서버만 쓰는 표는 브라우저와 AI 앱에 닫혀 있습니다. 행 단위 보안이 켜져 있고, 읽을 수 있게 하는 정책이나 권한이 없습니다.

전자계약 데이터는 브라우저가 조회할 수 없는 비공개 데이터베이스 스키마에 있습니다. 요청한 사람을 확인하는 정해진 데이터베이스 함수로만 접근할 수 있습니다. 계약서는 계약서 ID와 소유자로 찾으므로 다른 회원의 계약서는 없는 계약서와 똑같이 응답합니다.

일부 데이터는 설계상 공개입니다. 커뮤니티 글과 공개된 소식이 그렇습니다. 회사 로고, 도장, 서명 이미지는 주소를 아는 사람이 열 수 있는 곳에 저장되므로 기밀 이미지는 올리지 마세요.

비밀번호와 로그인

비밀번호는 브라우저에서 곧바로 로그인 제공업체인 Supabase Auth로 전달되며, QuoteBill의 코드는 비밀번호를 저장하지 않습니다. Supabase Auth가 비밀번호를 어떻게 보호하는지는 Supabase가 설명합니다. Google로 로그인하면 Google이 Supabase Auth에 이용자가 누구인지 알려 줍니다. QuoteBill은 다음 로그인을 빠르게 하려고 브라우저에 로그인한 방식과 이메일 주소만 기억합니다.

전자계약: 링크, 코드, 세션

서명 링크에는 무작위 32바이트 비밀값이 들어 있습니다. QuoteBill은 그 SHA-256 해시만 저장하며, 링크는 만들어질 때 보낸 사람에게 한 번 보입니다. 비밀값은 주소의 # 기호 뒤에 실려 서버가 남기는 요청 기록에 들어가지 않고, 페이지는 읽은 뒤 주소창에서 지웁니다. 서명자에게 새 링크를 만들면 이전 링크는 즉시 쓸 수 없게 됩니다.

액세스 코드는 32개 기호 중에서 고른 8자이며 솔트를 넣은 bcrypt 해시로만 저장합니다. 틀린 코드를 다섯 번, 열 번 입력하면 15분 동안 링크가 잠기고, 열다섯 번째에는 링크가 폐기됩니다.

서명자의 세션은 무작위 비밀값이며 SHA-256 해시만 저장합니다. 마지막 동작 후 30분, 최대 2시간 동안 유지됩니다.

각 동작에는 네트워크 주소별, 서명자별 한도가 있으며, 주소를 솔트 적용 해시로 바꿔 셉니다. 10분마다 도는 정리 작업이 2시간이 지난 횟수 기록을 삭제합니다.

계약이 종료되면(완료, 거절, 무효 처리, 만료) 링크는 쓸 수 없게 되고, 링크·코드·세션의 해시는 늦어도 30일 안에 삭제됩니다.

전자계약: 감사 기록의 연결

계약서에 대한 모든 동작(발송, 열람, 동의, 서명, 거절, 알림 등)은 하나의 기록으로 남습니다. 기록의 SHA-256 해시는 그 기록의 내용(계약서, 순번, 기록의 종류, 행위자, 데이터베이스 시계의 시각, 네트워크 주소, 브라우저와 세부 내용의 해시)과 바로 앞 기록의 해시를 담습니다. 첫 기록은 계약서의 참조번호에 이어집니다. 마지막 완료 기록의 해시가 서명 확인서에 인쇄되고 확인 페이지에 표시되는 확인서 해시입니다.

서명하는 계약서 본문은 고정되어 정규화한 형태의 SHA-256 해시인 문서 지문에 묶이며, 데이터베이스가 이를 다시 계산합니다. 데이터베이스의 트리거는 기록, 서명, 확인용 기록의 수정, 삭제, 비우기를 거부합니다.

확인 페이지에서 증거 파일을 확인할 수 있습니다. 브라우저가 연결 구조와 서명을 직접 따라가 확인하고, QuoteBill에는 계약서의 참조번호와 문서 지문만 보냅니다.

이것이 보여 주는 것은 증거 파일이 스스로 일관되고 QuoteBill이 보관하는 기록과 맞는다는 점입니다. 보여 주지 않는 것은 누가 서명했는지입니다. QuoteBill은 신원을 확인하지 않습니다. 이 기록의 연결에 서명하는 비밀 키도, 외부 타임스탬프 서비스도 없으므로 시각은 QuoteBill 데이터베이스 시계의 시각입니다. 데이터베이스 관리 권한이 있는 사람이 이론적으로는 트리거를 끄고 기록을 고쳐 해시를 다시 계산할 수 있으며, 그러면 당사자들이 가진 사본과 맞지 않게 됩니다. 그래서 각 당사자는 서명된 사본을 직접 보관해야 합니다.

관리자가 볼 수 있는 것

관리자 페이지에는 관리자 계정이 필요합니다. 호출할 때마다 데이터베이스가 역할과 로그인 세션이 아직 있는지를 확인하고, AI 앱에 발급된 토큰은 거절합니다.

관리자는 지원을 제공하거나 악용을 조사하거나 법적 의무를 이행하기 위해 회원이 클라우드에 저장한 문서를 열 수 있습니다. 열람은 읽기 전용이며, 열 때마다 어느 관리자가 어느 문서를 언제 열었는지가 트리거로 변경이 막힌 기록에 남습니다. 문서로 이어지는 목록에는 소유자의 이메일 주소와 이름, 문서 이름, 번호, 고객, 상태, 합계가 보이며, 기록되는 것은 문서를 여는 동작뿐입니다.

이 화면으로는 전자계약을 열 수 없습니다. 신고된 계약서에 대해서는 제목, 참조번호, 상태, 신고 수, 마지막 신고 사유, 소유자의 이메일 주소만 보이고 계약서 본문이나 서명은 보이지 않으며, 관리자는 발송된 계약서를 중지할 수 있습니다.

회원의 역할이나 문서 한도를 바꾸려면 사유를 적어야 하고 수정할 수 없는 기록에 남으며, 역할은 최고 관리자만 바꿀 수 있습니다. 회원으로 로그인하는 기능은 QuoteBill에 없습니다.

데이터베이스를 운영하는 사람은 기술적으로 저장된 모든 것에 접근할 수 있습니다. 위의 조치는 QuoteBill의 관리자 페이지 사용을 통제하고 기록하는 것이며, 그런 기술적 접근을 다루는 것은 아닙니다.

API 키와 AI 앱

API 키는 qbk_ 뒤에 무작위 32자가 붙은 형태입니다. QuoteBill은 키의 SHA-256 해시와 키를 구분할 수 있도록 앞 8자, 이름, 권한, 날짜를 저장하며, 키 자체는 만들 때 한 번만 보여 줍니다. 활성 키는 한 번에 5개까지 둘 수 있고, 만료는 30일, 90일, 365일 중에서 고르며, 언제든 폐기할 수 있습니다.

키로는 문서 초안을 만들고 저장된 문서를 줄여서 읽을 수 있습니다(고객 정보, 항목, 메모, 합계). 문서를 고치거나 삭제하거나 보내거나 서명하거나, 설정이나 키를 바꾸거나, 전자계약에 접근할 수는 없습니다. 키마다 분당 60회, 하루 5,000회로 제한되며, API 기록에는 요청 방식, 경로, 결과만 남고 내용은 남지 않습니다.

ChatGPT, Claude, Gemini 같은 AI 앱은 이용자의 허락을 받아 로그인하며, Supabase Auth가 발급한 계정의 일반 로그인 토큰을 받습니다. 앱은 이 토큰으로 Supabase Auth가 돌려주는 계정의 이메일 주소, 이름, 사진을 읽을 수 있습니다. MCP 서버는 토큰이 유효한지 Supabase Auth에 물어보고 그 답을 최대 1분 기억하며, 토큰을 다른 곳에 넘기지 않습니다. 도구는 공개 정보와 계산만 씁니다. 데이터베이스는 AI 앱에 발급된 토큰이 저장된 문서, 계약서, 파일, 관리자 기능에 접근하거나 API 키를 만드는 것을 거절합니다.

요청 한도를 위해 MCP 서버는 매분 바뀌는 솔트 적용 단방향 해시를 저장하며, 서버 기록에는 부른 도구, 응답 여부, 앱 이름만 남고 요청 내용은 남지 않습니다.

해시로 저장하는 것과 QuoteBill이 암호화하지 않는 것

QuoteBill의 코드는 저장된 데이터를 암호화하지 않습니다. 사이트는 HTTPS로 제공되고 1년짜리 Strict-Transport-Security 헤더를 보냅니다. 연결과 저장 데이터의 암호화는 호스팅 업체와 데이터베이스 업체가 제공하며, 이 페이지는 그에 관해 위 이상의 주장을 하지 않습니다.

  • 서명 링크, 서명자 세션, API 키: SHA-256 해시로만 저장합니다.
  • 액세스 코드: 솔트를 넣은 bcrypt 해시로만 저장합니다.
  • 방문자 코드: 네트워크 주소와 브라우저 정보를 매일 바뀌는 키로 만든 키 기반 해시(HMAC-SHA-256)로 바꾼 값이며 주소 자체는 저장하지 않습니다.
  • 요청 횟수 기록: 네트워크 주소의 솔트 적용 해시.
  • 초대 기록: 입력된 이메일 주소의 SHA-256 해시.
  • 비밀번호: Supabase Auth가 처리합니다.

브라우저에서의 보호

페이지는 X-Content-Type-Options: nosniff와 엄격한 리퍼러 정책과 함께 전송됩니다. 다른 사이트는 프레임으로 이 페이지를 보여 줄 수 없습니다(X-Frame-Options: DENY와 frame-ancestors none 규칙). 삽입하도록 만든 템플릿과 도구 삽입 화면만 예외입니다.

계약서, 서명, 확인 페이지에는 엄격한 콘텐츠 보안 정책이 적용됩니다. 스크립트는 QuoteBill 자체에서만 불러오고, 프레임이 없으며, QuoteBill과 데이터베이스 제공업체 외에는 연결하지 않습니다. 이 페이지에는 광고, 분석 도구, 방문 집계가 없고, 검색 엔진에 색인되지 않으며, 캐시되지 않습니다.

계정 삭제

설정에서 계정을 삭제할 수 있습니다. QuoteBill은 업로드한 이미지와 계정을 삭제하며, 계정에 연결된 문서, 계약서, API 키, 좋아요, 초대 코드도 함께 삭제됩니다. 남는 것은 개인정보 처리방침에 적혀 있습니다. 완료된 계약서의 확인용 기록, 삭제된 발송 계약서의 최소 기록(일정 기간 보관), 초대 기록입니다.

취약점 신고

취약점을 발견하시면 해당 페이지, 재현 방법, 확인한 내용을 auto0104@gmail.com로 보내 주세요. 다른 사람의 데이터는 포함하지 말고, 공개하기 전에 고칠 시간을 주세요. 같은 주소가 security.txt 파일(/.well-known/security.txt)에도 있습니다.

이 페이지는 보상을 약속하지 않습니다.

개정

최종 수정일: 2026-10-06.

  • 2026-10-06: 이 페이지의 첫 버전입니다.

auto0104@gmail.com