مشکلات مرتبط رو اینجا گزارش کنید.
پاسخ به بازخوردهای پروژهها
تکرار هدر گروه بندی اطلاعات در هر صفحه
نحوه تعریف این هدر و گزارش قبلا به چه صورتی بوده؟
بازخوردهای پروژهها
تهیه گزارش سرفصل های حسابداری
با سلام آیا امکان تهیه گزارش بدین صورت یا شبیه به این وجود دارد؟
با تشکر.
اشتراکها
تغییر مقررات whois در ایرنیک (IRNIC)
نظرات مطالب
ASP.NET MVC #17
- درخواست تحت مجوز و مشخصات کامل شخص لاگین کرده اجرا میشه.
- مرتبط به زمینه فعالیت سایت ما نیست و چنین لینکهایی اینجا گذاشته نخواهد شد.
- مرتبط به زمینه فعالیت سایت ما نیست و چنین لینکهایی اینجا گذاشته نخواهد شد.
من فایلهای سایت جاری رو در مسیر استاندارد app_data ذخیره سازی میکنم. علت هم این است که این پوشه، جزو پوشههای محافظت شدهی ASP.NET است و کسی نمیتواند فایلی را مستقیما از آن دریافت و یا سبب اجرای آن با فراخوانی مسیر مرتبط در مرورگر شود.
این مساله تا به اینجا یک مزیت مهم را به همراه دارد: اگر شخصی مثلا فایل shell.aspx را در این پوشه ارسال کند، از طریق مرورگر قابل اجرا و دسترسی نخواهد بود و کسی نخواهد توانست به این طریق به سایت و سرور دسترسی پیدا کند.
برای ارائه این نوع فایلها به کاربر، معمولا از روش خواندن محتوای آنها و سپس flush این محتوا در مرورگر کاربر استفاده میشود. برای نمونه اگر به لینکهای سایت دقت کرده باشید مثلا لینکهای تصاویر آن به این شکل است:
File هم در اینجا کنترلر فایل است که نام فایل را دریافت کرده و سپس به کمک FilePathResult و return File آنرا به کاربر ارائه خواهد داد.
تا اینجا همه چیز طبیعی به نظر میرسد. اما ... مورد ذیل چطور؟!
لاگ خطاهای فوق مرتبط است به سعی و خطای شب گذشته یکی از دوستان جهت دریافت فایل web.config برنامه!
متدهای Server.MapPath یا متد return File و امثال آن تمامی به کاراکتر ویژه ~ (اشارهگر به ریشه سایت) به خوبی پاسخ میدهند. به عبارتی اگر این بررسی امنیتی انجام نشده باشد که کاربر چه مسیری را درخواست میکند، محتوای کامل فایل web.config برنامه به سادگی قابل دریافت خواهد بود (به علاوه هر آنچه که در سرور موجود است).
چطور میشود با این نوع حملات مقابله کرد؟
دو کار الزامی ذیل حتما باید انجام شوند:
الف) با استفاده از متد Path.GetFileName نام فایل را از کاربر دریافت کنید. به این ترتیب تمام زواید وارد شده حذف گردیده و فقط نام فایل به متدهای مرتبط ارسال میشود.
ب) بررسی کنید مسیری که قرار است به کاربر ارائه شود به کجا ختم شده. آیا به c:\windows اشاره میکند یا مثلا به c:\myapp\app_data ؟
اگر به لاگ فوق دقت کرده باشید تا چند سطح بالاتر از ریشه سایت هم جستجو شده.
نتیجه گیری:
اگر در برنامههای وب خود (فرقی نمیکند مرتبط به چه فناوری است)، نام فایلی را از کاربر جهت ارائه محتوایی به او دریافت و از این نام فایل بدون هیچ نوع بررسی خاصی، مستقیما در برنامه استفاده میکنید، برنامه شما به مشکل امنیتی Directory Traversal مبتلا است.
پ.ن.
1- این باگ امنیتی در سایت وجود داشت که توسط یکی از دوستان در روزهای اول آن گزارش شد؛ ضمن تشکر!
2- از این نوع اسکنها در لاگهای خطاهای سایت جاری زیاد است. برای مثال به دنبال فایلهایی مانند DynamicStyle.aspx و css.ashx یا theme.ashx میگردند. حدس من این است که در یکی از پرتالهای معروف یا افزونههای این نوع پرتالها فایلهای یاد شده دارای باگ فوق هستند. فایلهای ashx عموما برای flush یک فایل یا محتوا به درون مرورگر کاربر در برنامههای ASP.NET Web forms مورد استفاده قرار میگیرند.
این مساله تا به اینجا یک مزیت مهم را به همراه دارد: اگر شخصی مثلا فایل shell.aspx را در این پوشه ارسال کند، از طریق مرورگر قابل اجرا و دسترسی نخواهد بود و کسی نخواهد توانست به این طریق به سایت و سرور دسترسی پیدا کند.
برای ارائه این نوع فایلها به کاربر، معمولا از روش خواندن محتوای آنها و سپس flush این محتوا در مرورگر کاربر استفاده میشود. برای نمونه اگر به لینکهای سایت دقت کرده باشید مثلا لینکهای تصاویر آن به این شکل است:
http://site/file?name=image.png
Image.png نام فایلی است در یکی از پوشههای قرار گرفته شده در مسیر app_data.File هم در اینجا کنترلر فایل است که نام فایل را دریافت کرده و سپس به کمک FilePathResult و return File آنرا به کاربر ارائه خواهد داد.
تا اینجا همه چیز طبیعی به نظر میرسد. اما ... مورد ذیل چطور؟!
لاگ خطاهای فوق مرتبط است به سعی و خطای شب گذشته یکی از دوستان جهت دریافت فایل web.config برنامه!
متدهای Server.MapPath یا متد return File و امثال آن تمامی به کاراکتر ویژه ~ (اشارهگر به ریشه سایت) به خوبی پاسخ میدهند. به عبارتی اگر این بررسی امنیتی انجام نشده باشد که کاربر چه مسیری را درخواست میکند، محتوای کامل فایل web.config برنامه به سادگی قابل دریافت خواهد بود (به علاوه هر آنچه که در سرور موجود است).
چطور میشود با این نوع حملات مقابله کرد؟
دو کار الزامی ذیل حتما باید انجام شوند:
الف) با استفاده از متد Path.GetFileName نام فایل را از کاربر دریافت کنید. به این ترتیب تمام زواید وارد شده حذف گردیده و فقط نام فایل به متدهای مرتبط ارسال میشود.
ب) بررسی کنید مسیری که قرار است به کاربر ارائه شود به کجا ختم شده. آیا به c:\windows اشاره میکند یا مثلا به c:\myapp\app_data ؟
اگر به لاگ فوق دقت کرده باشید تا چند سطح بالاتر از ریشه سایت هم جستجو شده.
نتیجه گیری:
اگر در برنامههای وب خود (فرقی نمیکند مرتبط به چه فناوری است)، نام فایلی را از کاربر جهت ارائه محتوایی به او دریافت و از این نام فایل بدون هیچ نوع بررسی خاصی، مستقیما در برنامه استفاده میکنید، برنامه شما به مشکل امنیتی Directory Traversal مبتلا است.
پ.ن.
1- این باگ امنیتی در سایت وجود داشت که توسط یکی از دوستان در روزهای اول آن گزارش شد؛ ضمن تشکر!
2- از این نوع اسکنها در لاگهای خطاهای سایت جاری زیاد است. برای مثال به دنبال فایلهایی مانند DynamicStyle.aspx و css.ashx یا theme.ashx میگردند. حدس من این است که در یکی از پرتالهای معروف یا افزونههای این نوع پرتالها فایلهای یاد شده دارای باگ فوق هستند. فایلهای ashx عموما برای flush یک فایل یا محتوا به درون مرورگر کاربر در برنامههای ASP.NET Web forms مورد استفاده قرار میگیرند.
از دقت کردن در نحوه اداره پروژههای خوب و بزرگ در سطح دنیا، میتوان به نکات آموزندهای رسید. برای مثال NHibernate را درنظر بگیرید. این پروژه شاید روز اول کپی مطابق اصل نمونه جاوای آن بوده، اما الان از خیلی از جهات یک سر و گردن از آن بالاتر است. پشتیبانی LINQ را اضافه کرده، خودش Syntax جدیدی را به نام QueryOver ارائه داده و همچنین معادلی را جهت حذف فایلهای XML به کمک امکانات جدید زبانهای دات نتی مانند lambda expressions ارائه کرده. خلاصه این تیم، فقط یک کپی کار نیست. پایه رو از یک جایی گرفته اما سبب تحول در آن شده. از اهداف پروژههای سورس باز هم همین است: برای هر کاری چرخ را از صفر ابداع نکنید.
اگر به نحوه اداره کلی پروژه NHibernate دقت کنید یک مورد مشهود است:
- تمام گزارشهای باگ بدون Unit test ندید گرفته میشوند.
- از کلیه بهبودهای ارائه شده (وصلهها یا patch ها) بدون Unit test صرفنظر میشود.
- از کلیه موارد جدید ارائه شده بدون Unit test هم صرفنظر خواهد شد.
بنابراین اگر در issue tracker این تیم رفتید و گفتید: «سلام، اینجا این مشکل هست»، خیالتان راحت باشد که ندید گرفته خواهید شد.
سؤال : چرا اینها اینطور رفتار میکنند؟!
- وجود Unit test دقیقا مشخص میکند که چه قسمت یا قسمتهایی به گزارش باگ شما مرتبط هستند. نیازی نیست حتما بتوانید یک خطا را با جملات ساده شرح دهید. این مساله خصوصا در پروژههای بین المللی حائز اهمیت است. ضعف زبان انگلیسی همه جا هست. همینقدر که توانستهاید برای آن یک Unit test بنویسید که مثلا در این عملیات با این ورودی، نتیجه قرار بوده بشود 10 و مثلا شده 5 یا حتی این Exception صادر شده که باید کنترل شود، یعنی مشکل را کاملا مشخص کردهاید.
- وجود Unit tests ، انجام Code review و همچنین Refactoring را تسهیل میبخشند. در هر دو حالت یاد شده، هدف تغییر کارکرد سیستم نیست؛ هدف بهبود کیفیت کدهای موجود است. بنابراین دست به یک سری تغییرات زده خواهد شد. اما سؤال اینجا است که از کجا باید مطمئن شد که این تغییرات، سیستم را به هم نریختهاند. پروژهی جاری چند سال است که در حال توسعه است. قسمتهای زیادی به آن اضافه شده. با نبود Unit tests ممکن است بعضی از قسمتها زاید یا احمقانه به نظر برسند.
- بهترین مستندات کدهای تهیه شده، Unit tests آن هستند. برای مثال علاقمند هستید که NHibernate را یاد بگیرید؟ هرچه میگردید مثالهای کمی را در اینترنت در این زمینه پیدا میکنید؟ وقت خودتان را تلف نکنید! این پروژه بالای 2000 آزمون واحد دارد. هر کدام از این آزمونها نحوهی بکارگیری قسمتهای مختلف را به نحوی کاربردی بیان میکنند.
- وجود Unit tests از پیدایش مجدد باگها جلوگیری میکنند. اگر آزمون واحدی وجود نداشته باشد، امروز کدی اضافه میشود. فردا همین کد توسط عدهای دیگر زاید تشخیص داده شده و حذف میشود! بنابراین احتمال بروز مجدد این خطا در آینده وجود خواهد داشت. با وجود Unit tests، فلسفه وجودی هر قسمتی از کدهای موجود پروژه دقیقا مشخص میشود و در صورت حذف آن، با اجرای آزمونهای خودکار سریعا میتوان به کمبودهای حاصل پیبرد.
فیلدهای استاتیکی که در سطح یک کلاس تعریف میشوند، برای نگهداری دادههایی به کار میروند که بین همهی اشیاء ساخته شدهی از آن کلاس مشترک هستند. لذا برای دستیابی به آنها، نیاز به ساختن شیءای از آن کلاس نبوده و از طریق خود کلاس در دسترس خواهند بود. اما نکتهای در مورد فیلدهای استاتیک وجود دارد و آن هم ترتیب مقدار دهی به آنها است که در این مجال قصد دارم به آن بپردازم.
در یک کلاس همانطور که میتوانیم متد استاتیک و یا پراپرتی استاتیک داشته باشیم، قادر هستیم فیلدهایی را نیز به صورت استاتیک تعریف نماییم. با نوشتن کلمهی کلیدی Static قبل از فیلد یک کلاس، آن فیلد تبدیل به فیلدی استاتیک شده و از این پس این فیلد، متعلق به اشیاء ساخته شدهی از کلاس نیست و تنها از طریق خود کلاس میتوان به آن دست یافت. اگر فیلد استاتیک به صورت خصوصی (private) تعریف شود، تنها اعضای داخلی آن کلاس میتوانند به آن دسترسی داشته باشند و آن را تغییر دهند؛ ولی اگر به صورت عمومیتری تعریف شود، هر نوعی که بتواند به آن دسترسی داشته باشد، میتواند مقدار آن را ببیند و تغییر دهد.
زمانیکه شما یک کلاس با فیلد استاتیک را تعریف میکنید باید مراقب ترتیب مقدار دهی آنها نیز باشید. به عنوان مثال کلاس زیر را در نظر بگیرید:
در نگاه اول شاید کد بالا بدون مشکل به نظر برسد؛ یعنی اگر بخواهیم از مقادیر این فیلدهای استاتیک استفاده کنیم، انتظار داریم فیلد MaxAttempts به مقدار 5، فیلد WarningAttempts به مقدار 2 و فیلد Threshold به تفاضل آنها یعنی 3 مقداردهی شده باشند. ولی اگر کد زیر را اجرا کنید خروجی متفاوتی را مشاهده خواهید کرد:
همانطور که در این خروجی مشاهده میکنید، مقدار فیلد Threshold به صفر مقداردهی شده است؛ در حالیکه ما انتظار عدد 2 را داشتیم.
دلیل این مقدار غیر منتظره را باید در سند مشخصات زبان سی شارپ ( C# Language Specification ) یافت. در سند مشخصات زبان سی شارپ، نحوهی استفادهی از این زبان و دستورات نحوی ( Syntax ) آن به صورت شفافی توضیح داده شدهاند. این سند بیان میکند هیچ فیلد استاتیکی هرگز نباید بدون مقدار باشد؛ یعنی اگر قبل از مقدار دهی یک فیلد استاتیک بخواهیم به مقدار آن دسترسی داشته باشیم، به مقدار اولیهی نوع آن فیلد، مقدار دهی خواهد شد. پس در مثال بالا چون فیلدهای MaxAttempts و WarningAttempts از نوع Integer هستند، مقدار پیشفرض صفر را خواهند گرفت. همچنین این سند بیان میکند که اگر در کلاسی چندین فیلد استاتیک تعریف شوند و آنها در چند سطر جداگانه مقداردهی شوند (همانند کاری که ما در مثال بالا انجام دادیم) بر طبق ترتیبی که عملیات مقداردهی به آنها انجام میگیرد، مقدار خواهند گرفت. یعنی وقتی که فیلد استاتیک Threshold مقدار دهی میشود، چون فیلدهای استاتیک MaxAttempts و WarningAttempts هنوز مقداردهی نشدهاند، مقدار صفر میگیرند. پس در نتیجهی فیلد Threshold هم مقدار صفر را میگیرد و چون ترتیب مقدار دهی نیز مهم است، مقدار آن با تغییر مقدار فیلدهای MaxAttempts و WarningAttempts تغیر نکرده و کماکان صفر باقی خواهد ماند و پس از آن در خطهای بعدی، فیلدهای MaxAttempts و WarningAttempts مقدار میگیرند.
پس برای رفع این مشکل باید ترتیب مقداردهی فیلدها را به گونهای تغییر داد که قبل از استفادهی از آنها، مقدارشان معلوم باشد.
اینبار خروجی زیر حاصل میشود:
مشکلی که این راه حل دارد این است که کد خوانایی نیست و قابلیت نگهداری خوبی هم ندارد. از آنجایی ما توسعه دهندگان عادت به تغییر کدهای دیگران را داریم، قابل پیش بینیاست که یک توسعه دهندهی دیگر، ترتیب نوشتن فیلدهای استاتیک را مثلا به قصد اینکه بخواهد آنها را به ترتیب حروف الفبا مرتب کند، تغییر دهد که اینکار منجر به یک باگ خواهد شد. یک راه حل بهتر این است که مقداردهی آنها را از تعریفاشان جدا کرده و عملیات مقداردهی به آنها را در یک سازندهی استاتیک قرار دهیم که در این صورت هم خروجی بالا را خواهیم داشت:
در یک کلاس همانطور که میتوانیم متد استاتیک و یا پراپرتی استاتیک داشته باشیم، قادر هستیم فیلدهایی را نیز به صورت استاتیک تعریف نماییم. با نوشتن کلمهی کلیدی Static قبل از فیلد یک کلاس، آن فیلد تبدیل به فیلدی استاتیک شده و از این پس این فیلد، متعلق به اشیاء ساخته شدهی از کلاس نیست و تنها از طریق خود کلاس میتوان به آن دست یافت. اگر فیلد استاتیک به صورت خصوصی (private) تعریف شود، تنها اعضای داخلی آن کلاس میتوانند به آن دسترسی داشته باشند و آن را تغییر دهند؛ ولی اگر به صورت عمومیتری تعریف شود، هر نوعی که بتواند به آن دسترسی داشته باشد، میتواند مقدار آن را ببیند و تغییر دهد.
زمانیکه شما یک کلاس با فیلد استاتیک را تعریف میکنید باید مراقب ترتیب مقدار دهی آنها نیز باشید. به عنوان مثال کلاس زیر را در نظر بگیرید:
class AttemptController { internal static int Threshold = MaxAttempts - WarningAttempts; internal static int MaxAttempts = 5; internal static int WarningAttempts = 2; }
Console.WriteLine("Maximum: {0}", AttemptController.MaxAttempts); Console.WriteLine("Warning: {0}", AttemptController.WarningAttempts); Console.WriteLine("Threshold: {0}", AttemptController.Threshold); /* OUTPUT Maximum: 5 Warning: 2 Threshold: 0 */
دلیل این مقدار غیر منتظره را باید در سند مشخصات زبان سی شارپ ( C# Language Specification ) یافت. در سند مشخصات زبان سی شارپ، نحوهی استفادهی از این زبان و دستورات نحوی ( Syntax ) آن به صورت شفافی توضیح داده شدهاند. این سند بیان میکند هیچ فیلد استاتیکی هرگز نباید بدون مقدار باشد؛ یعنی اگر قبل از مقدار دهی یک فیلد استاتیک بخواهیم به مقدار آن دسترسی داشته باشیم، به مقدار اولیهی نوع آن فیلد، مقدار دهی خواهد شد. پس در مثال بالا چون فیلدهای MaxAttempts و WarningAttempts از نوع Integer هستند، مقدار پیشفرض صفر را خواهند گرفت. همچنین این سند بیان میکند که اگر در کلاسی چندین فیلد استاتیک تعریف شوند و آنها در چند سطر جداگانه مقداردهی شوند (همانند کاری که ما در مثال بالا انجام دادیم) بر طبق ترتیبی که عملیات مقداردهی به آنها انجام میگیرد، مقدار خواهند گرفت. یعنی وقتی که فیلد استاتیک Threshold مقدار دهی میشود، چون فیلدهای استاتیک MaxAttempts و WarningAttempts هنوز مقداردهی نشدهاند، مقدار صفر میگیرند. پس در نتیجهی فیلد Threshold هم مقدار صفر را میگیرد و چون ترتیب مقدار دهی نیز مهم است، مقدار آن با تغییر مقدار فیلدهای MaxAttempts و WarningAttempts تغیر نکرده و کماکان صفر باقی خواهد ماند و پس از آن در خطهای بعدی، فیلدهای MaxAttempts و WarningAttempts مقدار میگیرند.
پس برای رفع این مشکل باید ترتیب مقداردهی فیلدها را به گونهای تغییر داد که قبل از استفادهی از آنها، مقدارشان معلوم باشد.
class AttemptController { internal static int MaxAttempts = 5; internal static int WarningAttempts = 2; internal static int Threshold = MaxAttempts - WarningAttempts; }
Maximum: 5 Warning: 2 Threshold: 3
مشکلی که این راه حل دارد این است که کد خوانایی نیست و قابلیت نگهداری خوبی هم ندارد. از آنجایی ما توسعه دهندگان عادت به تغییر کدهای دیگران را داریم، قابل پیش بینیاست که یک توسعه دهندهی دیگر، ترتیب نوشتن فیلدهای استاتیک را مثلا به قصد اینکه بخواهد آنها را به ترتیب حروف الفبا مرتب کند، تغییر دهد که اینکار منجر به یک باگ خواهد شد. یک راه حل بهتر این است که مقداردهی آنها را از تعریفاشان جدا کرده و عملیات مقداردهی به آنها را در یک سازندهی استاتیک قرار دهیم که در این صورت هم خروجی بالا را خواهیم داشت:
class AttemptController { internal static int MaxAttempts; internal static int WarningAttempts; internal static int Threshold; static AttemptController() { MaxAttempts = 5; WarningAttempts = 2; Threshold = MaxAttempts - WarningAttempts; } }
نظرات اشتراکها
تقویم شمسی رسپانسیو برای بوت استراپ
یکی از مشکلات خیلی شایع در تقویمهای فارسی قدیمی بودن اونهاست . مثلآ باگ عدم نمایش تقویم روی مودال در تقویم استاندارد بوت استرپ رفع شده ولی اکثر تقویمهای فارسی این مشکل رو دارند .
به دنبال باگ کشفشده، letsencrypt تا فردا حدود ۳ درصد از سرتیفیکیتهای صادر شده را منقضی میکند. برای اطمینان از اینکه سرتیفیکیت وبسایت شما جزو این لیست نیست میتوانید از وبسایت https://checkhost.unboundtest.com استفاده کنید