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

در این مقاله میخوانید
در بیشتر سرورهایی که دیدهام، سرویسها با حسابی وصل میشوند که 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 را روی سرور خودتان امتحان کنید
لاگ پرشده، دیسک در حال تمام شدن و بکاپ عقبافتاده را پیامک میکند — و هر بکاپ را با بازگرداندن واقعی میآزماید.
درخواست دمو