다크 모드
공개 API · 웹훅
사내 대시보드·BI·티켓 시스템에서 ISMS Doctor 의 준비 현황을 가져오거나(API), 이벤트를 받아 자동화(웹훅)할 수 있습니다.
API 키
설정 › 조직 › 공개 API 키 에서 조직 관리자가 발급합니다.
- 키 원문(
isdk_…)은 발급 직후 한 번만 표시됩니다. 서버에는 해시만 저장하므로 다시 볼 수 없습니다. - 유효 기간(30·90·180·365일)이 반드시 있고, 조직당 활성 키는 10개까지입니다.
- 권한 범위는 읽기 전용입니다 — 키로는 어떤 데이터도 바꿀 수 없습니다.
| 범위 | 허용하는 엔드포인트 |
|---|---|
summary:read | GET /api/v1/summary |
controls:read | GET /api/v1/controls |
action-items:read | GET /api/v1/action-items |
GET /api/v1/org 는 유효한 키면 범위와 관계없이 호출할 수 있습니다.
호출 방법
bash
curl -sS https://<도메인>/api/v1/summary \
-H "Authorization: Bearer $ISMS_DOCTOR_API_KEY"응답은 앱과 같은 형식 { "data": …, "error": null, "message": "OK" } 입니다.
| 엔드포인트 | 설명 | 쿼리 |
|---|---|---|
GET /api/v1/org | 조직 이름·인증 유형·카탈로그·최근/다음 스캔 시각 | — |
GET /api/v1/summary | 통제 상태별 개수(대시보드와 같은 집계) | — |
GET /api/v1/controls | 통제별 상태·증적 수·세부점검 상태 | status(READY · EVIDENCE_NEEDED · REMEDIATING · FAIL · UNKNOWN · NOT_APPLICABLE), area(예: 2 또는 2.5) |
GET /api/v1/action-items | 할 일 목록 (최신순) | status(OPEN · IN_PROGRESS · DONE), controlCode, limit(1~200, 기본 50), cursor |
할 일 목록은 커서 방식입니다. 응답의 nextCursor 를 다음 요청의 cursor 로 넘기고, null 이면 마지막 페이지입니다. 알 수 없는 커서(오타 · 다른 조직의 값 · 인증 유형 전환 전에 받은 값)는 빈 페이지 대신 400 INVALID_CURSOR 를 돌려주니, 첫 페이지부터 다시 조회하세요.
한도와 오류
- 키마다 시간당 600회, IP 마다 분당 120회. 넘으면
429. 401— 키 형식이 틀렸거나, 회수·만료된 키이거나, 키를 발급한 관리자가 조직을 떠났거나 관리자 권한을 잃었습니다 (사유는 구분하지 않습니다). 마지막 경우 설정 화면에 "발급자 권한 없음" 으로 표시되며, 현재 관리자가 새로 발급해야 합니다.- 키 호출은 조직 감사 로그에
api-key.access로 남습니다 (성공은 키·경로당 1분에 1건, 실패는 매번). 403 INSUFFICIENT_SCOPE— 키에 해당 범위가 없습니다.409 DEMO_MODE— 조직이 데모 데이터 모드입니다. 가상 데이터가 외부로 나가지 않도록 실데이터로 전환한 뒤 호출할 수 있습니다.
웹훅 (이벤트 수신)
설정 › 알림 › 알림 채널 에서 "범용 웹훅(Generic Webhook)" 채널을 추가하면, 알림으로 설정한 이벤트(스캔 완료 · 새 부적합 · 증적 만료 임박 · 할 일 기한 · 인증 마일스톤 등)가 JSON 으로 전송됩니다.
json
{
"event": "new_fail",
"orgId": "…",
"title": "…",
"text": "…",
"url": "https://<도메인>/controls/2.5.1",
"occurredAt": "2026-09-27T00:00:00.000Z"
}헤더 X-ISMSDoctor-Event 에 이벤트 이름이, X-ISMSDoctor-Signature 에 t=<발송 시각 unix 초>,v1=<서명 hex> 가 담깁니다. v1 은 HMAC-SHA256(서명 시크릿, "<t>.<원문 본문>") — 발송 시각 t 와 본문을 . 으로 이어 붙인 문자열의 HMAC 입니다. 서명 시크릿은 채널을 만들 때(또는 재발급할 때) 한 번만 표시됩니다.
수신 측은 다음을 모두 확인하세요.
- 헤더에서
t와v1을 꺼냅니다. t가 수신 시각과 5분 넘게 차이 나면 거절합니다 — 가로챈 요청을 나중에 다시 보내는 재전송(replay) 을 막습니다.- JSON 을 파싱·재직렬화하지 말고 원문 본문 그대로
"<t>.<원문 본문>"의 HMAC 을 계산해v1과 상수 시간 비교합니다.t가 서명에 포함되므로t만 바꿔 재전송하면 검증에 실패합니다.
js
import crypto from 'node:crypto';
const TOLERANCE_SECONDS = 5 * 60;
function verify(rawBody, signatureHeader, secret, now = Date.now()) {
const parts = Object.fromEntries(
(signatureHeader ?? '').split(',').map((kv) => {
const i = kv.indexOf('=');
return [kv.slice(0, i).trim(), kv.slice(i + 1).trim()];
}),
);
const t = Number(parts.t);
if (!Number.isInteger(t) || Math.abs(now / 1000 - t) > TOLERANCE_SECONDS) return false; // 재전송·시계 오류
const expected = crypto.createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest();
const received = Buffer.from(parts.v1 ?? '', 'hex');
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}수신 서버의 시계가 크게 틀리면 정상 요청도 거절되니 NTP 로 맞춰 두세요.
웹훅 대상 주소는 공인 IP 여야 합니다 (사설·루프백·예약·문서용 등 공인이 아닌 대역은 발송 시점에 차단됩니다).