UNPKG

@channel.io/channel-talk-integration-mcp

Version:

Channel Talk Integration MCP - Installation guides for Channel Talk SDK across multiple platforms

571 lines (461 loc) 17.6 kB
import { z } from "zod"; export function registerGetMemberHashGuideTool(server) { server.tool("get_member_hash_guide", `case1: 유저가 채널톡 멤버 해시(보안화 설정)에 대해 알고 싶다고 말하면 이 툴을 호출합니다. case2: 보안 강화를 위한 멤버 해시 설정 방법을 안내할 때 이 툴을 호출합니다.`, { language: z .enum(["ko", "en"]) .optional() .describe("Language for the guide. Use 'en' for English, defaults to Korean if not specified"), }, (args) => { try { const language = args.language; const result = language === "en" ? getEnglishMemberHashGuide() : getKoreanMemberHashGuide(); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`Failed to execute get_member_hash_guide: ${message}`); } }); console.log("Registered tool: get_member_hash_guide"); } function getKoreanMemberHashGuide() { return { result: `# 채널톡 멤버 해시(Member Hash) 보안 설정 가이드 채널톡의 멤버 해시 기능을 통해 고객 정보를 안전하게 보호하는 방법을 안내합니다. ## 🔐 멤버 해시 기능이란? 멤버 해시(Member Hash)는 고객의 \`memberId\`를 **HMAC-SHA256**을 이용해 암호화하여 적합한 유저의 부트 요청만 허용할 수 있도록 하는 보안 기능입니다. ### ⚠️ 보안 위험성 **예측 가능한 memberId 사용의 위험:** - 아이디, 이메일과 같은 예측 가능한 값을 \`memberId\`로 사용할 경우 - 제3자가 다른 고객의 \`memberId\`를 쉽게 유추할 수 있음 - 고객의 전화번호, 채팅 기록 등 **민감한 정보 탈취 가능** - 인증되지 않은 사용자가 다른 고객으로 위장하여 부트 가능 **💡 해결책: 멤버 해시 기능 사용을 강력히 권장** --- ## 🚀 멤버 해시 설정 방법 ### 1단계: 시크릿 키 발급 1. **채널 설정 > 보안 및 개발 > 보안** 메뉴로 이동 2. "고객 정보 암호화" 섹션에서 **"신규 발급하기"** 클릭 3. 시크릿 키 발급 완료 ### 2단계: 서버에서 멤버 해시 생성 ${getServerHashingExamples()} ### 3단계: 클라이언트에서 멤버 해시 사용 ${getClientImplementationExamples()} --- ## 🔍 멤버 해시 동작 방식 ### 멤버 해시 기능 **활성화** 시: - **멤버 유저 부트**: \`memberId\`와 \`memberHash\` 모두 필요 - **익명 유저 부트**: \`memberId\`와 \`memberHash\` 값을 비워두고 부트 - 해시 값 검증 실패 시 권한 오류 발생 ### 멤버 해시 기능 **비활성화** 시: - 어떤 검증도 수행하지 않음 - \`memberHash\` 값은 무시됨 --- ## 🛠️ 문제 해결 가이드 ### 주의사항 1. **설치 완료 후 활성화**: 채널톡 SDK 설치 완료 후 멤버 해시 기능 활성화 2. **시크릿 키 보안**: 서버 측에서만 시크릿 키 사용, 클라이언트 노출 금지 3. **시크릿 키 재발급**: 키 노출 시 즉시 재발급 후 재설정 ### 검증 방법 - **채널 설정 > 보안 및 개발 > 고객 정보 암호화**에서 "조회하기" 버튼으로 시크릿 키 확인 - **"해시 검증 테스트하기"**로 멤버 해시 값 검증 가능 - 새로고침 버튼으로 시크릿 키 재발급 가능 ### 지원 범위 ✅ **지원**: SDK 직접 설치한 자체 개발 사이트, 일부 빌더사(Cafe24, 메이크샵, 워드프레스) ❌ **제한적 지원**: 일반적인 빌더사 (연동 방식 차이로 인한 제약) --- ## 📋 구현 체크리스트 1. **시크릿 키 발급**: 채널 데스크에서 시크릿 키 생성 2. **서버 구현**: 백엔드에서 HMAC-SHA256 해싱 로직 구현 3. **클라이언트 연동**: 프론트엔드에서 해시값 받아서 부트에 사용 4. **테스트**: 해시 검증 테스트로 정상 동작 확인 5. **보안 점검**: 시크릿 키 노출 여부 확인 6. **모니터링**: 권한 오류 발생 여부 모니터링 **📚 참고 문서**: https://developers.channel.io/reference/member-hash-kr`, }; } function getEnglishMemberHashGuide() { return { result: `# Channel Talk Member Hash Security Setup Guide This guide explains how to securely protect customer information using Channel Talk's member hash feature. ## 🔐 What is Member Hash? Member Hash is a security feature that encrypts customer \`memberId\` using **HMAC-SHA256** to allow only legitimate user boot requests. ### ⚠️ Security Risks **Risks of using predictable memberId:** - Using predictable values like ID or email as \`memberId\` - Third parties can easily infer other customers' \`memberId\` - Possible theft of **sensitive information** like phone numbers and chat history - Unauthorized users can impersonate other customers and boot **💡 Solution: Strongly recommend using member hash feature** --- ## 🚀 Member Hash Setup Method ### Step 1: Generate Secret Key 1. Go to **Channel Settings > Security & Development > Security** 2. Click **"Generate New"** in the "Customer Information Encryption" section 3. Secret key generation completed ### Step 2: Generate Member Hash on Server ${getServerHashingExamplesEn()} ### Step 3: Use Member Hash on Client ${getClientImplementationExamplesEn()} --- ## 🔍 Member Hash Operation Method ### When Member Hash is **Enabled**: - **Member User Boot**: Both \`memberId\` and \`memberHash\` required - **Anonymous User Boot**: Boot with empty \`memberId\` and \`memberHash\` values - Permission error occurs when hash validation fails ### When Member Hash is **Disabled**: - No validation performed - \`memberHash\` value is ignored --- ## 🛠️ Troubleshooting Guide ### Precautions 1. **Activate After Installation**: Enable member hash feature after Channel Talk SDK installation 2. **Secret Key Security**: Use secret key only on server side, prohibit client exposure 3. **Secret Key Reissuance**: Immediately reissue and reconfigure when key is exposed ### Verification Method - Check secret key with "View" button in **Channel Settings > Security & Development > Customer Information Encryption** - Verify member hash value with **"Test Hash Verification"** - Reissue secret key with refresh button ### Support Range ✅ **Supported**: Self-developed sites with direct SDK installation, some builders (Cafe24, MakeShop, WordPress) ❌ **Limited Support**: General builders (constraints due to integration method differences) --- ## 📋 Implementation Checklist 1. **Generate Secret Key**: Create secret key in Channel Desk 2. **Server Implementation**: Implement HMAC-SHA256 hashing logic in backend 3. **Client Integration**: Use hash value from frontend for boot 4. **Testing**: Verify normal operation with hash verification test 5. **Security Check**: Check for secret key exposure 6. **Monitoring**: Monitor for permission error occurrences **📚 Reference Documentation**: https://developers.channel.io/reference/member-hash-kr`, }; } function getServerHashingExamples() { return `**🔧 서버 측 해싱 구현 예시:** ### JavaScript (Node.js) \`\`\`javascript const crypto = require('crypto'); function generateMemberHash(memberId, secretKey) { const hash = crypto.createHmac('sha256', Buffer.from(secretKey, 'hex')) .update(memberId) .digest('hex'); return hash; } // 사용 예시 const memberId = 'user123'; const secretKey = '4629de5def93d6a2abea6afa9bd5476d9c6cbc04223f9a2f7e517b535dde3e25'; const memberHash = generateMemberHash(memberId, secretKey); \`\`\` ### Python \`\`\`python import hmac import hashlib import binascii def generate_member_hash(member_id, secret_key): hash_value = hmac.new( binascii.unhexlify(bytearray(secret_key, "utf-8")), msg=member_id.encode('utf-8'), digestmod=hashlib.sha256 ).hexdigest() return hash_value # 사용 예시 member_id = "user123" secret_key = "4629de5def93d6a2abea6afa9bd5476d9c6cbc04223f9a2f7e517b535dde3e25" member_hash = generate_member_hash(member_id, secret_key) \`\`\` ### Java \`\`\`java import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; public static String generateMemberHash(String memberId, String secretKey) { try { Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(hexToBytes(secretKey), "HmacSHA256")); byte[] hash = mac.doFinal(memberId.getBytes()); StringBuilder sb = new StringBuilder(hash.length * 2); for (byte b: hash) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (Exception e) { throw new RuntimeException(e); } } private static byte[] hexToBytes(String hex) { return DatatypeConverter.parseHexBinary(hex); } \`\`\` ### PHP \`\`\`php function generateMemberHash($memberId, $secretKey) { return hash_hmac('sha256', $memberId, pack("H*", $secretKey)); } // 사용 예시 $memberId = 'user123'; $secretKey = '4629de5def93d6a2abea6afa9bd5476d9c6cbc04223f9a2f7e517b535dde3e25'; $memberHash = generateMemberHash($memberId, $secretKey); \`\`\``; } function getServerHashingExamplesEn() { return `**🔧 Server-side Hashing Implementation Examples:** ### JavaScript (Node.js) \`\`\`javascript const crypto = require('crypto'); function generateMemberHash(memberId, secretKey) { const hash = crypto.createHmac('sha256', Buffer.from(secretKey, 'hex')) .update(memberId) .digest('hex'); return hash; } // Usage example const memberId = 'user123'; const secretKey = '4629de5def93d6a2abea6afa9bd5476d9c6cbc04223f9a2f7e517b535dde3e25'; const memberHash = generateMemberHash(memberId, secretKey); \`\`\` ### Python \`\`\`python import hmac import hashlib import binascii def generate_member_hash(member_id, secret_key): hash_value = hmac.new( binascii.unhexlify(bytearray(secret_key, "utf-8")), msg=member_id.encode('utf-8'), digestmod=hashlib.sha256 ).hexdigest() return hash_value # Usage example member_id = "user123" secret_key = "4629de5def93d6a2abea6afa9bd5476d9c6cbc04223f9a2f7e517b535dde3e25" member_hash = generate_member_hash(member_id, secret_key) \`\`\` ### Java \`\`\`java import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; public static String generateMemberHash(String memberId, String secretKey) { try { Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(hexToBytes(secretKey), "HmacSHA256")); byte[] hash = mac.doFinal(memberId.getBytes()); StringBuilder sb = new StringBuilder(hash.length * 2); for (byte b: hash) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (Exception e) { throw new RuntimeException(e); } } private static byte[] hexToBytes(String hex) { return DatatypeConverter.parseHexBinary(hex); } \`\`\` ### PHP \`\`\`php function generateMemberHash($memberId, $secretKey) { return hash_hmac('sha256', $memberId, pack("H*", $secretKey)); } // Usage example $memberId = 'user123'; $secretKey = '4629de5def93d6a2abea6afa9bd5476d9c6cbc04223f9a2f7e517b535dde3e25'; $memberHash = generateMemberHash($memberId, $secretKey); \`\`\``; } function getClientImplementationExamples() { return `**🌐 클라이언트 구현 예시:** ### 웹 (JavaScript SDK) \`\`\`javascript // 서버에서 멤버 해시를 받아와서 사용 async function bootChannelTalkWithHash(userId) { try { // 서버 API 호출하여 멤버 해시 획득 const response = await fetch('/api/get-member-hash', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId: userId }) }); const { memberHash } = await response.json(); // 멤버 해시와 함께 부트 ChannelIO('boot', { "pluginKey": "YOUR_PLUGIN_KEY", "memberId": userId, "memberHash": memberHash, "profile": { "name": "사용자명", "mobileNumber": "010-1234-5678" } }); } catch (error) { console.error('Channel Talk boot failed:', error); } } \`\`\` ### React 예시 \`\`\`javascript import { useEffect, useState } from 'react'; import * as ChannelService from '@channel.io/channel-web-sdk-loader'; function useSecureChannelTalk(user) { const [isLoaded, setIsLoaded] = useState(false); useEffect(() => { if (!user) { // 익명 유저로 부트 ChannelService.shutdown(); ChannelService.boot({ pluginKey: "YOUR_PLUGIN_KEY" }); return; } // 멤버 유저로 부트 (멤버 해시 포함) const bootWithMemberHash = async () => { try { const response = await fetch('/api/get-member-hash', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId: user.id }) }); const { memberHash } = await response.json(); ChannelService.shutdown(); ChannelService.boot({ pluginKey: "YOUR_PLUGIN_KEY", memberId: user.id, memberHash: memberHash, profile: { name: user.name, mobileNumber: user.phone, email: user.email } }); } catch (error) { console.error('Failed to boot with member hash:', error); } }; bootWithMemberHash(); }, [user]); } \`\`\` ### 서버 API 엔드포인트 예시 (Express.js) \`\`\`javascript const express = require('express'); const crypto = require('crypto'); const app = express(); app.post('/api/get-member-hash', (req, res) => { const { userId } = req.body; // 환경변수에서 시크릿 키 가져오기 (보안상 중요!) const secretKey = process.env.CHANNEL_TALK_SECRET_KEY; if (!secretKey) { return res.status(500).json({ error: 'Secret key not configured' }); } // 멤버 해시 생성 const memberHash = crypto.createHmac('sha256', Buffer.from(secretKey, 'hex')) .update(userId) .digest('hex'); res.json({ memberHash }); }); \`\`\``; } function getClientImplementationExamplesEn() { return `**🌐 Client Implementation Examples:** ### Web (JavaScript SDK) \`\`\`javascript // Get member hash from server and use it async function bootChannelTalkWithHash(userId) { try { // Call server API to get member hash const response = await fetch('/api/get-member-hash', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId: userId }) }); const { memberHash } = await response.json(); // Boot with member hash ChannelIO('boot', { "pluginKey": "YOUR_PLUGIN_KEY", "memberId": userId, "memberHash": memberHash, "profile": { "name": "User Name", "mobileNumber": "010-1234-5678" } }); } catch (error) { console.error('Channel Talk boot failed:', error); } } \`\`\` ### React Example \`\`\`javascript import { useEffect, useState } from 'react'; import * as ChannelService from '@channel.io/channel-web-sdk-loader'; function useSecureChannelTalk(user) { const [isLoaded, setIsLoaded] = useState(false); useEffect(() => { if (!user) { // Boot as anonymous user ChannelService.shutdown(); ChannelService.boot({ pluginKey: "YOUR_PLUGIN_KEY" }); return; } // Boot as member user (with member hash) const bootWithMemberHash = async () => { try { const response = await fetch('/api/get-member-hash', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId: user.id }) }); const { memberHash } = await response.json(); ChannelService.shutdown(); ChannelService.boot({ pluginKey: "YOUR_PLUGIN_KEY", memberId: user.id, memberHash: memberHash, profile: { name: user.name, mobileNumber: user.phone, email: user.email } }); } catch (error) { console.error('Failed to boot with member hash:', error); } }; bootWithMemberHash(); }, [user]); } \`\`\` ### Server API Endpoint Example (Express.js) \`\`\`javascript const express = require('express'); const crypto = require('crypto'); const app = express(); app.post('/api/get-member-hash', (req, res) => { const { userId } = req.body; // Get secret key from environment variables (important for security!) const secretKey = process.env.CHANNEL_TALK_SECRET_KEY; if (!secretKey) { return res.status(500).json({ error: 'Secret key not configured' }); } // Generate member hash const memberHash = crypto.createHmac('sha256', Buffer.from(secretKey, 'hex')) .update(userId) .digest('hex'); res.json({ memberHash }); }); \`\`\``; } //# sourceMappingURL=get-member-hash-guide.tool.js.map