הסכנה הטמונה ב-Ownership Chaining

9 במרץ 2015

אין תגובות

לאחרונה, נתקלתי בעבודה במשמעות האמיתית של Ownership chaining ובסכנת אבטחה שהוא עלול לגרום. נתבקשתי להגדיר login עבור Web Service שמטרתו היחידה היא להריץ פרוצדורה אחת בלבד. הפרוצדורה תבצע שליפה פשוטה של עמודה אחת ויחידה מתוך טבלת עובדים, אשר מכילה גם אינפורמציה ממודרת (כגון משכורת).

לפני שאמשיך בתיאור המקרה, אנא הריצו את הסקריפטים 1 ו-2. הסקריפט הראשון יוצר בסיס נתונים בשם [oChaining] והשני יוצר שתי סכמות (Schemas): [Lists] ו-[operation], שתי טבלאות : טבלת סוגי מינים בשם [list].[Genders] וטבלת עובדים בשם [operation].[workers], אשר לצורך ההמחשה כוללת עמודת שם שאותה מעוניינים להציג ועמודת שכר אותה לא רוצים לחשוף בשום פנים.

סקריפט 1:

Use [master]

GO

 

IF

    DB_ID (N'oChaining') IS NOT NULL

BEGIN

    ALTER DATABASE

        oChaining

    SET

        SINGLE_USER

    WITH

        ROLLBACK IMMEDIATE;

 

    DROP DATABASE

        oChaining;

END;

GO

 

CREATE DATABASE 

     oChaining 

 ON  PRIMARY 

( NAME = N'oChain', 

  FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL12.SQLMADEIRA\MSSQL\DATA\oChain.mdf' ,

  SIZE = 5120KB ,

  FILEGROWTH = 10% )

 LOG ON 

( NAME = N'oChain_log',

  FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL12.SQLMADEIRA\MSSQL\DATA\oChain_log.ldf' ,

  SIZE = 2048KB , FILEGROWTH = 10%)

COLLATE Hebrew_CI_AS

GO

 

ALTER DATABASE 

    oChaining

SET RECOVERY 

    SIMPLE 

סקריפט 2:

Use [oChaining]

GO

 

IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = 'Operation')

BEGIN

    EXEC('CREATE SCHEMA [Operation] AUTHORIZATION [dbo]')

END

 

IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = 'Lists')

BEGIN

    EXEC('CREATE SCHEMA [Lists] AUTHORIZATION [dbo]')

END

 

IF NOT EXISTS 

    (SELECT * 

     FROM sys.objects

     WHERE OBJECT_ID('[Lists].[Genders]') = Object_id AND TYPE = N'U')

BEGIN

    CREATE TABLE

        Lists.Genders

    (

        Id    TINYINT NOT NULL CONSTRAINT pk_Genders_c_Id PRIMARY KEY CLUSTERED ,

        Name    NVARCHAR(50)    NOT NULL

    )

    ON

        [PRIMARY];

END;

GO

 

IF NOT EXISTS 

    (SELECT * 

     FROM sys.objects

     WHERE OBJECT_ID('[operation].[workers]') = Object_id AND TYPE = N'U')

BEGIN

    CREATE TABLE

       [operation].[workers]

    (

        Id        INT                NOT NULL CONSTRAINT    pk_workers_c_Id    PRIMARY KEY CLUSTERED ,

        Name    NVARCHAR(50)    NOT NULL ,

        Gender  TINYINT         NOT NULL ,

        Salary  NUMERIC(7,2)    NOT NULL

    

    )

    ON

        [PRIMARY];

    END;

GO

 

INSERT INTO

    Lists.Genders (Id , Name)

VALUES

   (1, N'Male'),(2,N'Female' )

GO

 

INSERT INTO

    Operation.workers (Id,Name,Gender,Salary)

VALUES

       (1,N'Christine McVie',2,17000.00),(2,N'Stevie Nicks',2,24000.00),

       (3,N'Lindsey Buckingham',1,21000.00),(4,N'John McVie',1,13000.00),

       (5,N'Mick Fleetwood',1,18000.00)

GO

לאחר דיון קצר שערך צוות ה-DBA, הוחלט ליישם את הבקשה ע"י ביצוע השלבים הבאים (ראה סקריפט 3):

  • יצירת LOGIN ו-USER עבורו (שניהם בשם 'Classified')
  • יצירת סכימה בשם 'Classified'.
  • יצירת הפרוצדורה תחת הסכימה.
  • מתן הרשאת הרצה (GRANT EXECUTE) לפרוצדורה עבור משתמש 'Classified'
  • מתן הרשאה לשליפה (GRANT SELECT) לכל העמודות בטבלה פרט לעמודת המשכורת.

סקריפט 3:

USE [master]

GO

 

IF NOT EXISTS  (SELECT name  FROM master.sys.server_principals WHERE name = 'Classified')

