اشتراکها
نظرات مطالب
مقابله با XSS ؛ یکبار برای همیشه!
کتابخانه امنیتی مایکروسافت خطوط جدید را حذف میکند: (+)
به همین جهت مثال مجددا به روز شد تا این مشکل وجود نداشته باشد
به همین جهت مثال مجددا به روز شد تا این مشکل وجود نداشته باشد
تا اینجا یک پروژهی خالی ASP.NET Core 1.0 را به مرحلهی فعال سازی ASP.NET MVC و تنظیمات مسیریابیهای اولیهی آن رساندهایم. مرحلهی بعد، افزودن Viewها، نمایش اطلاعاتی به کاربران و دریافت اطلاعات از آنها است و همانطور که پیشتر نیز عنوان شد، برای «ارتقاء» نیاز است «15 مورد» ابتدایی مطالب ASP.NET MVC سایت را پیش از ادامهی این سری مطالعه کنید.
معرفی فایل جدید ViewImports
پروژهی خالی ASP.NET Core 1.0 فاقد پوشهی Views به همراه فایلهای آغازین آن است. بنابراین ابتدا در ریشهی پروژه، پوشهی جدید Views را ایجاد کنید.
فایلهای آغازین این پوشه هم در مقایسهی با نگارشهای قبلی ASP.NET MVC اندکی تغییر کردهاند. برای مثال در نگارش قبلی، فایل web.config ایی در ریشهی پوشهی Views قرار داشت و چندین مقصود را فراهم میکرد:
الف) در آن تنظیم شده بود که هر نوع درخواستی به فایلهای موجود در پوشهی Views، برگشت خورده و قابل پردازش نباشند. این مورد هم از لحاظ مسایل امنیتی اضافه شده بود و هم اینکه در ASP.NET MVC، برخلاف وب فرمها، شروع پردازش یک درخواست، از فایلهای View شروع نمیشود. به همین جهت است که درخواست مستقیم آنها بیمعنا است.
در ASP.NET Core، فایل web.config از این پوشه حذف شدهاست؛ چون دیگر نیازی به آن نیست. اگر مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 4 - فعال سازی پردازش فایلهای استاتیک» را به خاطر داشته باشید، هر پوشهای که توسط میان افزار Static Files عمومی نشود، توسط کاربران برنامه قابل دسترسی نخواهد بود و چون پوشهی Views هم به صورت پیش فرض توسط این میان افزار عمومی نمیشود، نیازی به فایل web.config، جهت قطع دسترسی به فایلهای موجود در آن وجود ندارد.
ب) کاربرد دیگر این فایل web.config، تعریف فضاهای نام پیش فرضی بود که در فایلهای View مورد استفاده قرار میگرفتند. برای مثال چون فضای نام HTML Helperهای استاندارد ASP.NET MVC در این فایل web.config قید شده بود، دیگر نیازی به تکرار آن در تمام فایلهای View برنامه وجود نداشت. در ASP.NET Core، برای جایگزین کردن این قابلیت، فایل جدیدی را به نام ViewImports.cshtml_ معرفی کردهاند، تا دیگر نیازی به ارث بری از فایل web.config وجود نداشته باشد.
برای مثال اگر میخواهید بالای Viewهای خود، مدام ذکر using مربوط به فضای نام مدلها برنامه را انجام ندهید، این سطر تکراری را به فایل جدید view imports منتقل کنید:
و این فضاهای نام به صورت پیش فرض برای تمام viewها مهیا هستند و نیازی به تعریف مجدد، ندارند:
• System
• System.Linq
• System.Collections.Generic
• Microsoft.AspNetCore.Mvc
• Microsoft.AspNetCore.Mvc.Rendering
افزودن یک View جدید
در نگارشهای پیشین ASP.NET MVC، اگر بر روی نام یک اکشن متد کلیک راست میکردیم، در منوی ظاهر شده، گزینهی Add view وجود داشت. چنین گزینهای در نگارش RTM اول ASP.NET Core وجود ندارد و مراحل ایجاد یک View جدید را باید دستی طی کنید. برای مثال اگر نام کلاس کنترلر شما PersonController است، پوشهی Person را به عنوان زیر پوشهی Views ایجاد کرده و سپس بر روی این پوشه کلیک راست کنید، گزینهی add new item را انتخاب و سپس واژهی view را جستجو کنید:
البته یک دلیل این مساله میتواند امکان سفارشی سازی محل قرارگیری این پوشهها در ASP.NET Core نیز باشد که در ادامه آنرا بررسی خواهیم کرد (و ابزارهای از پیش تعریف شده عموما با مکانهای از پیش تعریف شده کار میکنند).
امکان پوشه بندی بهتر فایلهای یک پروژهی ASP.NET Core نسبت به مفهوم Areas در نگارشهای پیشین ASP.NET MVC
حالت پیش فرض پوشه بندی فایلهای اصلی برنامههای ASP.NET MVC، مبتنی بر فناوریها است؛ برای مثال پوشههای views و Controllers و امثال آن تعریف شدهاند.
روش دیگری را که برای پوشه بندی پروژههای ASP.NET MVC پیشنهاد میکنند (که Area توکار آن نیز زیر مجموعهی آن محسوب میشود)، اصطلاحا Feature Folder Structure نام دارد. در این حالت برنامه بر اساس ویژگیها و قابلیتهای مختلف آن پوشه بندی میشود؛ بجای اینکه یک پوشهی کلی کنترلرها را داشته باشیم و یک پوشهی کلی views را که پس از مدتی، ارتباط دادن بین اینها واقعا مشکل میشود.
هرکسی که مدتی با ASP.NET MVC کار کرده باشد حتما به این مشکل برخوردهاست. درحال پیاده سازی قابلیتی هستید و برای اینکار نیاز خواهید داشت مدام بین پوشههای مختلف برنامه سوئیچ کنید؛ از پوشهی کنترلرها به پوشهی ویووها، به پوشهی اسکریپتها، پوشهی اشتراکی ویووها و غیره. پس از رشد برنامه به جایی خواهید رسید که دیگر نمیتوانید تشخیص دهید این فایلی که اضافه شدهاست ارتباطش با سایر قسمتها چیست؟
ایدهی «پوشه بندی بر اساس ویژگیها»، بر مبنای قرار دادن تمام نیازهای یک ویژگی، درون یک پوشهی خاص آن است:
همانطور که مشاهده میکنید، در این حالت تمام اجزای یک ویژگی، داخل یک پوشه قرار گرفتهاند؛ از کنترلر مرتبط با Viewهای آن تا فایلهای css و js خاص آن.
برای پیاده سازی آن:
1) نام پوشهی Views را به Features تغییر دهید.
2) پوشهای را به نام StartupCustomizations به برنامه اضافه کرده و سپس کلاس ذیل را به آن اضافه کنید:
حالت پیش فرض ASP.NET MVC، یافتن فایلها در مسیرهای Views/{1}/{0}.cshtml و Views/Shared/{0}.cshtml است؛ که در اینجا {0} نام view است و {1} نام کنترلر. این ساختار هم در اینجا حفظ شدهاست؛ اما اینبار به پوشهی جدید Features اشاره میکند.
RazorViewEngine برنامه، بر اساس وهلهی پیش فرضی از اینترفیس IViewLocationExpander، محل یافتن Viewها را دریافت میکند. با استفاده از پیاه سازی فوق، این پیش فرضها را به پوشهی features هدایت کردهایم.
3) در ادامه به کلاس آغازین برنامه مراجعه کرده و پس از فعال سازی ASP.NET MVC، این قابلیت را فعال سازی میکنیم:
4) اکنون تمام فایلهای مرتبط با یک ویژگی را به پوشهی خاص آن انتقال دهید. منظور از این فایلها، کنترلر، فایلهای مدل و ویوومدل، فایلهای ویوو و فایلهای js و css هستند و نه مورد دیگری.
5) اکنون باید پوشهی Controllers خالی شده باشد. این پوشه را کلا حذف کنید. از این جهت که کنترلرها بر اساس پیش فرضهای ASP.NET MVC (کلاس ختم شدهی به کلمهی Controller واقع در اسمبلی که از وابستگیهای ASP.NET MVC استفاده میکند) در هر مکانی که تعریف شده باشند، یافت خواهند شد و پوشهی واقع شدن آنها مهم نیست.
6) در آخر به فایل project.json مراجعه کرده و قسمت publish آنرا جهت درج نام پوشهی Features اصلاح کنید (تا در حین توزیع نهایی استفاده شود):
در اینجا نیز یک نمونهی دیگر استفادهی از این روش بسیار معروف را مشاهده میکنید.
امکان ارائهی برنامه بدون ارائهی فایلهای View آن
ASP.NET Core به همراه یک EmbeddedFileProvider نیز هست. حالت پیش فرض آن PhysicalFileProvider است که بر اساس تنظیمات IViewLocationExpander توکار (و یا نمونهی سفارشی فوق در مبحث پوشهی ویژگیها) کار میکند.
برای راه اندازی آن ابتدا نیاز است بستهی نیوگت ذیل را به فایل project.json اضافه کرد:
سپس تنظیمات متد ConfigureServices کلاس آغازین برنامه را به صورت ذیل جهت معرفی EmbeddedFileProvider تغییر میدهیم:
و در آخر در فایل project.json، در قسمت build options، گزینهی embed را مقدار دهی میکنیم:
در اینجا چند نکته را باید مدنظر داشت:
1) اگر نام پوشهی Views را به Features تغییر دادهاید، نیاز به ثبت ViewLocationExpanders آنرا دارید (وگرنه، خیر).
2) در اینجا جهت مثال و بررسی اینکه واقعا این فایلها از اسمبلی برنامه خوانده میشوند، متد options.FileProviders.Clear فراخوانی شدهاست. این متد PhysicalFileProvider پیش فرض را حذف میکند. کار PhysicalFileProvider خواندن فایلهای ویووها از فایل سیستم به صورت متداول است.
3) کار قسمت embed در تنظیمات build، افزودن فایلهای cshtml به قسمت منابع اسمبلی است (به همین جهت دیگر نیازی به توزیع آنها نخواهد بود). اگر صرفا **/Features را ذکر کنید، تمام فایلهای موجود را پیوست میکند. همچنین اگر نام پوشهی Views را تغییر ندادهاید، این مقدار همان Views/**/*.cshtml خواهد بود و یا **/Views
4) در EmbeddedFileProvider میتوان هر نوع اسمبلی را ذکر کرد. یعنی میتوان برنامه را به صورت چندین و چند ماژول تهیه و سپس سرهم و یکپارچه کرد (options.FileProviders یک لیست قابل تکمیل است). در اینجا ذکر baseNamespace نیز مهم است. در غیر اینصورت منبع مورد نظر از اسمبلی یاد شده، قابل استخراج نخواهد بود (چون نام اسمبلی، قسمت اول نام هر منبعی است).
فعال سازی کامپایل خودکار فایلهای View در ASP.NET Core 1.0
این قابلیت به زودی جهت یافتن مشکلات موجود در فایلهای razor پیش از اجرای آنها، اضافه خواهد شد. اطلاعات بیشتر
معرفی فایل جدید ViewImports
پروژهی خالی ASP.NET Core 1.0 فاقد پوشهی Views به همراه فایلهای آغازین آن است. بنابراین ابتدا در ریشهی پروژه، پوشهی جدید Views را ایجاد کنید.
فایلهای آغازین این پوشه هم در مقایسهی با نگارشهای قبلی ASP.NET MVC اندکی تغییر کردهاند. برای مثال در نگارش قبلی، فایل web.config ایی در ریشهی پوشهی Views قرار داشت و چندین مقصود را فراهم میکرد:
الف) در آن تنظیم شده بود که هر نوع درخواستی به فایلهای موجود در پوشهی Views، برگشت خورده و قابل پردازش نباشند. این مورد هم از لحاظ مسایل امنیتی اضافه شده بود و هم اینکه در ASP.NET MVC، برخلاف وب فرمها، شروع پردازش یک درخواست، از فایلهای View شروع نمیشود. به همین جهت است که درخواست مستقیم آنها بیمعنا است.
در ASP.NET Core، فایل web.config از این پوشه حذف شدهاست؛ چون دیگر نیازی به آن نیست. اگر مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 4 - فعال سازی پردازش فایلهای استاتیک» را به خاطر داشته باشید، هر پوشهای که توسط میان افزار Static Files عمومی نشود، توسط کاربران برنامه قابل دسترسی نخواهد بود و چون پوشهی Views هم به صورت پیش فرض توسط این میان افزار عمومی نمیشود، نیازی به فایل web.config، جهت قطع دسترسی به فایلهای موجود در آن وجود ندارد.
ب) کاربرد دیگر این فایل web.config، تعریف فضاهای نام پیش فرضی بود که در فایلهای View مورد استفاده قرار میگرفتند. برای مثال چون فضای نام HTML Helperهای استاندارد ASP.NET MVC در این فایل web.config قید شده بود، دیگر نیازی به تکرار آن در تمام فایلهای View برنامه وجود نداشت. در ASP.NET Core، برای جایگزین کردن این قابلیت، فایل جدیدی را به نام ViewImports.cshtml_ معرفی کردهاند، تا دیگر نیازی به ارث بری از فایل web.config وجود نداشته باشد.
برای مثال اگر میخواهید بالای Viewهای خود، مدام ذکر using مربوط به فضای نام مدلها برنامه را انجام ندهید، این سطر تکراری را به فایل جدید view imports منتقل کنید:
@using MyProject.Models
و این فضاهای نام به صورت پیش فرض برای تمام viewها مهیا هستند و نیازی به تعریف مجدد، ندارند:
• System
• System.Linq
• System.Collections.Generic
• Microsoft.AspNetCore.Mvc
• Microsoft.AspNetCore.Mvc.Rendering
افزودن یک View جدید
در نگارشهای پیشین ASP.NET MVC، اگر بر روی نام یک اکشن متد کلیک راست میکردیم، در منوی ظاهر شده، گزینهی Add view وجود داشت. چنین گزینهای در نگارش RTM اول ASP.NET Core وجود ندارد و مراحل ایجاد یک View جدید را باید دستی طی کنید. برای مثال اگر نام کلاس کنترلر شما PersonController است، پوشهی Person را به عنوان زیر پوشهی Views ایجاد کرده و سپس بر روی این پوشه کلیک راست کنید، گزینهی add new item را انتخاب و سپس واژهی view را جستجو کنید:
البته یک دلیل این مساله میتواند امکان سفارشی سازی محل قرارگیری این پوشهها در ASP.NET Core نیز باشد که در ادامه آنرا بررسی خواهیم کرد (و ابزارهای از پیش تعریف شده عموما با مکانهای از پیش تعریف شده کار میکنند).
امکان پوشه بندی بهتر فایلهای یک پروژهی ASP.NET Core نسبت به مفهوم Areas در نگارشهای پیشین ASP.NET MVC
حالت پیش فرض پوشه بندی فایلهای اصلی برنامههای ASP.NET MVC، مبتنی بر فناوریها است؛ برای مثال پوشههای views و Controllers و امثال آن تعریف شدهاند.
Project - Controllers - Models - Services - ViewModels - Views
هرکسی که مدتی با ASP.NET MVC کار کرده باشد حتما به این مشکل برخوردهاست. درحال پیاده سازی قابلیتی هستید و برای اینکار نیاز خواهید داشت مدام بین پوشههای مختلف برنامه سوئیچ کنید؛ از پوشهی کنترلرها به پوشهی ویووها، به پوشهی اسکریپتها، پوشهی اشتراکی ویووها و غیره. پس از رشد برنامه به جایی خواهید رسید که دیگر نمیتوانید تشخیص دهید این فایلی که اضافه شدهاست ارتباطش با سایر قسمتها چیست؟
ایدهی «پوشه بندی بر اساس ویژگیها»، بر مبنای قرار دادن تمام نیازهای یک ویژگی، درون یک پوشهی خاص آن است:
همانطور که مشاهده میکنید، در این حالت تمام اجزای یک ویژگی، داخل یک پوشه قرار گرفتهاند؛ از کنترلر مرتبط با Viewهای آن تا فایلهای css و js خاص آن.
برای پیاده سازی آن:
1) نام پوشهی Views را به Features تغییر دهید.
2) پوشهای را به نام StartupCustomizations به برنامه اضافه کرده و سپس کلاس ذیل را به آن اضافه کنید:
using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.Razor; namespace Core1RtmEmptyTest.StartupCustomizations { public class FeatureLocationExpander : IViewLocationExpander { public void PopulateValues(ViewLocationExpanderContext context) { context.Values["customviewlocation"] = nameof(FeatureLocationExpander); } public IEnumerable<string> ExpandViewLocations( ViewLocationExpanderContext context, IEnumerable<string> viewLocations) { return new[] { "/Features/{1}/{0}.cshtml", "/Features/Shared/{0}.cshtml" }; } } }
RazorViewEngine برنامه، بر اساس وهلهی پیش فرضی از اینترفیس IViewLocationExpander، محل یافتن Viewها را دریافت میکند. با استفاده از پیاه سازی فوق، این پیش فرضها را به پوشهی features هدایت کردهایم.
3) در ادامه به کلاس آغازین برنامه مراجعه کرده و پس از فعال سازی ASP.NET MVC، این قابلیت را فعال سازی میکنیم:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.Configure<RazorViewEngineOptions>(options => { options.ViewLocationExpanders.Add(new FeatureLocationExpander()); });
5) اکنون باید پوشهی Controllers خالی شده باشد. این پوشه را کلا حذف کنید. از این جهت که کنترلرها بر اساس پیش فرضهای ASP.NET MVC (کلاس ختم شدهی به کلمهی Controller واقع در اسمبلی که از وابستگیهای ASP.NET MVC استفاده میکند) در هر مکانی که تعریف شده باشند، یافت خواهند شد و پوشهی واقع شدن آنها مهم نیست.
6) در آخر به فایل project.json مراجعه کرده و قسمت publish آنرا جهت درج نام پوشهی Features اصلاح کنید (تا در حین توزیع نهایی استفاده شود):
"publishOptions": { "include": [ "wwwroot", "Features", "appsettings.json", "web.config" ] },
در اینجا نیز یک نمونهی دیگر استفادهی از این روش بسیار معروف را مشاهده میکنید.
امکان ارائهی برنامه بدون ارائهی فایلهای View آن
ASP.NET Core به همراه یک EmbeddedFileProvider نیز هست. حالت پیش فرض آن PhysicalFileProvider است که بر اساس تنظیمات IViewLocationExpander توکار (و یا نمونهی سفارشی فوق در مبحث پوشهی ویژگیها) کار میکند.
برای راه اندازی آن ابتدا نیاز است بستهی نیوگت ذیل را به فایل project.json اضافه کرد:
{ "dependencies": { //same as before "Microsoft.Extensions.FileProviders.Embedded": "1.0.0" },
services.AddMvc(); services.Configure<RazorViewEngineOptions>(options => { options.ViewLocationExpanders.Add(new FeatureLocationExpander()); var thisAssembly = typeof(Startup).GetTypeInfo().Assembly; options.FileProviders.Clear(); options.FileProviders.Add(new EmbeddedFileProvider(thisAssembly, baseNamespace: "Core1RtmEmptyTest")); });
"buildOptions": { "emitEntryPoint": true, "preserveCompilationContext": true, "embed": "Features/**/*.cshtml" },
1) اگر نام پوشهی Views را به Features تغییر دادهاید، نیاز به ثبت ViewLocationExpanders آنرا دارید (وگرنه، خیر).
2) در اینجا جهت مثال و بررسی اینکه واقعا این فایلها از اسمبلی برنامه خوانده میشوند، متد options.FileProviders.Clear فراخوانی شدهاست. این متد PhysicalFileProvider پیش فرض را حذف میکند. کار PhysicalFileProvider خواندن فایلهای ویووها از فایل سیستم به صورت متداول است.
3) کار قسمت embed در تنظیمات build، افزودن فایلهای cshtml به قسمت منابع اسمبلی است (به همین جهت دیگر نیازی به توزیع آنها نخواهد بود). اگر صرفا **/Features را ذکر کنید، تمام فایلهای موجود را پیوست میکند. همچنین اگر نام پوشهی Views را تغییر ندادهاید، این مقدار همان Views/**/*.cshtml خواهد بود و یا **/Views
4) در EmbeddedFileProvider میتوان هر نوع اسمبلی را ذکر کرد. یعنی میتوان برنامه را به صورت چندین و چند ماژول تهیه و سپس سرهم و یکپارچه کرد (options.FileProviders یک لیست قابل تکمیل است). در اینجا ذکر baseNamespace نیز مهم است. در غیر اینصورت منبع مورد نظر از اسمبلی یاد شده، قابل استخراج نخواهد بود (چون نام اسمبلی، قسمت اول نام هر منبعی است).
فعال سازی کامپایل خودکار فایلهای View در ASP.NET Core 1.0
این قابلیت به زودی جهت یافتن مشکلات موجود در فایلهای razor پیش از اجرای آنها، اضافه خواهد شد. اطلاعات بیشتر
فرض کنید قصد داریم خاصیت htmlContent زیر را در قالب این کامپوننت نمایش دهیم:
اگر از روش متداول binding استفاده شود:
چنین خروجی حاصل خواهد شد:
همچنین اگر به کنسول developer tools مرورگر مراجعه کنیم، چنین اخطاری نیز درج شده است:
به این معنا که Angular به صورت توکار تمام خروجیها را به صورت encode شده نمایش میدهد و در مقابل حملات XSS مقاوم است. Sanitizing نیز در اینجا به معنای تغییر ورودی و تبدیل آن به مقداری است که جهت درج در DOM امن است.
روش نمایش HTML در برنامههای Angular
اما اگر خواستیم اطلاعات HTML ایی را به همان صورتی که هستند نمایش دهیم چطور؟ در این حالت باید از روش ویژهی ذیل استفاده کرد:
برای نمایش HTML نیاز است آنرا به ویژگی innerHTML متصل کرد؛ با این خروجی:
همانطور که مشاهده میکنید، هنوز هم عملیات پاکسازی قسمتهایی که ممکن است مخرب باشند صورت میگیرد (برای مثال تگ script حذف شدهاست). اما مابقی تگهای امن به همان حالتی که هستند نمایش داده خواهند شد.
روش دیگر کار با innerHTML، تعریف یک template reference variable در قالب کامپوننت است:
و سپس دسترسی به آن از طریق یک ViewChild و انتساب مقداری بهinnerHTML آن به صورت ذیل:
با این خروجی:
که اینبار قسمت script آن به طور کامل حذف شدهاست.
حالات مختلفی که Angular برنامه را از حملات XSS محافظت میکند
در ذیل، لیست مواردی را مشاهده میکنید که به صورت پیشفرض توسط Angular در مقابل حملات XSS محافظت میشوند و اطلاعات انتساب داده شدهی به آنها تمیزسازی خواهند شد:
تبدیل کردن یک HTML نا امن ورودی به یک HTML امن در Angular
بهتر است اطلاعات دریافتی از کاربران پیش از ارسال به سرور تمیز شوند. برای این منظور میتوان از سرویس ویژهای به نام DomSanitizer کمک گرفت. کار این سرویس، امن سازی اطلاعات نمایش داده شدهی در برنامههای Angular است.
در این حالت سرویس DomSanitizer به سازندهی کلاس تزریق شده و سپس میتوان از متدهای مختلف آن مانند sanitize استفاده کرد. خروجی آن صرفا حذف تگ اسکریپت و نگهداری کدهای درون آن است.
در این حالت میتوان موارد ذیل را کنترل کرد. برای مثال اگر مقدار دریافتی CSS است، میتوان از SecurityContext.STYLE استفاده کرد و سایر حالات آن مانند امن سازی HTML، اسکریپت و آدرسهای اینترنتی به شرح ذیل هستند:
غیرفعال کردن سیستم امنیتی Angular جهت نمایش کامل یک مقدار HTML ایی
اگر خواستیم اطلاعات HTML ایی را با فرض امن بودن آن، به همان نحوی که هست نمایش دهیم چطور؟
سرویس DomSanitizer شامل متدهای ذیل نیز میباشد:
اولین متد آن sanitize است که در مورد آن توضیح داده شد. سایر متدها، کار غیرفعال سازی سیستم امنیتی توکار Angular را انجام میدهند.
برای کار با آنها همانند مثال استفادهی از متد sanitize میتوان سرویس DomSanitizer را به سازندهی یک کامپوننت تزریق کرد و یا میتوان این عملیات تکراری فرمت اطلاعات ورودی را تبدیل به یک Pipe جدید کرد:
کار این Pipe غیرفعال کردن سیستم امنیتی Angular و نمایش html، style و غیره به همان صورتی که هستند، میباشد.
برای استفادهی از آن، ابتدا این Pipe به قسمت declarations ماژول مدنظر اضافه خواهد شد:
و سپس در قالب کامپوننت به نحو ذیل میتوان با آن کار کرد:
در این حالت متد bypassSecurityTrustHtml بر روی htmlContent، فراخوانی شده و نتیجهی نهایی نمایش داده خواهد شد.
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید.
export class ShowHtmlComponent { htmlContent = "Template <script>alert(\"Hello!\")</script> <b>Syntax</b>"; }
<h3>Binding innerHTML</h3> <p>Bound value:</p> <p>{{htmlContent}}</p>
همچنین اگر به کنسول developer tools مرورگر مراجعه کنیم، چنین اخطاری نیز درج شده است:
WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss).
روش نمایش HTML در برنامههای Angular
اما اگر خواستیم اطلاعات HTML ایی را به همان صورتی که هستند نمایش دهیم چطور؟ در این حالت باید از روش ویژهی ذیل استفاده کرد:
<p>Result of binding to innerHTML:</p> <p [innerHTML]="htmlContent"></p>
همانطور که مشاهده میکنید، هنوز هم عملیات پاکسازی قسمتهایی که ممکن است مخرب باشند صورت میگیرد (برای مثال تگ script حذف شدهاست). اما مابقی تگهای امن به همان حالتی که هستند نمایش داده خواهند شد.
روش دیگر کار با innerHTML، تعریف یک template reference variable در قالب کامپوننت است:
<p #dataContainer></p>
export class ShowHtmlComponent implements OnInit { @ViewChild("dataContainer") dataContainer: ElementRef; ngOnInit() { this.dataContainer.nativeElement.innerHTML = "nativeElement <script>alert(\"Hello!\")</script> <b>Syntax</b>"; } }
که اینبار قسمت script آن به طور کامل حذف شدهاست.
حالات مختلفی که Angular برنامه را از حملات XSS محافظت میکند
در ذیل، لیست مواردی را مشاهده میکنید که به صورت پیشفرض توسط Angular در مقابل حملات XSS محافظت میشوند و اطلاعات انتساب داده شدهی به آنها تمیزسازی خواهند شد:
HTML Attributes – <div [innerHTML]="UNTRUSTED"></div> OR <input value="UNTRUSTED"> Style— <div [style]="height:UNTRUSTED"></div> URL — <a [href]="UNTRUSTED-URL"></a> OR <script [src]="UNTRUSTED-URL"></script> OR <iframe src="UNTRUSTED-URL" /> GET Parameter – <a href="/user?id=UNTRUSTED">link</a> JavaScript Variable – <script> var value='UNTRUSTED';</script>
تبدیل کردن یک HTML نا امن ورودی به یک HTML امن در Angular
بهتر است اطلاعات دریافتی از کاربران پیش از ارسال به سرور تمیز شوند. برای این منظور میتوان از سرویس ویژهای به نام DomSanitizer کمک گرفت. کار این سرویس، امن سازی اطلاعات نمایش داده شدهی در برنامههای Angular است.
export class ShowHtmlComponent implements OnInit { sanitizedHtml: string; constructor(private sanitizer: DomSanitizer) { } ngOnInit() { this.sanitizedHtml = this.sanitizer.sanitize(SecurityContext.HTML, "<b>Sanitize</b><script>attackerCode()</script>"); } }
در این حالت میتوان موارد ذیل را کنترل کرد. برای مثال اگر مقدار دریافتی CSS است، میتوان از SecurityContext.STYLE استفاده کرد و سایر حالات آن مانند امن سازی HTML، اسکریپت و آدرسهای اینترنتی به شرح ذیل هستند:
SecurityContext.NONE SecurityContext.HTML SecurityContext.STYLE SecurityContext.SCRIPT SecurityContext.URL SecurityContext.RESOURCE_URL
غیرفعال کردن سیستم امنیتی Angular جهت نمایش کامل یک مقدار HTML ایی
اگر خواستیم اطلاعات HTML ایی را با فرض امن بودن آن، به همان نحوی که هست نمایش دهیم چطور؟
سرویس DomSanitizer شامل متدهای ذیل نیز میباشد:
export enum SecurityContext { NONE, HTML, STYLE, SCRIPT, URL, RESOURCE_URL } export abstract class DomSanitizer implements Sanitizer { abstract sanitize(context: SecurityContext, value: SafeValue|string|null): string|null; abstract bypassSecurityTrustHtml(value: string): SafeHtml; abstract bypassSecurityTrustStyle(value: string): SafeStyle; abstract bypassSecurityTrustScript(value: string): SafeScript; abstract bypassSecurityTrustUrl(value: string): SafeUrl; abstract bypassSecurityTrustResourceUrl(value: string): SafeResourceUrl; }
برای کار با آنها همانند مثال استفادهی از متد sanitize میتوان سرویس DomSanitizer را به سازندهی یک کامپوننت تزریق کرد و یا میتوان این عملیات تکراری فرمت اطلاعات ورودی را تبدیل به یک Pipe جدید کرد:
import { Pipe, PipeTransform } from "@angular/core"; import { DomSanitizer, SafeHtml, SafeResourceUrl, SafeScript, SafeStyle, SafeUrl } from "@angular/platform-browser"; @Pipe({ name: "safe" }) export class SafePipe implements PipeTransform { constructor(protected sanitizer: DomSanitizer) { } public transform(value: any, type: string): SafeHtml | SafeStyle | SafeScript | SafeUrl | SafeResourceUrl { switch (type) { case "html": return this.sanitizer.bypassSecurityTrustHtml(value); case "style": return this.sanitizer.bypassSecurityTrustStyle(value); case "script": return this.sanitizer.bypassSecurityTrustScript(value); case "url": return this.sanitizer.bypassSecurityTrustUrl(value); case "resourceUrl": return this.sanitizer.bypassSecurityTrustResourceUrl(value); default: throw new Error(`Invalid safe type specified: ${type}`); } } }
برای استفادهی از آن، ابتدا این Pipe به قسمت declarations ماژول مدنظر اضافه خواهد شد:
@NgModule({ imports: [ // ... ], declarations: [ SafePipe] })
<p [innerHTML]="htmlContent | safe: 'html'"></p>
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید.
روش اصلاح daylight saving time در سیستم عامل اندروید
محل ذخیره شدن بانک اطلاعاتی time-zones در اندروید
بسته به نگارش اندروید شما، یکی از مکانهای زیر، محل ذخیره سازی دو فایل tzdata و tz_version است:
برای مثال بر روی اندروید 10 من (که روش نصب آن در این مطلب آمده)، مسیر آخری، مسیر اصلی و تاثیرگذار است.
مشکل! به روز رسانی مسیرهای سیستمی اندروید، نیاز به دسترسی root دارند!
تا اینجا مشخص شد که برای رفع مشکل time-zone و به روز رسانی اطلاعات آن، باید دو فایل tzdata و tz_version را در مسیرهای یاد شده، بازنویسی کنیم؛ اما ... اندروید چنین اجازهای را نمیدهد!
در سیستم عامل LineageOS 17.1 ای که من نصب کردم، میتوان با نصب برنامهی معروف Magisk، دسترسی روت پیدا کرد. روش نصب آن هم به صورت زیر است:
1) فایل apk آنرا از این آدرس دریافت کرده و پسوند آنرا به zip. تغییر دهید.
2) سیستم را ریاستارت کرده و رفتن به گزینهی recovery را انتخاب کنید تا پس از ریاستارت سیستم، وارد برنامهی TWRP شویم (که روش نصب آن در مطلب جاری بحث شده).
3) در برنامهی TWRP، گزینهی install را انتخاب و سپس فایل zip. برنامهی magisk را انتخاب و نصب کنید.
4) پس از نصب، یکبار هم کش را پاک کنید (Wipe cache/dalvik).
5) در آخر بر روی دکمهی Reboot System تا ... وارد اندروید شوید.
6) پس از راه اندازی سیستم، برنامهی Magisk را اجرا کنید تا مرحلهی آخر نصب آن به پایان برسد (یکسری از فایلها را نیاز دارد تا از اینترنت دریافت و نصب کند؛ این نصب، از github و به صورت خودکار است).
پس از نصب magisk، برنامهی total commander را هم نصب کنید. به محض اجرای آن، پیام درخواست دسترسی root آن هم ظاهر میشود (این برنامهی magisk هست که این درخواست را تشخیص داده و مدیریت میکند) و نیاز است این دسترسی را بدهید تا بتوان:
1) به مسیر file system root و سپس apex/com.android.tzdata/etc/tz/tzdata وارد شد و دو فایل موجود و قدیمی tzdata و tz_version را به نحو متداولی پاک کرد.
2) سپس دو فایل جدید tzdata و tz_version را (که لینک دریافت آن کمی بالاتر ارائه شد) به سیستم خود منتقل کنید (همانند تمام فایلهای دیگر از طریق usb) و در آخر با استفاده از total commander ای که اکنون دسترسی root هم دارد، این فایلها را به مسیر یاد شده، کپی کنید.
باتوجه به اینکه از امسال دیگر تغییر ساعت بهاری و تابستانی را نداریم، از ابتدای سال، سیستم عاملهای اندرویدی، یک ساعت جلوتر را نمایش میدهند که در ابتدا تصور بر این بود که مشکل از اپراتورهاست، اما اینطور نیست! ساعت ارسالی توسط آنها صحیح است. مشکل از قدیمی بودن بانک اطلاعاتی tzdata سیستم عاملهای اندرویدی است.
نگارش جدید و کامپایل شدهی این فایلها را از اینجا میتوانید دریافت کنید: tzdata-new.zip
محل ذخیره شدن بانک اطلاعاتی time-zones در اندروید
بسته به نگارش اندروید شما، یکی از مکانهای زیر، محل ذخیره سازی دو فایل tzdata و tz_version است:
/data/misc/zoneinfo/tzdata /system/usr/share/zoneinfo /apex/com.android.tzdata/etc/tz/tzdata
مشکل! به روز رسانی مسیرهای سیستمی اندروید، نیاز به دسترسی root دارند!
تا اینجا مشخص شد که برای رفع مشکل time-zone و به روز رسانی اطلاعات آن، باید دو فایل tzdata و tz_version را در مسیرهای یاد شده، بازنویسی کنیم؛ اما ... اندروید چنین اجازهای را نمیدهد!
در سیستم عامل LineageOS 17.1 ای که من نصب کردم، میتوان با نصب برنامهی معروف Magisk، دسترسی روت پیدا کرد. روش نصب آن هم به صورت زیر است:
1) فایل apk آنرا از این آدرس دریافت کرده و پسوند آنرا به zip. تغییر دهید.
2) سیستم را ریاستارت کرده و رفتن به گزینهی recovery را انتخاب کنید تا پس از ریاستارت سیستم، وارد برنامهی TWRP شویم (که روش نصب آن در مطلب جاری بحث شده).
3) در برنامهی TWRP، گزینهی install را انتخاب و سپس فایل zip. برنامهی magisk را انتخاب و نصب کنید.
4) پس از نصب، یکبار هم کش را پاک کنید (Wipe cache/dalvik).
5) در آخر بر روی دکمهی Reboot System تا ... وارد اندروید شوید.
6) پس از راه اندازی سیستم، برنامهی Magisk را اجرا کنید تا مرحلهی آخر نصب آن به پایان برسد (یکسری از فایلها را نیاز دارد تا از اینترنت دریافت و نصب کند؛ این نصب، از github و به صورت خودکار است).
پس از نصب magisk، برنامهی total commander را هم نصب کنید. به محض اجرای آن، پیام درخواست دسترسی root آن هم ظاهر میشود (این برنامهی magisk هست که این درخواست را تشخیص داده و مدیریت میکند) و نیاز است این دسترسی را بدهید تا بتوان:
1) به مسیر file system root و سپس apex/com.android.tzdata/etc/tz/tzdata وارد شد و دو فایل موجود و قدیمی tzdata و tz_version را به نحو متداولی پاک کرد.
2) سپس دو فایل جدید tzdata و tz_version را (که لینک دریافت آن کمی بالاتر ارائه شد) به سیستم خود منتقل کنید (همانند تمام فایلهای دیگر از طریق usb) و در آخر با استفاده از total commander ای که اکنون دسترسی root هم دارد، این فایلها را به مسیر یاد شده، کپی کنید.
3) اکنون سیستم را ریاستارت کنید تا تنظیمات جدید خوانده شوند (همانند تصویر زیر، که در آن منطقه زمانی جدید 3:30+ قابل مشاهدهاست):
یک نکته: « Magisk-Modules-Repo
/
Systemless_TZData » در صورت به روز شدن، این کار نصب دستی tzdata را به صورت خودکار انجام میدهد که در زمان نگارش این مطلب، هنوز بهروز نشده.
پیشتر مطلب «افزودن هدرهای Content Security Policy به برنامههای ASP.NET» را در این سایت مطالعه کردهاید. در اینجا قصد داریم معادل آنرا برای ASP.NET Core تهیه کرده و همچنین نکات مرتبط با برنامههای Angular را نیز در آن لحاظ کنیم.
تهیه میان افزار افزودن هدرهای Content Security Policy
کدهای کامل این میان افزار را در ادامه مشاهده میکنید:
که نحوهی استفاده از آن در کلاس آغازین برنامه به صورت ذیل خواهد بود:
توضیحات تکمیلی
افزودن X-Frame-Options
از هدر X-FRAME-OPTIONS، جهت منع نمایش و رندر سایت جاری، در iframeهای سایتهای دیگر استفاده میشود. ذکر مقدار SAMEORIGIN آن، به معنای مجاز تلقی کردن دومین جاری برنامه است.
افزودن X-Xss-Protection
تقریبا تمام مرورگرهای امروزی قابلیت تشخیص حملات XSS را توسط static analysis توکار خود دارند. این هدر، آنالیز اجباری XSS را فعال کرده و همچنین تنظیم حالت آن به block، نمایش و رندر قسمت مشکلدار را به طور کامل غیرفعال میکند.
افزودن X-Content-Type-Options
وجود این هدر سبب میشود تا مرورگر، حدسزدن نوع فایلها، درخواستها و محتوا را کنار گذاشته و صرفا به content-type ارسالی توسط سرور اکتفا کند. به این ترتیب برای مثال امکان لینک کردن یک فایل غیرجاوا اسکریپتی و اجرای آن به صورت کدهای جاوا اسکریپت، چون توسط تگ script ذکر شدهاست، غیرفعال میشود. در غیراینصورت مرورگر هرچیزی را که توسط تگ script به صفحه لینک شده باشد، صرف نظر از content-type واقعی آن، اجرا خواهد کرد.
افزودن Content-Security-Policy
وجود این هدر، تزریق کدها و منابع را از دومینهای دیگر غیرممکن میکند. برای مثال ذکر self در اینجا به معنای مجاز بودن الصاق و اجرای اسکریپتها، شیوهنامهها، تصاویر و اشیاء، صرفا از طریق دومین جاری برنامه است و هرگونه منبعی که از دومینهای دیگر به برنامه تزریق شود، قابلیت اجرایی و یا نمایشی نخواهد داشت.
در اینجا ذکر unsafe-inline و unsafe-eval را مشاهده میکنید. برنامههای Angular به همراه شیوهنامههای inline و یا بکارگیری متد eval در مواردی خاص هستند. اگر این دو گزینه ذکر و فعال نشوند، در کنسول developer مرورگر، خطای بلاک شدن آنها را مشاهده کرده و همچنین برنامه از کار خواهد افتاد.
یک نکته: با فعالسازی گزینهی aot-- در حین ساخت برنامه، میتوان unsafe-eval را نیز حذف کرد.
استفاده از فایل web.config برای تعریف SameSite Cookies
یکی از پیشنهادهای اخیر ارائه شدهی جهت مقابلهی با حملات CSRF و XSRF، قابلیتی است به نام Same-Site Cookies. به این ترتیب مرورگر، کوکی سایت جاری را به همراه یک درخواست ارسال آن به سایت دیگر، پیوست نمیکند (کاری که هم اکنون با درخواستهای Cross-Site صورت میگیرد). برای رفع این مشکل، با این پیشنهاد امنیتی جدید، تنها کافی است SameSite، به انتهای کوکی اضافه شود:
نگارشهای بعدی ASP.NET Core، ویژگی SameSite را نیز به عنوان CookieOptions لحاظ کردهاند. همچنین یک سری از کوکیهای خودکار تولیدی توسط آن مانند کوکیهای anti-forgery به صورت خودکار با این ویژگی تولید میشوند.
اما مدیریت این مورد برای اعمال سراسری آن، با کدنویسی میسر نیست (مگر اینکه مانند نگارشهای بعدی ASP.NET Core پشتیبانی توکاری از آن صورت گیرد). به همین جهت میتوان از ماژول URL rewrite مربوط به IIS برای افزودن ویژگی SameSite به تمام کوکیهای تولید شدهی توسط سایت، کمک گرفت. برای این منظور تنها کافی است فایل web.config را ویرایش کرده و موارد ذیل را به آن اضافه کنید:
لاگ کردن منابع بلاک شدهی توسط مرورگر در سمت سرور
اگر به هدر Content-Security-Policy دقت کنید، گزینهی آخر آن، ذکر اکشن متدی در سمت سرور است:
با تنظیم این مورد، میتوان موارد بلاک شده را در سمت سرور لاگ کرد. اما این اطلاعات ارسالی به سمت سرور، فرمت خاصی را دارند:
به همین جهت ابتدا نیاز است توسط JsonProperty کتابخانهی JSON.NET، معادل این خواص را تولید کرد:
اکنون میتوان بدنهی درخواست را استخراج و سپس به این شیء ویژه نگاشت کرد:
در اینجا نحوهی استخراج Request.Body را به صورت خام را مشاهده میکنید. سپس توسط متد DeserializeObject کتابخانهی JSON.NET، این رشته به شیء CspPost نگاشت شدهاست.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
تهیه میان افزار افزودن هدرهای Content Security Policy
کدهای کامل این میان افزار را در ادامه مشاهده میکنید:
using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; namespace AngularTemplateDrivenFormsLab.Utils { public class ContentSecurityPolicyMiddleware { private readonly RequestDelegate _next; public ContentSecurityPolicyMiddleware(RequestDelegate next) { _next = next; } public Task Invoke(HttpContext context) { context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN"); context.Response.Headers.Add("X-Xss-Protection", "1; mode=block"); context.Response.Headers.Add("X-Content-Type-Options", "nosniff"); string[] csp = { "default-src 'self'", "style-src 'self' 'unsafe-inline'", "script-src 'self' 'unsafe-inline' 'unsafe-eval'", "font-src 'self'", "img-src 'self' data:", "connect-src 'self'", "media-src 'self'", "object-src 'self'", "report-uri /api/CspReport/Log" //TODO: Add api/CspReport/Log }; context.Response.Headers.Add("Content-Security-Policy", string.Join("; ", csp)); return _next(context); } } public static class ContentSecurityPolicyMiddlewareExtensions { /// <summary> /// Make sure you add this code BEFORE app.UseStaticFiles();, /// otherwise the headers will not be applied to your static files. /// </summary> public static IApplicationBuilder UseContentSecurityPolicy(this IApplicationBuilder builder) { return builder.UseMiddleware<ContentSecurityPolicyMiddleware>(); } } }
public void Configure(IApplicationBuilder app) { app.UseContentSecurityPolicy();
توضیحات تکمیلی
افزودن X-Frame-Options
context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
افزودن X-Xss-Protection
context.Response.Headers.Add("X-Xss-Protection", "1; mode=block");
افزودن X-Content-Type-Options
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
افزودن Content-Security-Policy
string[] csp = { "default-src 'self'", "style-src 'self' 'unsafe-inline'", "script-src 'self' 'unsafe-inline' 'unsafe-eval'", "font-src 'self'", "img-src 'self' data:", "connect-src 'self'", "media-src 'self'", "object-src 'self'", "report-uri /api/CspReport/Log" //TODO: Add api/CspReport/Log }; context.Response.Headers.Add("Content-Security-Policy", string.Join("; ", csp));
در اینجا ذکر unsafe-inline و unsafe-eval را مشاهده میکنید. برنامههای Angular به همراه شیوهنامههای inline و یا بکارگیری متد eval در مواردی خاص هستند. اگر این دو گزینه ذکر و فعال نشوند، در کنسول developer مرورگر، خطای بلاک شدن آنها را مشاهده کرده و همچنین برنامه از کار خواهد افتاد.
یک نکته: با فعالسازی گزینهی aot-- در حین ساخت برنامه، میتوان unsafe-eval را نیز حذف کرد.
استفاده از فایل web.config برای تعریف SameSite Cookies
یکی از پیشنهادهای اخیر ارائه شدهی جهت مقابلهی با حملات CSRF و XSRF، قابلیتی است به نام Same-Site Cookies. به این ترتیب مرورگر، کوکی سایت جاری را به همراه یک درخواست ارسال آن به سایت دیگر، پیوست نمیکند (کاری که هم اکنون با درخواستهای Cross-Site صورت میگیرد). برای رفع این مشکل، با این پیشنهاد امنیتی جدید، تنها کافی است SameSite، به انتهای کوکی اضافه شود:
Set-Cookie: sess=abc123; path=/; SameSite
نگارشهای بعدی ASP.NET Core، ویژگی SameSite را نیز به عنوان CookieOptions لحاظ کردهاند. همچنین یک سری از کوکیهای خودکار تولیدی توسط آن مانند کوکیهای anti-forgery به صورت خودکار با این ویژگی تولید میشوند.
اما مدیریت این مورد برای اعمال سراسری آن، با کدنویسی میسر نیست (مگر اینکه مانند نگارشهای بعدی ASP.NET Core پشتیبانی توکاری از آن صورت گیرد). به همین جهت میتوان از ماژول URL rewrite مربوط به IIS برای افزودن ویژگی SameSite به تمام کوکیهای تولید شدهی توسط سایت، کمک گرفت. برای این منظور تنها کافی است فایل web.config را ویرایش کرده و موارد ذیل را به آن اضافه کنید:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <rewrite> <outboundRules> <clear /> <!-- https://scotthelme.co.uk/csrf-is-dead/ --> <rule name="Add SameSite" preCondition="No SameSite"> <match serverVariable="RESPONSE_Set_Cookie" pattern=".*" negate="false" /> <action type="Rewrite" value="{R:0}; SameSite=lax" /> <conditions></conditions> </rule> <preConditions> <preCondition name="No SameSite"> <add input="{RESPONSE_Set_Cookie}" pattern="." /> <add input="{RESPONSE_Set_Cookie}" pattern="; SameSite=lax" negate="true" /> </preCondition> </preConditions> </outboundRules> </rewrite> </system.webServer> </configuration>
لاگ کردن منابع بلاک شدهی توسط مرورگر در سمت سرور
اگر به هدر Content-Security-Policy دقت کنید، گزینهی آخر آن، ذکر اکشن متدی در سمت سرور است:
"report-uri /api/CspReport/Log" //TODO: Add api/CspReport/Log
{ "csp-report": { "document-uri": "http://localhost:5000/untypedSha", "referrer": "", "violated-directive": "script-src", "effective-directive": "script-src", "original-policy": "default-src 'self'; style-src 'self'; script-src 'self'; font-src 'self'; img-src 'self' data:; connect-src 'self'; media-src 'self'; object-src 'self'; report-uri /api/Home/CspReport", "disposition": "enforce", "blocked-uri": "eval", "line-number": 21, "column-number": 8, "source-file": "http://localhost:5000/scripts.bundle.js", "status-code": 200, "script-sample": "" } }
class CspPost { [JsonProperty("csp-report")] public CspReport CspReport { get; set; } } class CspReport { [JsonProperty("document-uri")] public string DocumentUri { get; set; } [JsonProperty("referrer")] public string Referrer { get; set; } [JsonProperty("violated-directive")] public string ViolatedDirective { get; set; } [JsonProperty("effective-directive")] public string EffectiveDirective { get; set; } [JsonProperty("original-policy")] public string OriginalPolicy { get; set; } [JsonProperty("disposition")] public string Disposition { get; set; } [JsonProperty("blocked-uri")] public string BlockedUri { get; set; } [JsonProperty("line-number")] public int LineNumber { get; set; } [JsonProperty("column-number")] public int ColumnNumber { get; set; } [JsonProperty("source-file")] public string SourceFile { get; set; } [JsonProperty("status-code")] public string StatusCode { get; set; } [JsonProperty("script-sample")] public string ScriptSample { get; set; } }
namespace AngularTemplateDrivenFormsLab.Controllers { [Route("api/[controller]")] public class CspReportController : Controller { [HttpPost("[action]")] [IgnoreAntiforgeryToken] public async Task<IActionResult> Log() { CspPost cspPost; using (var bodyReader = new StreamReader(this.HttpContext.Request.Body)) { var body = await bodyReader.ReadToEndAsync().ConfigureAwait(false); this.HttpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); cspPost = JsonConvert.DeserializeObject<CspPost>(body); } //TODO: log cspPost return Ok(); } } }
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
- این پروژه برای سازگاری با آخرین نگارش موجود، بارها به روز رسانی شده. متن مطلب فوق را تغییر ندادم، ولی کدهای مخزن کد آن کاملا به روز هست.
- برای درک این مورد باید ساختار پروژهی اصلی Identity را بررسی کنید. در یک طرف آن تعدادی کلاس سطح بالا و abstraction هست و در طرف دیگر پوشهای به نام EntityFrameworkCore که پیاده سازی مخصوص EF-Core این abstractionها است. هستند پروژههای دیگری که بجای EntityFrameworkCore از NHibernate و یا MongoDB استفاده کرده باشند.
این باگ را در اینجا گزارش کنید (به نظر structuremap.dnx هنوز برای نگارش RTM آماده نیست).
به روز رسانی
این مساله در اینجا گزارش شده و عنوان کردهاند که یک populate اضافی دارد:
به روز رسانی
این مساله در اینجا گزارش شده و عنوان کردهاند که یک populate اضافی دارد:
private IServiceProvider IocConfig(IServiceCollection services) { var container = new Container(); container.Configure(config => { //config.Populate(services); ---> این اضافی است }); container.Populate(services); return container.GetInstance<IServiceProvider>(); }