Database Engineering

ایجاد ردپای حسابرسی جنایی با PostgreSQL

در عرصه مهندسی پایگاه داده، اعتماد یک انتخاب نیست؛ بلکه پایه و اساس آن است. هنگام کار با تراکنش‌های مالی، سوابق پزشکی یا اسناد حقوقی، توانایی اثبات دقیق اینکه چه چیزی، چه زمانی و توسط چه کسی رخ داده است، حیاتی است. اینجاست که ردپاهای حسابرسی درجه جنایی وارد میدان می‌شوند. روش‌های ثبت لاگ سنتی اغلب ناکافی هستند زیرا لاگ‌ها قابل تغییرند و به راحتی دستکاری می‌شوند. برای دستیابی به یکپارچگی واقعی، باید یک مدل منبع‌گذاری رویدادهای غیرقابل تغییر را که توسط یک پایگاه داده رابطه‌ای قدرتمند مانند PostgreSQL پشتیبانی می‌شود، اتخاذ کنیم.

این مقاله بررسی می‌کند که چگونه می‌توان یک انبار رویدادهای غیرقابل تغییر را با استفاده از PostgreSQL پیاده‌سازی کرد، به طوری که داده‌های حسابرسی شما قابل ردیابی تغییرات باشند و با استانداردهای نظارتی سخت‌گیرانه مطابقت داشته باشند.

چرا غیرقابل تغییر بودن برای انطباق مهم است

چارچوب‌های انطباق مانند GDPR، HIPAA و SOC 2 از سازمان‌ها می‌خواهند که یکپارچگی داده‌ها را حفظ کنند. یک عملیات به‌روزرسانی یا حذف استاندارد روی یک لاگ حسابرسی می‌تواند به عنوان یک تلاش برای پنهان‌کاری تعبیر شود، حتی اگر یک خطای اداری بوده باشد. با غیرقابل تغییر کردن ردپای حسابرسی، ما تضمین می‌کنیم که یک رویداد پس از ثبت، نمی‌تواند تغییر یا حذف شود. این کار یک «زنجیره نگهداری» برای تغییرات داده ایجاد می‌کند و به توسعه‌دهندگان و حسابرس‌ها یک منبع حقیقت واحد ارائه می‌دهد.

پیاده‌سازی این مورد در PostgreSQL نیازمند تغییر رویکرد از عملیات سنتی CRUD (ایجاد، خواندن، به‌روزرسانی، حذف) به یک استراتژی فقط-افزودنی (Append-Only) است. ما از ویژگی‌های PostgreSQL مانند تریگرهای AFTER و جداول UNLOGGED با دقت استفاده خواهیم کرد، اما برای اهداف جنایی، به جداول استاندارد و بادوام با محدودیت‌های سخت‌گیرانه پایبند می‌مانیم.

طراحی طرحواره انبار رویدادها

هسته اصلی راهکار ما یک جدول اختصاصی برای ذخیره رویدادها است. هر رویداد نشان‌دهنده یک تغییر وضعیت در منطق کسب‌وکار است. برای اطمینان از قابلیت ردیابی، هر رکورد باید شامل یک شناسه یکتا، یک مهر زمانی، کاربر یا سیستم مسئول، و محتوای تغییر باشد.