BEGIN

    CREATE LOGIN [Classified] WITH PASSWORD=N'Aa123456', DEFAULT_DATABASE=[oChaining], CHECK_EXPIRATION=OFF, CHECK_POLICY=ON

END;

GO

 

Use [oChaining]

GO

 

IF NOT EXISTS 

    (SELECT name  

     FROM sys.database_principals

     WHERE name = 'Classified')

BEGIN

    CREATE USER [Classified] FOR LOGIN [Classified]

END;

GO

 

IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = 'Classified')

BEGIN

    EXEC('CREATE SCHEMA [Classified] AUTHORIZATION [dbo]')

END

/* Stored Procedure */

 

IF NOT EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID('[Classified].[usp_GetMales]')

              AND TYPE IN (N'P', N'PC'))

EXEC(N'CREATE PROCEDURE [Classified].[usp_GetMales] AS RETURN ')

GO

 

ALTER PROCEDURE [Classified].[usp_GetMales]

          @Gender INT

AS

BEGIN 

    SELECT Name --*

    FROM  [Operation].[workers]

    WHERE Gender = @Gender

END;

GO

/* Permissions */

GRANT EXECUTE ON [Classified].[usp_GetMales] TO [classified]

GO

 

GRANT SELECT ON [Operation].[workers] ([Id]) TO [Classified]

GO

GRANT SELECT ON [Operation].[workers] ([name]) TO [Classified]

GO

GRANT SELECT ON [Operation].[workers] ([Gender]) TO [Classified]

GO

הכול נבדק בהצלחה (הרץ את הפרוצדורה כמשתמש 'Classified'), אך אחד מחברי הצוות לא היה בטוח לגמרי לגבי המימוש. משהו הטריד אותו בקשר ל- Ownership Chainingולסכנה מסוימת הכרוכה בו. "מה יקרה אם", הוא שאל, "השאילתה בפרוצדורה תבצע שליפה של כל השדות בטבלת העובדים?" האם כל השדות יוצגו כולל שדה השכר אותו אנו לא רוצים לחשוף בשום אופן?" מסוקרנים, החלטנו לבדוק את חשדותיו ע"י שינוי ה-SELECT בפרוצדורה ל-SELECT * ואופס… אכן כל השדות הוצגו כולל עמודת השכר –לא טוב, אבל ממש לא טוב!

מהו הגורם ל"פריצת" אבטחה זו? ובכן, לאחר חקירה קצרה האשמים נמצאו. הראשון, ה-Ownership Chaining ושני, צוות ה-DBA אשר החליט לשאיר את ה-Owner של סכימה 'Classified' כ-dbo. לפני שאני ממשיך להסבר יותר מפורט של המקרה, אני רוצה להציג הגדרה קצרה של מושג ה- Ownership Chaining. (באמצעות עזרה מ-BOL ותרשים 5):

כאשר מספר אובייקטים בבסיס הנתונים ניגשים אחד לשני באופן סדרתי, הגישה הסדרתית יוצרת מעין שרשרת. כאשר מתבצעת פניה לאובייקט דרך השרשרת, SQL Server מבצע פעולה ראשונית של השוואת ה-Owner-ים של האובייקטים (הקורא והנקרא). אם לשניהם יש את אותו Owner, ההרשאות של האובייקט הנקרא לא מתבצעות.

במקרה שלנו, הפרוצדורה שייכת לסכימה 'Classified' אשר ה-Owner שלה הוא [dbo]. פקודת ה-SELECT בתוך הפרוצדורה היא על טבלת [operation].[workers] וה-Owner של סכימה [operation] הוא גם [dbo]. אי לכך, למרות שנתנו במפורש למשתמש 'Classified' הרשאה רק על חלק מהשדות בטבלה, משתמש זה יוכל לבצע שליפה על כל השדות בטבלה בגלל האופן בו עובד מנגנון ה- Ownership Chaining אשר מדלג על ביצוע בחינת ההרשאות לאובייקט הנקרא. מהו הפתרון ? לשנות את ה-owner של סכימה 'Classified' למשתמש [Classfied] (ראו סקריפט 4), ואין יותר צורך לתת במפורש הרשאת הרצה לפרוצדורה. נסו זאת בעצמכם.

/* Change ownr of Classified schema  */

ALTER AUTHORIZATION ON SCHEMA::[Classified] TO [classified]

GO

 

 

EXEC AS LOGIN = 'Classified'

SELECT SUSER_SNAME()

 

EXEC [Classified].[usp_GetMales] 1

 

REVERT

image

Eyal-Golombekהפוסט נכתב על ידי אייל גולומבק, יועץ בחברת מדירה התמחה ב-Performance Tuning, פיתוח ל-Database, Database Design, והרבה Internals. הוא אוהב לחלוק מהניסיון שלו ומהדברים שבהם הוא נתקל ביום יום.

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *