보안 모범 사례
Ruby API 연동을 안전하게 유지하기 위해 다음 권장 사항을 따르십시오.
API 시크릿 보호
team_api_secret은 요청 서명을 생성하는 데 사용하는 개인 키입니다. 이를 보유한 사람은 누구나 귀하를 대신하여 인증된 API 호출을 할 수 있습니다.
team_api_secret을 절대 다음에 노출하지 마십시오:
- 클라이언트 측 JavaScript 또는 모바일 앱 번들
- 공개 Git 저장소 또는 버전 관리 이력
- 로그 파일, 오류 메시지 또는 분석 이벤트
- URL 또는 쿼리 매개변수
모든 API 호출은 최종 사용자가 시크릿에 접근할 수 없는 서버 측 백엔드에서 발생해야 합니다.
시크릿 안전 저장
자격 증명을 소스 코드 외부에 저장하십시오. 권장 방법:
| 환경 | 권장 방법 |
|---|---|
| 개발 | dotenv 또는 이에 상응하는 도구로 로드하는 .env 파일 (gitignore 처리) |
| 스테이징 / 프로덕션 | 배포 시 주입되는 환경 변수 |
| 클라우드 환경 | 시크릿 매니저 (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault 등) |
예제 — 환경 변수에서 로드 (Python):
import os
API_KEY = os.environ["RUBY_TEAM_API_KEY"]
API_SECRET = os.environ["RUBY_TEAM_API_SECRET"]
예제 — 환경 변수에서 로드 (Node.js):
const API_KEY = process.env.RUBY_TEAM_API_KEY;
const API_SECRET = process.env.RUBY_TEAM_API_SECRET;
키 교체
team_api_key와 team_api_secret을 주기적으로 또는 유출이 의심되는 경우 즉시 교체하십시오.
키 교체를 요청하려면 Ruby 지원팀에 연락하십시오. 교체 과정:
- Ruby에서 새 키 쌍을 발급합니다
- 새 값으로 환경 변수 또는 시크릿 매니저를 업데이트합니다
- 새 자격 증명을 적용하기 위해 서비스를 재배포합니다
- 이전 키가 폐기되었는지 확인합니다
높은 보안이 필요한 환경에서는 교체 주기를 짧게 유지하십시오 (예: 90일마다).
항상 HTTPS 사용
모든 API 요청은 HTTPS를 통해 이루어져야 합니다. Ruby API 서버는 일반 HTTP 연결을 허용하지 않습니다. HTTPS는 다음을 보장합니다:
- 요청 헤더(
X-Team-Key및X-Team-Signature포함)가 전송 중 암호화됩니다 - 서버의 TLS 인증서를 검증하여 중간자 공격을 방지할 수 있습니다
HTTP 클라이언트가 서버 인증서를 검증하고 TLS 검증을 비활성화하지 않도록 하십시오 (예: Python requests의 verify=False 또는 Node.js의 rejectUnauthorized: false를 피하십시오).
시계 동기화
X-Team-Timestamp 헤더는 ±300초 허용 범위로 서버 시간과 대조하여 검증됩니다. 서버 시계가 이 범위를 초과하여 드리프트되면 모든 요청이 401 Unauthorized 오류로 거부됩니다.
NTP(Network Time Protocol)를 사용하여 서버 시계를 동기화하십시오:
# 현재 시간 오프셋 확인 (Linux)
timedatectl status
# NTP 강제 동기화 (systemd 기반 시스템)
sudo systemctl restart systemd-timesyncd
# NTP 활성화 확인
timedatectl show --property=NTPSynchronized
클라우드 호스팅 서버의 경우 NTP가 일반적으로 자동으로 구성됩니다. 활성화되어 실행 중인지 확인하십시오.
상수 시간 서명 비교 사용
심리스 월렛 콜백을 구현하고 Ruby에서 수신되는 서명을 검증해야 하는 경우, 항상 상수 시간 비교 함수를 사용하십시오. 일반 문자열 동등 비교(==)는 공격자가 예상되는 서명을 바이트 단위로 추론할 수 있는 타이밍 공격에 취약합니다.
Python:
import hmac
def verify_signature(expected: str, received: str) -> bool:
return hmac.compare_digest(expected.encode(), received.encode())
Node.js:
const crypto = require("crypto");
function verifySignature(expected, received) {
const a = Buffer.from(expected, "utf8");
const b = Buffer.from(received, "utf8");
if (a.length !== b.length) return false;
return crypto.timingSafeEqual(a, b);
}
Java:
import java.security.MessageDigest;
public static boolean verifySignature(String expected, String received) {
return MessageDigest.isEqual(
expected.getBytes(StandardCharsets.UTF_8),
received.getBytes(StandardCharsets.UTF_8)
);
}
자세한 콜백 서명 검증 가이드는 콜백 인증을 참조하십시오.
요약 체크리스트
-
team_api_secret은 서버 측에만 저장되어 있으며, 클라이언트 코드에는 없음 - 자격 증명은 환경 변수 또는 시크릿 매니저에서 로드됨
- 키 교체가 예정되어 있거나 신속하게 수행할 수 있음
- 모든 API 요청이 인증서 검증이 활성화된 HTTPS를 사용함
- 서버 시계가 NTP로 동기화됨 (주기적으로 확인)
- 콜백 서명 검증이 상수 시간 비교를 사용함