CREATE TABLE audit_events (
    event_id BIGSERIAL PRIMARY KEY,
    timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    user_id UUID NOT NULL,
    action VARCHAR(50) NOT NULL,
    entity_type VARCHAR(50) NOT NULL,
    entity_id VARCHAR(100) NOT NULL,
    old_state JSONB,
    new_state JSONB,
    metadata JSONB DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- اطمینان از اینکه هیچ به‌روزرسانی یا حذفی روی این جدول امکان‌پذیر نیست
ALTER TABLE audit_events 
  DROP CONSTRAINT IF EXISTS audit_events_pkey,
  ADD CONSTRAINT audit_events_pkey PRIMARY KEY (event_id);

-- غیرفعال کردن تمام مجوزهای به‌روزرسانی و حذف برای نقش‌های استاندارد
REVOKE UPDATE, DELETE ON audit_events FROM PUBLIC;
GRANT INSERT, SELECT ON audit_events TO app_user;

با لغو مجوزهای UPDATE و DELETE در سطح پایگاه داده، ما غیرقابل تغییر بودن را حتی اگر منطق برنامه شکست بخورد، اعمال می‌کنیم. این یک استراتژی دفاع در عمق است که برای یکپارچگی جنایی حیاتی است.

پیاده‌سازی تریگر حسابرسی

به جای نوشتن دستی دستورات درج برای هر رویداد حسابرسی، می‌توانیم از تریگرهای PostgreSQL برای ثبت خودکار تغییرات استفاده کنیم. این کار کدهای تکراری را کاهش داده و خطای انسانی را به حداقل می‌رساند. به عنوان مثال، اگر کاربر رکوردی را در جدول users به‌روزرسانی کند، یک تریگر می‌تواند به طور خودکار فعال شود و وضعیت‌های قدیمی و جدید را ثبت کند.

CREATE OR REPLACE FUNCTION capture_user_changes()
RETURNS TRIGGER AS $$
BEGIN
    IF (TG_OP = 'UPDATE') THEN
        INSERT INTO audit_events (user_id, action, entity_type, entity_id, old_state, new_state)
        VALUES (
            NEW.updated_by,
            'UPDATE',
            'users',
            NEW.id::TEXT,
            row_to_json(OLD)::jsonb,
            row_to_json(NEW)::jsonb
        );
        RETURN NEW;
    ELSIF (TG_OP = 'DELETE') THEN
        INSERT INTO audit_events (user_id, action, entity_type, entity_id, old_state)
        VALUES (
            NEW.updated_by,
            'DELETE',
            'users',
            OLD.id::TEXT,
            row_to_json(OLD)::jsonb
        );
        RETURN OLD;
    END IF;
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER audit_user_changes
    AFTER UPDATE OR DELETE ON users
    FOR EACH ROW
    EXECUTE FUNCTION capture_user_changes();

پرس‌وجو در لاگ غیرقابل تغییر

یکی از نقاط قوت PostgreSQL پشتیبانی از JSONB است. ما می‌توانیم ردپای حسابرسی خود را برای تغییرات خاص به طور کارآمد پرس‌وجو کنیم بدون اینکه نیاز به اسکن کل سطرها باشد. به عنوان مثال، پیدا کردن تمام تغییرات یک فیلد خاص ساده است:

SELECT *
FROM audit_events
WHERE entity_type = 'users'
  AND entity_id = '123e4567-e89b-12d3-a456-426614174000'
  AND new_state->>'email' != old_state->>'email';

این پرس‌وجو به افسران انطباق اجازه می‌دهد تا به سرعت تشخیص دهند که آدرس ایمیل دقیقاً چه زمانی، توسط چه کسی و چه مقدار قبلی آن تغییر کرده است.

نتیجه‌گیری

ساخت ردپاهای حسابرسی درجه جنایی تنها درباره ذخیره داده‌ها نیست؛ بلکه درباره ایجاد اعتماد است. با ترکیب قابلیت‌های تراکنشی قدرتمند PostgreSQL با الگوی منبع‌گذاری رویدادهای غیرقابل تغییر، توسعه‌دهندگان می‌توانند سیستم‌هایی ایجاد کنند که در برابر دستکاری مقاوم باشند و با سخت‌گیرانه‌ترین مقررات مطابقت داشته باشند. اگرچه این رویکرد پیچیدگی عملیات نوشتن را افزایش می‌دهد، اما آرامش خیالی که برای امنیت و انطباق فراهم می‌کند، بی‌نظیر است. با افزایش نگرانی‌های مربوط به حریم خصوصی داده‌ها، لاگ‌های غیرقابل تغییر به یک نیاز استاندارد تبدیل خواهند شد، نه صرفاً یک بهترین روش.

Share: