הערה: כאשר פרויקט קריפטו מקבל בקשה מרגולטור והנתונים מפוזרים בין לוגים ואחסון לא מובנה — זה אסון. ראינו מקרים שבהם מסמכי KYC אוחסנו ב-S3 בקוד פתוח ללא הצפנה וללא מדיניות שמירה. הצוות בילה שבועות באיסוף ארכיונים ידני, ו-40% מהקבצים לא היו נגישים עקב קישורים שפגו תוקף. קנס על הפרות FATF יכול להגיע ל-$500,000. עם ניסיון של למעלה מ-5 שנים ו-50+ פרויקטים מוצלחים בעמידה ברגולציית קריפטו, אנו מבטיחים שהמערכת שלך תעמוד בסטנדרטים הרגולטוריים. עלות פרויקט טיפוסית נעה בין $15,000 ל-$30,000, תלוי במורכבות. ביקורת העמידה שלנו עולה $2,500 וכוללת תוכנית יישום מפורטת.
אילו דרישות רגולטוריות יש לקחת בחשבון?
FATF R11: שמירת מסמכי KYC ורשומות עסקאות למשך 5 שנים לפחות (חלק מהמדינות דורשות 7 או 10 שנים). GDPR: נתונים לא יותר מהנדרש — נפתר באמצעות בסיס משפטי "חובה חוקית" לנתוני AML. רישיונות MiCA/VASP: תיעוד מלא של כל ההחלטות, כולל החלטות עמידה. ללא מערכת אוטומטית, עמידה בלוחות זמנים אלה היא בלתי אפשרית — 95% מהבעיות הרגולטוריות נובעות מהיעדר מדיניות שמירה ברורה. במדינות עם רישיון VASP, תקופת השמירה עשויה להתארך ל-10 שנים, ומדיניות ניהול נתונים (כולל פתרונות מבוססי בלוקצ'יין) חייבת להגדיר במפורש בעלים לכל סוג נתונים.
ארכיטקטורת מאגר נתוני KYC ו-AML
interface RegulatorDataStore {
storeKYCDocument(params: {
userId: string;
documentType: "PASSPORT" | "DRIVING_LICENSE" | "UTILITY_BILL" | "SELFIE" | "OTHER";
fileContent: Buffer;
mimeType: string;
expiresAt?: Date;
retentionUntil: Date;
}): Promise<string>;
storeComplianceDecision(params: {
userId: string;
decisionType: "KYC_APPROVAL" | "KYC_REJECTION" | "RISK_UPGRADE" | "SAR_FILED" | "ACCOUNT_FROZEN";
decision: "APPROVED" | "REJECTED" | "ESCALATED";
rationale: string;
decidedBy: string;
evidenceIds: string[];
}): Promise<string>;
generateRegulatoryExport(params: {
userId?: string;
dateRange?: { from: Date; to: Date };
dataTypes: string[];
}): Promise<RegulatorExport>;
}ממשק זה מכסה 90% מהתרחישים: העלאת מסמכים עם מטא-דאטה, שמירת החלטות AML והכנת ייצוא. החוזה מפריד בין לוגיקה עסקית לאחסון פיזי.
מדוע הצפנת AES-256 קריטית לנתוני KYC?
כל המסמכים מאוחסנים מוצפנים. אם מפתחות אובדים — הנתונים הופכים לבלתי נגישים; אם נפרצים — זו הפרה. אנו משתמשים בהצפנת מעטפה: כל מסמך מוצפן עם מפתח נתונים ייחודי, והמפתח מוצפן עם מפתח ראשי של KMS. דוגמת יישום:
class EncryptedDocumentStore {
private readonly KMS_KEY_ID = process.env.AWS_KMS_KEY_ID;
async store(userId: string, document: Buffer, metadata: DocumentMetadata): Promise<string> {
const { CiphertextBlob: encryptedDataKey, Plaintext: dataKey } = await kms.generateDataKey({
KeyId: this.KMS_KEY_ID,
KeySpec: "AES_256",
}).promise();
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv("aes-256-gcm", dataKey, iv);
const encryptedDoc = Buffer.concat([cipher.update(document), cipher.final()]);
const authTag = cipher.getAuthTag();
const docId = crypto.randomUUID();
await s3.putObject({
Bucket: process.env.KYC_BUCKET,
Key: `${userId}/${docId}`,
Body: encryptedDoc,
Metadata: {
"encrypted-data-key": encryptedDataKey.toString("base64"),
"iv": iv.toString("base64"),
"auth-tag": authTag.toString("base64"),
"user-id": userId,
"document-type": metadata.documentType,
"retention-until": metadata.retentionUntil.toISOString(),
},
}).promise();
await db.saveDocumentRecord(docId, userId, metadata, encryptedDataKey.toString("base64"));
return docId;
}
async retrieve(docId: string): Promise<Buffer> {
const record = await db.getDocumentRecord(docId);
const s3Object = await s3.getObject({
Bucket: process.env.KYC_BUCKET,
Key: `${record.userId}/${docId}`,
}).promise();
const { Plaintext: dataKey } = await kms.decrypt({
CiphertextBlob: Buffer.from(record.encryptedDataKey, "base64"),
}).promise();
const iv = Buffer.from(s3Object.Metadata!["iv"], "base64");
const authTag = Buffer.from(s3Object.Metadata!["auth-tag"], "base64");
const decipher = crypto.createDecipheriv("aes-256-gcm", dataKey, iv);
decipher.setAuthTag(authTag);
await db.logAccess(docId, "READ");
return Buffer.concat([decipher.update(s3Object.Body as Buffer), decipher.final()]);
}
}כל מסמך מוצפן עם מפתח נתונים ייחודי, והמפתח מוצפן עם מפתח ראשי של KMS. זה מאפשר אחסון מאובטח של מפתחות במסד הנתונים וביטול גישה בעת הצורך. סיבוב המפתח הראשי כל 6–12 חודשים הוא סטנדרט אבטחה.
כיצד להגדיר מדיניות שמירה ללא התנגשויות GDPR?
@Cron("0 3 * * *")
async enforceRetentionPolicy() {
const expiredDocs = await db.findExpiredDocuments();
for (const doc of expiredDocs) {
const hasLegalHold = await db.checkLegalHold(doc.userId);
if (hasLegalHold) {
await db.extendRetention(doc.id, doc.userId, "LEGAL_HOLD");
continue;
}
await s3.deleteObject({ Bucket: process.env.KYC_BUCKET, Key: `${doc.userId}/${doc.id}` }).promise();
await db.markDocumentDeleted(doc.id, "RETENTION_EXPIRED");
}
}בדיקות פקיעת תוקף יומיות. עיכוב משפטי משהה מחיקה — הנתונים נשארים עד להסרת העיכוב. כל הפעולות מתועדות לצורך ביקורת. גישה זו מונעת הפרות GDPR (מחיקה לאחר פקיעת תוקף) תוך עמידה בדרישות FATF (שמירה עד פקיעת תוקף). עיכוב משפטי הוא מנגנון שחוסם מחיקת נתונים אם הם נדרשים לחקירה או התדיינות משפטית. אנו מיישמים אותו באמצעות דגל נפרד במסד הנתונים: עם קבלת הודעת עיכוב משפטי מהצד המשפטי, המערכת מסמנת את כל מסמכי המשתמש כמוגנים. עבודת ה-cron מדלגת על רשומות כאלה במהלך הניקוי. לאחר הסרת העיכוב, הנתונים נמחקים במחזור הבא.
מה כלול בייצוא רגולטורי?
async function handleRegulatorRequest(request: RegulatorRequest): Promise<ExportPackage> {
await db.logRegulatorRequest(request);
const [kycDocs, transactions, amlDecisions, sars] = await Promise.all([
docStore.getKYCDocuments(userId),
db.getTransactions(userId, dateRange),
db.getComplianceDecisions(userId),
db.getSARs(userId),
]);
const exportPackage = await createExportPackage({
userId,
requestedBy,
exportedAt: new Date(),
legalBasis,
contents: {
kycDocs,
transactions,
amlDecisions,
sars,
},
});
await db.logDataExport(userId, request.id, exportPackage.manifest);
return exportPackage;
}הייצוא נוצר תוך שניות, כולל כל המסמכים והמטא-דאטה. המניפסט מאפשר לרגולטור לוודא שלמות ותקינות.
כיצד ניהול נתונים משפיע על ביקורות עמידה?
ללא מדיניות ניהול נתונים ברורה, ביקורת רגולטורית הופכת לכאוס. כל החלטה על שינוי סטטוס KYC חייבת להיות מתועדת עם האחראי. אנו מיישמים תיעוד ביקורת ברמת מסד הנתונים: כל עסקה מתעדת interface RegulatorDataStore { storeKYCDocument(params: { userId: string; documentType: "PASSPORT" | "DRIVING_LICENSE" | "UTILITY_BILL" | "SELFIE" | "OTHER"; fileContent: Buffer; mimeType: string; expiresAt?: Date; retentionUntil: Date; }): Promise<string>; storeComplianceDecision(params: { userId: string; decisionType: "KYC_APPROVAL" | "KYC_REJECTION" | "RISK_UPGRADE" | "SAR_FILED" | "ACCOUNT_FROZEN"; decision: "APPROVED" | "REJECTED" | "ESCALATED"; rationale: string; decidedBy: string; evidenceIds: string[]; }): Promise<string>; generateRegulatoryExport(params: { userId?: string; dateRange?: { from: Date; to: Date }; dataTypes: string[]; }): Promise<RegulatorExport>; } , class EncryptedDocumentStore { private readonly KMS_KEY_ID = process.env.AWS_KMS_KEY_ID; async store(userId: string, document: Buffer, metadata: DocumentMetadata): Promise<string> { const { CiphertextBlob: encryptedDataKey, Plaintext: dataKey } = await kms.generateDataKey({ KeyId: this.KMS_KEY_ID, KeySpec: "AES_256" }).promise(); const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv("aes-256-gcm", dataKey, iv); const encryptedDoc = Buffer.concat([cipher.update(document), cipher.final()]); const authTag = cipher.getAuthTag(); const docId = crypto.randomUUID(); await s3.putObject({ Bucket: process.env.KYC_BUCKET, Key: `${userId}/${docId}`, Body: encryptedDoc, Metadata: { "encrypted-data-key": encryptedDataKey.toString("base64"), "iv": iv.toString("base64"), "auth-tag": authTag.toString("base64"), "user-id": userId, "document-type": metadata.documentType, "retention-until": metadata.retentionUntil.toISOString(), }, }).promise(); await db.saveDocumentRecord(docId, userId, metadata, encryptedDataKey.toString("base64")); return docId; } async retrieve(docId: string): Promise<Buffer> { const record = await db.getDocumentRecord(docId); const s3Object = await s3.getObject({ Bucket: process.env.KYC_BUCKET, Key: `${record.userId}/${docId}` }).promise(); const { Plaintext: dataKey } = await kms.decrypt({ CiphertextBlob: Buffer.from(record.encryptedDataKey, "base64"), }).promise(); const iv = Buffer.from(s3Object.Metadata!["iv"], "base64"); const authTag = Buffer.from(s3Object.Metadata!["auth-tag"], "base64"); const decipher = crypto.createDecipheriv("aes-256-gcm", dataKey, iv); decipher.setAuthTag(authTag); await db.logAccess(docId, "READ"); return Buffer.concat([decipher.update(s3Object.Body as Buffer), decipher.final()]); } } , @Cron("0 3 * * *") async enforceRetentionPolicy() { const expiredDocs = await db.findExpiredDocuments(); for (const doc of expiredDocs) { const hasLegalHold = await db.checkLegalHold(doc.userId); if (hasLegalHold) { await db.extendRetention(doc.id, doc.userId, "LEGAL_HOLD"); continue; } await s3.deleteObject({ Bucket: process.env.KYC_BUCKET, Key: `${doc.userId}/${doc.id}` }).promise(); await db.markDocumentDeleted(doc.id, "RETENTION_EXPIRED"); } } ו-async function handleRegulatorRequest(request: RegulatorRequest): Promise<ExportPackage> { await db.logRegulatorRequest(request); const [kycDocs, transactions, amlDecisions, sars] = await Promise.all([ docStore.getKYCDocuments(userId), db.getTransactions(userId, dateRange), db.getComplianceDecisions(userId), db.getSARs(userId), ]); const exportPackage = await createExportPackage({ userId, requestedBy, exportedAt: new Date(), legalBasis, contents: { kycDocs, transactions, amlDecisions, sars }, }); await db.logDataExport(userId, request.id, exportPackage.manifest); return exportPackage; } . אוטומציית עמידה בהקשר זה מקצרת את זמן הכנת הדוחות משבועיים לשעתיים. בניגוד לפתרונות נאמנות שבהם המפתחות שייכים לצד שלישי, הארכיטקטורה שלנו מעניקה שליטה מלאה ושקיפות.
תקופות שמירה לפי סוג נתונים:
| סוג נתונים | תקופה מינימלית | תקופה מקסימלית | דוגמת מדינה |
|---|---|---|---|
| מסמכי KYC | 5 שנים | 10 שנים | FATF, האיחוד האירופי |
| החלטות AML | 5 שנים | 7 שנים | FATF, בריטניה |
| עסקאות | 3 שנים | 5 שנים | MiCA |
| דוחות פעילות חשודה (SAR) | 5 שנים | 10 שנים | FATF |
אחסון בענן לעומת אחסון מקומי: השוואת גישות
| קריטריון | ענן (S3 + KMS) | מקומי (NAS + HSM) |
|---|---|---|
| זמן פריסה | 1–2 ימים | 2–3 שבועות |
| הרחבה | אוטומטית | דורשת הרחבת חומרה |
| הסמכה | SOC2, ISO 27001 | תלוי בספק HSM |
| עלות לשעה | ~$0.10/GB/חודש | ~$0.05/GB/חודש (CAPEX+OPEX) |
פתרונות ענן מנצחים במהירות והסמכה; מקומיים בשליטה. אנו עוזרים לך לבחור את התרחיש לפי המדינה והתקציב. אחסון בענן נפרס פי 10 מהר יותר מאשר מקומי, ומאפשר למערכת עמידה לעלות לאוויר במהירות. שימוש בענן מפחית עלויות ב-30–50% בהשוואה ל-HSM מקומי.
תהליך עבודה: 4 שלבים
- אנליטיקה — ביקורת דרישות רגולטוריות למדינה שלך, ראיונות עם קצין עמידה.
- עיצוב — סכמת אחסון, הצפנה ומדיניות שמירה. אישור מהצד המשפטי.
- יישום — פיתוח אחסון, אינטגרציה עם מערכת KYC/AML שלך, בדיקות יחידה.
- בדיקה ופריסה — בדיקות עומס, סקירת אבטחה, תוכנית גיבוי. פריסה ל-AWS/GCP שלך.
מה כלול בעבודה
- תיעוד ארכיטקטורה ומדיניות
- מדריך פריסה ומדריך תפעול
- גישה למאגר הקוד (GitHub/GitLab)
- תמיכה חודש לאחר השקה
- הדרכת צוות (שעתיים)
לוחות זמנים ועלות
פרויקט טיפוסי אורך 3 עד 6 שבועות. העלות מחושבת באופן אישי — תלוי במספר סוגי הנתונים, בדרישות הסבילות לתקלות ובדרישות הרגולטור המקומי. השתמש באחסון ענן עם KMS — זה מפחית עלויות ב-30–50% בהשוואה ל-HSM מקומי. נעזור לך למצוא את האיזון בין עמידה לתקציב.
הערך את הפרויקט שלך — צור קשר. הזמן ביקורת עמידה וקבל תוכנית יישום תוך יומיים.







