کمترین دسترسی در SQL Server: چه مجوزی برای چه کاری واقعاً لازم است

کمترین دسترسی در SQL Server: چه مجوزی برای چه کاری واقعاً لازم است
در این مقاله می‌خوانید
  1. چرا اهمیت دارد، وقتی «شبکه داخلی است»
  2. فهرست کارها و مجوز لازمشان
  3. تلهٔ ۴۶۲۱: مجوزی که می‌دهید و داده نمی‌شود
  4. دو حساب، نه یکی
  5. چیزهایی که هرگز نباید روشن باشند
  6. گذرواژه‌ها را کجا نگه داریم
  7. ممیزی امروز

در بیشتر سرورهایی که دیده‌ام، سرویس‌ها با حسابی وصل می‌شوند که sysadmin است. نه به این دلیل که لازم بوده، بلکه به این دلیل که یک بار چیزی کار نکرد، کسی sysadmin داد، کار کرد، و هیچ‌کس دیگر برنگشت سراغش.

این مقاله فهرست کارهای متداول است و کمترین مجوزی که هر کدام واقعاً لازم دارند — با کوئری‌های آماده، تا بتوانید همین امروز دسترسی‌های اضافی را پس بگیرید.

چرا اهمیت دارد، وقتی «شبکه داخلی است»

سه دلیل، مستقل از اینکه به آدم‌ها اعتماد دارید یا نه:

  • اشتباه. حسابی که فقط می‌خواند، نمی‌تواند سهواً چیزی را حذف کند. بیشتر خرابی‌های داده‌ای که دیده‌ام از بدخواهی نیامده‌اند، از یک WHERE فراموش‌شده در پنجرهٔ اشتباه آمده‌اند.
  • سرقت اعتبارنامه. رشتهٔ اتصال در فایل تنظیمات اپلیکیشن است؛ هر کسی که به آن سرور وب دسترسی پیدا کند، همان دسترسی را دارد. اگر آن حساب sysadmin باشد، یک آسیب‌پذیری وب به تصاحب کامل دیتابیس تبدیل می‌شود.
  • ردپا. وقتی همه با یک حساب همه‌کاره وصل می‌شوند، «چه کسی این کار را کرد؟» جواب ندارد.

فهرست کارها و مجوز لازمشان

کار کمترین مجوز
خواندن DMVها (پایش) VIEW SERVER STATE
دیدن فهرست دیتابیس‌ها VIEW ANY DATABASE
دیدن متن پروسیجرها و تعاریف VIEW ANY DEFINITION
تاریخچهٔ بکاپ db_datareader روی msdb
گرفتن بکاپ db_backupoperator روی همان دیتابیس
خواندن داده db_datareader — یا بهتر، SELECT روی همان شِما
بازگرداندن در دیتابیس تازه نقش سروری dbcreator
خواندن ERRORLOG EXECUTE روی sys.xp_readerrorlog
مدیریت جاب‌های Agent نقش‌های SQLAgent*Role در msdb

هیچ‌کدام sysadmin نیستند. یک حساب پایش که ۲۴ ساعته وصل است، با سه سطر اول به‌اضافهٔ دو سطر بعدی کارش راه می‌افتد.

تلهٔ ۴۶۲۱: مجوزی که می‌دهید و داده نمی‌شود

این یکی را از تجربهٔ خودم می‌نویسم، چون گران تمام شد. مجوزهای سطح سرور فقط وقتی داده می‌شوند که دیتابیس جاری master باشد. اگر اسکریپت شما در میانه‌اش USE msdb کرده باشد و بعد GRANT VIEW SERVER STATE بزند، خطای ۴۶۲۱ می‌گیرد:

Permissions at the server scope can only be granted when the current database is master

نکتهٔ خطرناک این است که در یک اسکریپت طولانی، بقیهٔ دستورها موفق می‌شوند: لاگین ساخته می‌شود، کاربر ساخته می‌شود، نقش‌ها داده می‌شوند. اسکریپت «تقریباً» موفق به نظر می‌رسد. ولی بی VIEW SERVER STATE، عملاً هیچ DMVی کار نمی‌کند — اتصال برقرار می‌شود و هر کوئری پایشی خالی برمی‌گردد. خرابی‌ای که از بیرون شبیه «کار می‌کند ولی داده نمی‌آید» است و پیدا کردنش وقت می‌برد.

و نکتهٔ ظریف‌تر: ALTER SERVER ROLE از هر دیتابیسی کار می‌کند، ولی GRANT نه. پس ممکن است نقش dbcreator داده شده باشد و مجوز نه — که همین باعث می‌شود اسکریپت موفق به نظر برسد.

USE master;   -- همین یک خط
GO
GRANT VIEW SERVER STATE   TO datamug_svc;
GRANT VIEW ANY DEFINITION TO datamug_svc;
GRANT VIEW ANY DATABASE   TO datamug_svc;
GO

همیشه بعد از اجرای هر اسکریپت دسترسی، نتیجه را بخوانید؛ به «خطا نداد» اعتماد نکنید:

SELECT pr.name, pe.permission_name, pe.state_desc
FROM   sys.server_permissions pe
JOIN   sys.server_principals  pr ON pr.principal_id = pe.grantee_principal_id
WHERE  pr.name = N'datamug_svc';

دو حساب، نه یکی

الگویی که در محصول خودمان به آن رسیدیم و توصیه‌اش می‌کنم: حسابی که همیشه وصل است باید کم‌ترین توان را داشته باشد، و کارهای پرقدرت را به حساب دومی بدهید که فقط گاهی استفاده می‌شود.

در مورد پایش و بکاپ این یعنی: یک حساب برای خواندن و بکاپ گرفتن (بی هیچ نقش سروری)، و یک حساب جدا برای آزمون بازیابی که dbcreator دارد چون باید دیتابیس موقت بسازد. اگر روزی اعتبارنامهٔ حساب اول لو برود، مهاجم نمی‌تواند دیتابیس بسازد یا جایگزین کند.

چیزهایی که هرگز نباید روشن باشند

  • xp_cmdshell. اجرای فرمان ویندوز از داخل SQL. اگر روشن است، یک تزریق SQL به اجرای فرمان روی سرور تبدیل می‌شود. هیچ ابزار پایشی به آن نیاز ندارد؛ اگر ابزاری خواست، همان خودش یک سیگنال است.
  • حساب سرویس SQL به‌عنوان Local System یا Domain Admin. حساب سرویس باید کم‌دسترسی باشد.
  • لاگین sa فعال با گذرواژهٔ ساده. غیرفعالش کنید یا دست‌کم نامش را عوض کنید؛ هر حملهٔ خودکاری اول همین را امتحان می‌کند.
  • عضویت اپلیکیشن در db_owner. برای خواندن و نوشتن لازم نیست.

گذرواژه‌ها را کجا نگه داریم

الگویی که خودمان به آن پایبندیم: در دیتابیس فقط نام متغیر محیطی ذخیره می‌شود، نه مقدارش — یا اگر لازم شد مقدار ذخیره شود، به‌صورت رمزشده با کلیدی که در دیتابیس نیست. خاصیتش این است که یک نسخهٔ دزدیده‌شده از دیتابیس — از بکاپ، از فایل mdf، یا از دسترسی خواندن به جدول — بی آن کلید بی‌ارزش است.

و یک قاعدهٔ ساده که خیلی نقض می‌شود: گذرواژه هرگز به‌عنوان آرگومان خط فرمان داده نشود. در تاریخچهٔ شل می‌ماند و در فهرست فرآیندها برای هر کاربر دیگری روی همان ماشین دیدنی است.

ممیزی امروز

این دو کوئری را اجرا کنید. احتمالاً چیزی پیدا می‌کنید که انتظارش را نداشتید:

-- چه کسانی sysadmin هستند؟
SELECT p.name, p.type_desc, p.is_disabled
FROM   sys.server_role_members m
JOIN   sys.server_principals  r ON r.principal_id = m.role_principal_id
JOIN   sys.server_principals  p ON p.principal_id = m.member_principal_id
WHERE  r.name = N'sysadmin';

-- چه کسانی db_owner هستند، در دیتابیس جاری؟
SELECT dp.name, dp.type_desc
FROM   sys.database_role_members m
JOIN   sys.database_principals r  ON r.principal_id = m.role_principal_id
JOIN   sys.database_principals dp ON dp.principal_id = m.member_principal_id
WHERE  r.name = N'db_owner';

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

یک هفته DBMug را روی سرور خودتان امتحان کنید

لاگ پرشده، دیسک در حال تمام شدن و بکاپ عقب‌افتاده را پیامک می‌کند — و هر بکاپ را با بازگرداندن واقعی می‌آزماید.

درخواست دمو