گرفتن مجوزهای public در دیتابیس اوراکل

مقدمه: اهمیت گرفتن مجوزها از PUBLIC

در پایگاه‌داده‌های اوراکل، نقش "PUBLIC" به تمامی کاربران پایگاه‌داده اشاره دارد، از جمله کاربرانی که دسترسی خاصی ندارند یا مجوزهای مشخصی به آن‌ها اعطا نشده است. این نقش به عنوان روشی راحت برای مدیریت دسترسی‌ها شناخته می‌شود، اما در صورتی که به درستی کنترل نشود، می‌تواند تهدیدات امنیتی قابل توجهی ایجاد کند. بر اساس توصیه‌های مرکز امنیت اینترنت (CIS)، مجوزهای پیش‌فرضی که به "PUBLIC" داده شده‌اند باید گرفته شوند، به‌ویژه زمانی که به پکیج‌های حساس و توابع سیستمی مربوط می‌شوند.

یکی از مشکلات عمده‌ای که ممکن است با رها کردن مجوزهای مربوط به "PUBLIC" ایجاد شود این است که اجازه می‌دهد هر کاربری، چه مخرب و چه غیرمخرب، به توابع سیستم دسترسی پیدا کرده و ممکن است باعث ایجاد مشکلات امنیتی شود. به‌عنوان مثال، پکیج‌های خاصی در اوراکل مانند UTL_FILE، DBMS_JOB، و `DBMS_SCHEDULER وجود دارند که به کاربران این امکان را می‌دهند تا فایل‌ها را در سیستم‌عامل بخوانند و بنویسند، کارهای زمان‌بندی‌شده ایجاد کنند و حتی دستورات SQL را در سطح سیستم اجرا کنند.

بر اساس استانداردهای CIS، باید از اعطای مجوزهای EXECUTE به "PUBLIC" برای پکیج‌های حیاتی جلوگیری کرد. به‌طور خاص، اقدامات زیر باید انجام شود:

  • گرفتن مجوز 'EXECUTE' از "PUBLIC" برای پکیج‌های حساس مانند UTL_FILE، DBMS_SQL، DBMS_RANDOM و DBMS_JOB.
  • محدود کردن دسترسی به پکیج‌های سطح سیستم که می‌توانند عملیات حساسی مانند خواندن/نوشتن فایل، زمان‌بندی کارها یا تعامل با منابع سیستم‌عامل را انجام دهند.

با گرفتن این مجوزها، سازمان‌ها می‌توانند خطر سوءاستفاده را کاهش دهند و اطمینان حاصل کنند که تنها کاربران مجاز با نقش‌ها و مجوزهای خاص قادر به اجرای این عملیات حساس خواهند بود. این یکی از بخش‌های مهم برای حفظ امنیت پایگاه‌داده اوراکل و کاهش سطح حمله به پایگاه‌داده است.

در بخش‌های بعدی، به چگونگی این تغییرات به منظور بهبود وضعیت امنیتی پایگاه‌داده می پردازیم.

مراحل شناسایی مجوزهای موجود:

  1. بررسی مجوزهای EXECUTE برای پکیج‌ها و توابع سیستمی: ابتدا باید بررسی کنیم که کدام پکیج‌ها و توابع سیستم به PUBLIC اعطا شده‌اند. برای این کار، می‌توانیم از جداول و ویوهای سیستم اوراکل استفاده کنیم. دستور زیر می‌تواند به ما نشان دهد که چه پکیج‌ها و توابعی برای PUBLIC مجوز EXECUTE دارند:

 

SELECT PRIVILEGE, TABLE_NAME
FROM DBA_TAB_PRIVS
WHERE GRANTEE = 'PUBLIC'
AND TABLE_NAME IN (
    'UTL_FILE', 'UTL_SMTP', 'UTL_TCP', 'UTL_HTTP', 'DBMS_RANDOM',
    'DBMS_LOB', 'DBMS_SQL', 'DBMS_SYS_SQL', 'DBMS_IJOB', 'DBMS_JOB',
    'DBMS_BACKUP_RESTORE', 'DBMS_OBFUSCATION_TOOLKIT', 'DBMS_LDAP',
    'DBMS_SCHEDULER', 'DBMS_STREAMS_ADM_UTL', 'DBMS_AQADM_SYS',
    'DBMS_STREAMS_RPC', 'DBMS_PRVTAQIM', 'DBMS_ADVISOR', 'DBMS_CRYPTO',
    'DBMS_JAVA', 'DBMS_JAVA_TEST', 'DBMS_XMLGEN', 'DBMS_XMLQUERY',
    'DBMS_FILE_TRANSFER'
);

شناسایی و ذخیره‌سازی اشیای INVALID در پایگاه داده اوراکل

 object های INVALID به object هایی گفته می‌شود که به دلیل تغییرات در وابستگی‌ها یا مشکلات هنگام کامپایل، قادر به اجرا یا عملکرد صحیح نیستند. این object ها ممکن است شامل پکیج‌ها، توابع، پروسیجرها، ویوها، یا تریگرها باشند.

برای شروع، باید تمامی INVALID های پایگاه‌داده شناسایی و در یک جدول ذخیره شوند تا بتوانیم آنها را با object هایی که حاصل کار ما هست، تشخیص دهیم:

CREATE TABLE sys.pre_invalid_objects AS
SELECT * FROM dba_objects WHERE status = 'INVALID';

توضیحات دستور:

  1. CREATE TABLE sys.pre_invalid_objects: این بخش از دستور، یک جدول جدید به نام pre_invalid_objects در اسکیما SYS ایجاد می‌کند. این جدول برای ذخیره‌سازی وضعیت object های INVALID استفاده می‌شود.

  2. SELECT * FROM dba_objects WHERE status = 'INVALID': در این بخش، تمامی رکوردهایی که وضعیت آن‌ها INVALID است از جدول DBA_OBJECTS استخراج می‌شود. 

 

شناسایی و مدیریت مجوزهای PUBLIC در اوراکل

در این بخش از مقاله، قصد داریم با استفاده از یک جدول جدید به نام vahiddb_public_privilege، تمامی پکیج‌هایی که مجوز EXECUTE به PUBLIC داده شده است را شناسایی کنیم.

مراحل ایجاد جدول و ذخیره مجوزها:

  1. ایجاد جدول vahiddb_public_privilege:
    ابتدا یک جدول جدید به نام vahiddb_public_privilege ایجاد می‌کنیم که در آن تمامی پکیج‌ها و توابعی که مجوز EXECUTE به PUBLIC داده شده، ذخیره می‌شوند.

CREATE TABLE sys.vahiddb_public_privilege (privilege VARCHAR2(255));

 

  • این جدول دارای یک ستون به نام privilege است که در آن نام پکیج‌ها و توابع ذخیره می‌شود.

  • وارد کردن داده‌ها به جدول:
    بعد از ایجاد جدول، باید تمامی پکیج‌ها و توابعی که به PUBLIC مجوز EXECUTE داده شده‌اند را در این جدول وارد کنیم. این کار را با استفاده از دستور INSERT انجام می‌دهیم.

INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('UTL_FILE');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('UTL_SMTP');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('UTL_TCP');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('UTL_HTTP');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_RANDOM');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_LOB');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_SQL');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_SYS_SQL');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_IJOB');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_JOB');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_BACKUP_RESTORE');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_OBFUSCATION_TOOLKIT');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_LDAP');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_SCHEDULER');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_STREAMS_ADM_UTL');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_AQADM_SYS');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_STREAMS_RPC');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_PRVTAQIM');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_ADVISOR');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_CRYPTO');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_JAVA');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_JAVA_TEST');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_XMLGEN');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_XMLQUERY');
INSERT INTO sys.vahiddb_public_privilege (privilege) VALUES ('DBMS_FILE_TRANSFER');
COMMIT;

 

 

هدف از این عملیات:
این جدول به‌عنوان یک منبع متمرکز برای شناسایی پکیج‌هایی عمل می‌کند که به PUBLIC مجوز EXECUTE داده شده است. با وارد کردن این اطلاعات، می‌توان به راحتی شناسایی کرد که کدام پکیج‌ها باید از PUBLIC برداشته شوند تا امنیت پایگاه‌داده اوراکل بهبود یابد.

 

دستور ایجاد جدول لاگ برای لغو مجوزها:

در اینجا، دستور SQL برای ایجاد یک جدول به نام revoke_log آورده شده است که اطلاعات مربوط به لغو مجوزها را ثبت می‌کند:

CREATE TABLE sys.revoke_log (
    action_time DATE DEFAULT SYSDATE,          
    action_type VARCHAR2(50),                
    object_name VARCHAR2(100),                
    granted_to VARCHAR2(100),                  
    status VARCHAR2(200)                        
);

توضیح فیلدها:

  1. action_time:
    این فیلد زمان انجام عملیات لغو مجوز را ذخیره می‌کند. به‌طور پیش‌فرض، زمان جاری (SYSDATE) در این فیلد ثبت می‌شود.

  2. action_type:
    این فیلد نوع عملیات را مشخص می‌کند، در این حالت به‌طور خاص عملیات لغو مجوزها (REVOKE) خواهد بود.

  3. object_name:
    این فیلد نام شیء پایگاه‌داده (مثل پکیج یا تابع) که مجوز از آن برداشته شده را ذخیره می‌کند.

  4. granted_to:
    در این فیلد نام کاربر یا نقش (در اینجا PUBLIC) که به آن مجوز داده شده بود، ذخیره می‌شود.

  5. status:
    این فیلد وضعیت عملیات لغو مجوز را ثبت می‌کند. وضعیت می‌تواند شامل عبارت‌هایی مانند SUCCESS (موفقیت‌آمیز) یا پیغام خطا در صورت بروز مشکل در فرآیند لغو مجوز باشد.

هدف از ایجاد این جدول:

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

 

دستور PL/SQL که در اینجا آورده شده است، برای لغو مجوزهای EXECUTE از PUBLIC روی پکیج‌های مختلف پایگاه‌داده اوراکل طراحی شده است.

BEGIN
  FOR rec IN (SELECT privilege FROM sys.vahiddb_public_privilege) LOOP
    BEGIN
      EXECUTE IMMEDIATE 'REVOKE EXECUTE ON ' || rec.privilege || ' FROM PUBLIC';
      INSERT INTO sys.revoke_log (action_type, object_name, granted_to, status)
      VALUES ('REVOKE', rec.privilege, 'PUBLIC', 'SUCCESS');
    EXCEPTION
      WHEN OTHERS THEN
        INSERT INTO sys.revoke_log (action_type, object_name, granted_to, status)
VALUES ('REVOKE', rec.privilege, 'PUBLIC', DBMS_UTILITY.FORMAT_ERROR_STACK);
    END;
  END LOOP;
END;
/

ایجاد یک حلقه (Loop):

  • این حلقه از طریق تمام مجوزهایی که در جدول vahiddb_public_privilege ذخیره شده‌اند عبور می‌کند. هر مجوزی که در این جدول موجود است، به نوبه خود در حلقه بررسی می‌شود.
 
FOR rec IN (SELECT privilege FROM sys.vahiddb_public_privilege) LOOP
  1. لغو مجوز EXECUTE:

    • در داخل حلقه، دستور EXECUTE IMMEDIATE برای لغو مجوز EXECUTE از PUBLIC روی هر یک از پکیج‌ها اجرا می‌شود.
    • نام پکیج به‌صورت داینامیک ساخته شده و دستور REVOKE EXECUTE ON {privilege} FROM PUBLIC به‌طور خودکار برای هر پکیج اجرا می‌شود.
     
    EXECUTE IMMEDIATE 'REVOKE EXECUTE ON ' || rec.privilege || ' FROM PUBLIC';
  2. ثبت لاگ برای عملیات موفق:

    • پس از لغو مجوز، اطلاعات مربوط به عملیات موفق (نام پکیج، زمان انجام عملیات، و وضعیت موفقیت‌آمیز) در جدول revoke_log ثبت می‌شود.
     
    INSERT INTO sys.revoke_log (action_type, object_name, granted_to, status) VALUES ('REVOKE', rec.privilege, 'PUBLIC', 'SUCCESS');
  3. مدیریت استثناها (Exception Handling):

    • در صورتی که خطایی در حین لغو مجوزها رخ دهد، این خطا به‌طور خودکار در بخش EXCEPTION مدیریت می‌شود. در این حالت، پیغام خطای مربوطه از طریق DBMS_UTILITY.FORMAT_ERROR_STACK در جدول revoke_log ثبت می‌شود.
     
    EXCEPTION WHEN OTHERS THEN INSERT INTO sys.revoke_log (action_type, object_name, granted_to, status) VALUES ('REVOKE', rec.privilege, 'PUBLIC', DBMS_UTILITY.FORMAT_ERROR_STACK);

مزایای این عملیات:

  • مستندسازی خودکار: تمامی عملیات لغو مجوز به‌طور خودکار در جدول revoke_log ثبت می‌شود که باعث می‌شود تمامی تغییرات امنیتی به‌صورت دقیق و قابل پیگیری باشند.
  • مدیریت خطا: به کمک بخش مدیریت استثنا، تمامی خطاها به‌صورت دقیق ثبت می‌شوند و در صورت بروز مشکل، می‌توان اطلاعات مورد نیاز برای رفع آن‌ها را به‌راحتی پیدا کرد.
  • امنیت بیشتر: با لغو مجوزهای اضافی از PUBLIC، سطح دسترسی‌های غیرمجاز در پایگاه‌داده کاهش می‌یابد و از امنیت سیستم حفاظت می‌شود.

 

دستور SQL زیر برای بررسی وضعیت عملیات لغو مجوزها از جدول revoke_log استفاده می‌شود. این دستور تعداد عملیات‌های لغو مجوز را بر اساس وضعیت هر یک از آن‌ها دسته‌بندی می‌کند:

SELECT status , count(*) 
FROM sys.revoke_log 
GROUP BY status;

 

حالا برای پر شدن جدول dba_errors از خطاهایی که بعد از compile ایجاد می شود، یکبار دستور زیر را اجرا می کنیم:

 

@?/rdbms/admin/utlrp

حالا با یک پروسیجر به سراغ دادن مجوزهای مورد نیاز می رویم:

CREATE OR REPLACE PROCEDURE grant_privileges_auto AS
    CURSOR privilege_cursor IS
        SELECT DISTINCT privilege
        FROM vahiddb_public_privilege;
    CURSOR error_cursor(privilege_name VARCHAR2) IS
        SELECT DISTINCT owner
        FROM dba_errors
        WHERE UPPER(TEXT) LIKE '%' || privilege_name || '%';
    v_privilege_name VARCHAR2(128);
BEGIN
    FOR priv IN privilege_cursor LOOP
        v_privilege_name := priv.privilege;
        FOR err IN error_cursor(v_privilege_name) LOOP
            BEGIN
                EXECUTE IMMEDIATE 'GRANT EXECUTE ON ' || v_privilege_name || ' TO ' || err.owner;
                DBMS_OUTPUT.put_line('Granted EXECUTE on ' || v_privilege_name || ' to ' || err.owner);
            EXCEPTION
                WHEN OTHERS THEN
                    DBMS_OUTPUT.put_line('Error granting privilege ' || v_privilege_name || ' to ' || err.owner || ': ' || SQLERRM);
            END;
        END LOOP;
    END LOOP;
END grant_privileges_auto;
/

 

 

توضیح پروسیجر grant_privileges_auto برای اعطای مجوزها به‌طور خودکار

در این بخش از مقاله، به بررسی پروسیجر grant_privileges_auto می‌پردازیم که برای اعطای مجوز EXECUTE به کاربران مختلف بر اساس خطاهای موجود در جدول DBA_ERRORS طراحی شده است. این پروسیجر به‌طور خودکار مجوزهای لازم را به کاربران مرتبط با خطاهای مشخص شده در پایگاه‌داده می‌دهد.

مراحل اجرای پروسیجر grant_privileges_auto:

  1. تعریف کرسرها:

    • پروسیجر از دو کرسر استفاده می‌کند:
      • privilege_cursor: این کرسر برای دریافت مجوزهای مختلف از جدول vahiddb_public_privilege طراحی شده است. این جدول شامل پکیج‌ها و توابعی است که مجوز EXECUTE به PUBLIC داده شده است.
      • error_cursor: این کرسر برای بررسی خطاهای مربوط به هر مجوز در جدول DBA_ERRORS طراحی شده است. برای هر مجوز، اگر خطایی وجود داشته باشد، این کرسر نام صاحب شیء را (که معمولاً کاربر یا نقش خاصی است) برمی‌گرداند.
  2. چرخش در مجوزها و بررسی خطاها:

    • پروسیجر ابتدا با استفاده از privilege_cursor، لیست تمامی مجوزهای موجود در جدول vahiddb_public_privilege را دریافت کرده و به‌صورت داینامیک از آن‌ها استفاده می‌کند.
    • سپس برای هر مجوز، با استفاده از error_cursor، بررسی می‌کند که آیا خطایی در رابطه با آن مجوز در جدول DBA_ERRORS وجود دارد. در صورت وجود خطا، نام صاحب شیء که خطا مربوط به آن است، در نظر گرفته می‌شود.
  3. اعطای مجوز به‌صورت داینامیک:

    • اگر خطا پیدا شود، پروسیجر دستور EXECUTE IMMEDIATE را برای اعطای مجوز EXECUTE به صاحب شیء مورد نظر اجرا می‌کند. این عملیات داینامیک است و به‌طور خودکار برای هر خطا انجام می‌شود.
  4. گزارش وضعیت عملیات:

    • پس از هر اعطای مجوز، پروسیجر از دستور DBMS_OUTPUT.put_line برای گزارش وضعیت عملیات استفاده می‌کند. این گزارش شامل نام مجوز، نام صاحب شیء و نتیجه عملیات (که می‌تواند موفقیت‌آمیز یا دارای خطا باشد) است.
  5. مدیریت استثناها:

    • اگر در حین اعطای مجوز خطایی رخ دهد، بخش استثنا (Exception Handling) وارد عمل می‌شود. در این بخش، خطای مربوطه با استفاده از DBMS_UTILITY.FORMAT_ERROR_STACK ثبت و در جدول revoke_log ذخیره می‌شود. این اطلاعات به مدیر پایگاه‌داده کمک می‌کند تا مشکلات احتمالی را بررسی و رفع کند.

من با sqlplus  این دستور رو اجرا میکنم که با زدن دستور زیر، خروجی ها به درستی نمایش داده میشن.

SET SERVEROUTPUT ON;
exec grant_privileges_auto;

می توانیم در این مرحله تعداد object هایی که بعد از عملیات  ما ایجاد شده است را با دستور زیر ببینیم:

SELECT * FROM dba_objects WHERE status = 'INVALID'
 minus select * from pre_invalid_objects;

بعد از اجرای این پروسیجر، یکبار دیگر compile میکنیم:

 

@?/rdbms/admin/utlrp

 

و یکبار دیگه دادن مجوزها را با پروسیجری که نوشته ایم انجام بدهیم:

 

exec grant_privileges_auto;

و مجدد compile:

 

@?/rdbms/admin/utlrp

 

در این حالت در سیستم من invalid ی باقی نمانده بود. در صورتی که پکیجی از کاربران شما هنوز دچار مشکل هست، می توانید تمام مجوزها رو با پروسیجر زیر به آن کاربر بدهید. 

در آینده نیز در صورتی که می دانید هر کاربر چه نیازمندی هایی دارد ، بهتر این است که فقط آن مجوزها به او داده شود و اگر نه تمام مجوزها را می توانید با پروسیجر زیر با دادن نام کاربر بدهید:

 

CREATE OR REPLACE PROCEDURE grant_privileges_to_user(p_username VARCHAR2) AS
    CURSOR privilege_cursor IS
        SELECT privilege
        FROM vahiddb_public_privilege;
    v_privilege_name VARCHAR2(128);
BEGIN
    FOR priv IN privilege_cursor LOOP
        v_privilege_name := priv.privilege;
        BEGIN
            EXECUTE IMMEDIATE 'GRANT EXECUTE ON ' || v_privilege_name || ' TO ' || p_username;
            DBMS_OUTPUT.put_line('Granted EXECUTE on ' || v_privilege_name || ' to ' || p_username);
        EXCEPTION
            WHEN OTHERS THEN
                DBMS_OUTPUT.put_line('Error granting privilege ' || v_privilege_name || ' to ' || p_username || ': ' || SQLERRM);
        END;
    END LOOP;
END grant_privileges_to_user;
/