اشتراک‌ها
ملاحظات امنیتی جهت کار با JavaScript در سال 2024

5 JavaScript Security Best Practices for 2024

Any JavaScript web application needs to have a Content Security Policy (CSP), a browser security standard that dictates what the browser is allowed to load — whether that be a domain, subdomain, or resource. Without a CSP, hackers can exploit cross-site scripting vulnerabilities, potentially resulting in a data breach.

ملاحظات امنیتی جهت کار با JavaScript  در سال 2024
نظرات مطالب
Anti CSRF module for ASP.NET
سلام؛
توی توضیحات مربوط به ماژول این خط رو متوجه نشدم
must ensure your GET requests are idempotent (i.e. the side-effects of multiple identical requests are the same as for a single request)

این یعنی چی ؟
ممنون
اشتراک‌ها
آموزش JavaScript Front-End Web App
Learn how to build a front-end web application in plain JavaScript with minimal effort. Do not use any (third-party) framework or library, such as jQuery or Angular, which create black-box dependencies and overhead, and prevent you from learning how to do it yourself.
آموزش JavaScript Front-End Web App
اشتراک‌ها
چهار قانون بهتر برای طراحی نرم‌افزار

Kent’s rules, from Extreme Programming Explained are:

  • Runs all the tests
  • Has no duplicated logic. Be wary of hidden duplication like parallel class hierarchies
  • States every intention important to the programmer
  • Has the fewest possible classes and methods

In my experience, these don’t quite serve the needs of software design. My four rules might be that a well-designed system:

  • is well-covered by passing tests.
  • has no abstractions not directly needed by the program.
  • has unambiguous behavior.
  • requires the fewest number of concepts.
چهار قانون بهتر برای طراحی نرم‌افزار
نظرات اشتراک‌ها
روش امن نگهداری پسورد کاربران
پیاده سازی روش گفته شده در این سایت :
/* 
 * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
 * Copyright (c) 2013, Taylor Hornby
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Text;
using System.Security.Cryptography;

namespace PasswordHash
{
    /// <summary>
    /// Salted password hashing with PBKDF2-SHA1.
    /// Author: havoc AT defuse.ca
    /// www: http://crackstation.net/hashing-security.htm
    /// Compatibility: .NET 3.0 and later.
    /// </summary>
    public class PasswordHash
    {
        // The following constants may be changed without breaking existing hashes.
        public const int SALT_BYTE_SIZE = 24;
        public const int HASH_BYTE_SIZE = 24;
        public const int PBKDF2_ITERATIONS = 1000;

        public const int ITERATION_INDEX = 0;
        public const int SALT_INDEX = 1;
        public const int PBKDF2_INDEX = 2;

        /// <summary>
        /// Creates a salted PBKDF2 hash of the password.
        /// </summary>
        /// <param name="password">The password to hash.</param>
        /// <returns>The hash of the password.</returns>
        public static string CreateHash(string password)
        {
            // Generate a random salt
            RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();
            byte[] salt = new byte[SALT_BYTE_SIZE];
            csprng.GetBytes(salt);

            // Hash the password and encode the parameters
            byte[] hash = PBKDF2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);
            return PBKDF2_ITERATIONS + ":" +
                Convert.ToBase64String(salt) + ":" +
                Convert.ToBase64String(hash);
        }

        /// <summary>
        /// Validates a password given a hash of the correct one.
        /// </summary>
        /// <param name="password">The password to check.</param>
        /// <param name="correctHash">A hash of the correct password.</param>
        /// <returns>True if the password is correct. False otherwise.</returns>
        public static bool ValidatePassword(string password, string correctHash)
        {
            // Extract the parameters from the hash
            char[] delimiter = { ':' };
            string[] split = correctHash.Split(delimiter);
            int iterations = Int32.Parse(split[ITERATION_INDEX]);
            byte[] salt = Convert.FromBase64String(split[SALT_INDEX]);
            byte[] hash = Convert.FromBase64String(split[PBKDF2_INDEX]);

            byte[] testHash = PBKDF2(password, salt, iterations, hash.Length);
            return SlowEquals(hash, testHash);
        }

        /// <summary>
        /// Compares two byte arrays in length-constant time. This comparison
        /// method is used so that password hashes cannot be extracted from
        /// on-line systems using a timing attack and then attacked off-line.
        /// </summary>
        /// <param name="a">The first byte array.</param>
        /// <param name="b">The second byte array.</param>
        /// <returns>True if both byte arrays are equal. False otherwise.</returns>
        private static bool SlowEquals(byte[] a, byte[] b)
        {
            uint diff = (uint)a.Length ^ (uint)b.Length;
            for (int i = 0; i < a.Length && i < b.Length; i++)
                diff |= (uint)(a[i] ^ b[i]);
            return diff == 0;
        }

        /// <summary>
        /// Computes the PBKDF2-SHA1 hash of a password.
        /// </summary>
        /// <param name="password">The password to hash.</param>
        /// <param name="salt">The salt.</param>
        /// <param name="iterations">The PBKDF2 iteration count.</param>
        /// <param name="outputBytes">The length of the hash to generate, in bytes.</param>
        /// <returns>A hash of the password.</returns>
        private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes)
        {
            Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt);
            pbkdf2.IterationCount = iterations;
            return pbkdf2.GetBytes(outputBytes);
        }
    }
}
اشتراک‌ها
امکانات جدید در فایرفاکس کوانتم

Firefox Quantum is over twice as fast as Firefox from 6 months ago, built on a completely overhauled core engine with brand new technology stolen from our advanced research group, and graced with a beautiful new look designed to get out of the way and let you do what you do best: surf a ton of pages, open a zillion tabs, all guilt free because Firefox Quantum uses less memory than the competition 

امکانات جدید در فایرفاکس کوانتم
اشتراک‌ها
NET. چه چیزی است؟ پلتفرم یا فریم ورک؟

You'll often hear the terms framework and platform used interchangeably. This can be very confusing. You'll hear that .NET Core is a platform. Then you'll read that .NET Core is cross-platform. Next, you'll learn that .NET Core is a framework. And it doesn't help that framework is part of the name of the original full .NET Framework.

In this post, I explain the difference between frameworks and platforms and explain why sometimes you'll see .NET being referred to as a framework and other times see it referred to as a platform. 

NET. چه چیزی است؟ پلتفرم یا فریم ورک؟
مطالب
Parallel Programming در Vb.Net
حقیقتا تا این لحظه تو پروژه ای استفاده نکردم ولی فکر میکنم یادگیری و استفادش ضروری باشه. ظهورش برمیگرده به .net1 با عنوان Threading. اما کار با Threading  خیلی مشکله. من که اینطوری فکر میکنم. حالا با اصلاح کلاس Threading  و آمده task خیلی بهتر شده. 
گام اول:Threading.Tasks را بعنوان namespace اضافه کنید
یک مثال: این loop در نظر بگیرید
Private Sub work()
        While True
        End While
End Sub 
میخوام برا متد بالا یک task تعریف کنم
Task.Factory.StartNew(Sub() work()) 
مثال دوم: یک لیست  تعریف میکنم و با استفاد از یک loop میخوام اجزا لیستو چاپ کنم.
Dim lst As New List(Of String) From {"meysam", ".nettips", "vahidnasirii"} 
Parallel.ForEach(lst, Sub(item) Console.WriteLine("name:{0}", item)) 
مثال سوم: میخوام از این تکنیک تو linq استفاده کنیم:
Dim no(9) As Integer
   For i As Integer = 0 To no.Length - 1
        no(i) = i
   Next
Dim result As IEnumerable(Of Double) = no.AsParallel.Select(Function(q) Math.Pow(q, 3)).OrderBy(Function(q) q)
   For Each items In result
        Console.WriteLine(items)
   Next 
موفق باشید. 
مطالب
Blazor 5x - قسمت 33 - احراز هویت و اعتبارسنجی کاربران Blazor WASM - بخش 3- بهبود تجربه‌ی کاربری عدم دسترسی‌ها
در قسمت قبل، دسترسی به قسمت‌هایی از برنامه‌ی کلاینت را توسط ویژگی Authorize و همچنین نقش‌های مشخصی، محدود کردیم. در این مطلب می‌خواهیم اگر کاربری هنوز وارد سیستم نشده‌است و قصد مشاهده‌ی صفحات محافظت شده را دارد، به صورت خودکار به صفحه‌ی لاگین هدایت شود و یا اگر کاربری که وارد سیستم شده‌است اما نقش مناسبی را جهت دسترسی به یک صفحه ندارد، بجای هدایت به صفحه‌ی لاگین، پیام مناسبی را دریافت کند.


هدایت سراسری و خودکار کاربران اعتبارسنجی نشده به صفحه‌ی لاگین

در برنامه‌ی این سری، اگر کاربری که به سیستم وارد نشده‌است، بر روی دکمه‌ی Book یک اتاق کلیک کند، فقط پیام «Not Authorized» را مشاهده خواهد کرد که تجربه‌ی کاربری مطلوبی به‌شمار نمی‌رود. بهتر است در یک چنین حالتی، کاربر را به صورت خودکار به صفحه‌ی لاگین هدایت کرد و پس از لاگین موفق، مجددا او را به همین آدرس درخواستی پیش از نمایش صفحه‌ی لاگین، هدایت کرد. برای مدیریت این مساله کامپوننت جدید RedirectToLogin را طراحی می‌کنیم که جایگزین پیام «Not Authorized» در کامپوننت ریشه‌ای BlazorWasm.Client\App.razor خواهد شد. بنابراین ابتدا فایل جدید BlazorWasm.Client\Pages\Authentication\RedirectToLogin.razor را ایجاد می‌کنیم. چون این کامپوننت بدون مسیریابی خواهد بود و قرار است مستقیما داخل کامپوننت دیگری درج شود، نیاز است فضای نام آن‌را نیز به فایل BlazorWasm.Client\_Imports.razor اضافه کرد:
@using BlazorWasm.Client.Pages.Authentication
پس از آن، محتوای این کامپوننت را به صورت زیر تکمیل می‌کنیم:
@using System.Security.Claims

@inject NavigationManager NavigationManager

if(AuthState is not null)
{
    <div class="alert alert-danger">
        <p>You [@AuthState.User.Identity.Name] do not have access to the requested page</p>
        <div>
            Your roles:
            <ul>
            @foreach (var claim in AuthState.User.Claims.Where(c => c.Type == ClaimTypes.Role))
            {
                <li>@claim.Value</li>
            }
            </ul>
        </div>
    </div>
}

@code
{
    [CascadingParameter]
    private Task<AuthenticationState> AuthenticationState {set; get;}

    AuthenticationState AuthState;

    protected override async Task OnInitializedAsync()
    {
        AuthState = await AuthenticationState;
        if (!IsAuthenticated(AuthState))
        {
            var returnUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
            if (string.IsNullOrEmpty(returnUrl))
            {
                NavigationManager.NavigateTo("login");
            }
            else
            {
                NavigationManager.NavigateTo($"login?returnUrl={Uri.EscapeDataString(returnUrl)}");
            }
        }
    }

    private bool IsAuthenticated(AuthenticationState authState) =>
            authState?.User?.Identity is not null && authState.User.Identity.IsAuthenticated;
}
توضیحات:
در اینجا روش کار کردن با AuthenticationState را از طریق کدنویسی ملاحظه می‌کنید. در زمان بارگذاری اولیه‌ی این کامپوننت، بررسی می‌شود که آیا کاربر جاری، به سیستم وارد شده‌است یا خیر؟ اگر خیر، او را به سمت صفحه‌ی لاگین هدایت می‌کنیم. اما اگر کاربر پیشتر به سیستم وارد شده باشد، متن شما دسترسی ندارید، به همراه لیست نقش‌های او در صفحه ظاهر می‌شوند که برای دیباگ برنامه مفید است و دیگر به سمت صفحه‌ی لاگین هدایت نمی‌شود.

در ادامه برای استفاده از این کامپوننت، به کامپوننت ریشه‌ای BlazorWasm.Client\App.razor مراجعه کرده و قسمت NotAuthorized آن‌را به صورت زیر، با معرفی کامپوننت RedirectToLogin، جایگزین می‌کنیم:

<NotAuthorized>
    <RedirectToLogin></RedirectToLogin>
</NotAuthorized>
چون این کامپوننت اکنون در بالاترین سطح سلسله مراتب کامپوننت‌های تعریف شده قرار دارد، به صورت سراسری به تمام صفحات و کامپوننت‌های برنامه اعمال می‌شود.


چگونه دسترسی نقش ثابت Admin را به تمام صفحات محافظت شده برقرار کنیم؟

اگر خاطرتان باشد در زمان ثبت کاربر ادمین Identity، تنها نقشی را که برای او ثبت کردیم، Admin بود که در تصویر فوق هم مشخص است؛ اما ویژگی Authorize استفاده شده جهت محافظت از کامپوننت (attribute [Authorize(Roles = ConstantRoles.Customer)]@)، تنها نیاز به نقش Customer را دارد. به همین جهت است که کاربر وارد شده‌ی به سیستم، هرچند از دیدگاه ما ادمین است، اما به این صفحه دسترسی ندارد. بنابراین اکنون این سؤال مطرح است که چگونه می‌توان به صورت خودکار دسترسی نقش Admin را به تمام صفحات محافظت شده‌ی با نقش‌های مختلف، برقرار کرد؟
برای رفع این مشکل همانطور که پیشتر نیز ذکر شد، نیاز است تمام نقش‌های مدنظر را با یک کاما از هم جدا کرد و به خاصیت Roles ویژگی Authorize انتساب داد؛ و یا می‌توان این عملیات را به صورت زیر نیز خلاصه کرد:
using System;
using BlazorServer.Common;
using Microsoft.AspNetCore.Authorization;

namespace BlazorWasm.Client.Utils
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
    public class RolesAttribute : AuthorizeAttribute
    {
        public RolesAttribute(params string[] roles)
        {
            Roles = $"{ConstantRoles.Admin},{string.Join(",", roles)}";
        }
    }
}
در این حالت، AuthorizeAttribute سفارشی تهیه شده، همواره به همراه نقش ثابت ConstantRoles.Admin هم هست و همچنین دیگر نیازی نیست کار جمع زدن قسمت‌های مختلف را با کاما انجام داد؛ چون string.Join نوشته شده همین‌کار را انجام می‌دهد.
پس از این تعریف می‌توان در کامپوننت‌ها، ویژگی Authorize نقش دار را با ویژگی جدید Roles، جایگزین کرد که همواره دسترسی کاربر Admin را نیز برقرار می‌کند:
@attribute [Roles(ConstantRoles.Customer, ConstantRoles.Employee)]


مدیریت سراسری خطاهای حاصل از درخواست‌های HttpClient

تا اینجا نتایج حاصل از شکست اعتبارسنجی سمت کلاینت را به صورت سراسری مدیریت کردیم. اما برنامه‌های سمت کلاینت، به کمک HttpClient خود نیز می‌توانند درخواست‌هایی را به سمت سرور ارسال کرده و در پاسخ، برای مثال not authorized و یا forbidden را دریافت کنند و یا حتی internal server error ای را در صورت بروز استثنایی در سمت سرور.
فرض کنید Web API Endpoint جدید زیر را تعریف کرده‌ایم که نقش ادیتور را می‌پذیرد. این نقش، جزو نقش‌های تعریف شده‌ی در برنامه و سیستم Identity ما نیست. بنابراین هر درخواستی که به سمت آن ارسال شود، برگشت خواهد خورد و پردازش نمی‌شود:
namespace BlazorWasm.WebApi.Controllers
{
    [Route("api/[controller]")]
    [Authorize(Roles = "Editor")]
    public class MyProtectedEditorsApiController : Controller
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok(new ProtectedEditorsApiDTO
            {
                Id = 1,
                Title = "Hello from My Protected Editors Controller!",
                Username = this.User.Identity.Name
            });
        }
    }
}
برای مدیریت سراسری یک چنین خطای سمت سروری در یک برنامه‌ی Blazor WASM می‌توان یک Http Interceptor نوشت:
namespace BlazorWasm.Client.Services
{
    public class ClientHttpInterceptorService : DelegatingHandler
    {
        private readonly NavigationManager _navigationManager;
        private readonly ILocalStorageService _localStorage;
        private readonly IJSRuntime _jsRuntime;

        public ClientHttpInterceptorService(
                NavigationManager navigationManager,
                ILocalStorageService localStorage,
                IJSRuntime JsRuntime)
        {
            _navigationManager = navigationManager ?? throw new ArgumentNullException(nameof(navigationManager));
            _localStorage = localStorage ?? throw new ArgumentNullException(nameof(localStorage));
            _jsRuntime = JsRuntime ?? throw new ArgumentNullException(nameof(JsRuntime));
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            // How to add a JWT to all of the requests
            var token = await _localStorage.GetItemAsync<string>(ConstantKeys.LocalToken);
            if (token is not null)
            {
                request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
            }

            var response = await base.SendAsync(request, cancellationToken);

            if (!response.IsSuccessStatusCode)
            {
                await _jsRuntime.ToastrError($"Failed to call `{request.RequestUri}`. StatusCode: {response.StatusCode}.");

                switch (response.StatusCode)
                {
                    case HttpStatusCode.NotFound:
                        _navigationManager.NavigateTo("/404");
                        break;
                    case HttpStatusCode.Forbidden: // 403
                    case HttpStatusCode.Unauthorized: // 401
                        _navigationManager.NavigateTo("/unauthorized");
                        break;
                    default:
                        _navigationManager.NavigateTo("/500");
                        break;
                }
            }

            return response;
        }
    }
}
توضیحات:
با ارث‌بری از کلاس پایه‌ی DelegatingHandler می‌توان متد SendAsync تمام درخواست‌های ارسالی توسط برنامه را بازنویسی کرد و تحت نظر قرار داد. برای مثال در اینجا، پیش از فراخوانی await base.SendAsync کلاس پایه (یا همان درخواست اصلی که در قسمتی از برنامه صادر شده‌است)، یک توکن را به هدرهای درخواست، اضافه کرده‌ایم و یا پس از این فراخوانی (که معادل فراخوانی اصل کد در حال اجرای برنامه است)، با بررسی StatusCode بازگشتی از سمت سرور، کاربر را به یکی از صفحات یافت نشد، خطایی رخ داده‌است و یا دسترسی ندارید، هدایت کرده‌ایم. برای نمونه کامپوننت Unauthorized.razor را با محتوای زیر تعریف کرده‌ایم:
@page "/unauthorized"

<div class="alert alert-danger mt-3">
    <p>You don't have access to the requested resource.</p>
</div>
که سبب می‌شود زمانیکه StatusCode مساوی 401 و یا 403 را از سمت سرور دریافت کردیم، خطای فوق را به صورت خودکار به کاربر نمایش دهیم.

پس از تدارک این Interceptor سراسری، نوبت به معرفی آن به برنامه‌است که ... در ابتدا نیاز به نصب بسته‌ی نیوگت زیر را دارد:
dotnet add package Microsoft.Extensions.Http
این بسته‌ی نیوگت، امکان دسترسی به متدهای الحاقی AddHttpClient و سپس AddHttpMessageHandler را میسر می‌کند که توسط متد AddHttpMessageHandler است که می‌توان Interceptor سراسری را به سیستم معرفی کرد. بنابراین تعاریف قبلی و پیش‌فرض HttpClient را حذف کرده و با AddHttpClient جایگزین می‌کنیم:
namespace BlazorWasm.Client
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            //...

            // builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
            /*builder.Services.AddScoped(sp => new HttpClient
            {
                BaseAddress = new Uri(builder.Configuration.GetValue<string>("BaseAPIUrl"))
            });*/

            // dotnet add package Microsoft.Extensions.Http
            builder.Services.AddHttpClient(
                    name: "ServerAPI",
                    configureClient: client =>
                    {
                        client.BaseAddress = new Uri(builder.Configuration.GetValue<string>("BaseAPIUrl"));
                        client.DefaultRequestHeaders.Add("User-Agent", "BlazorWasm.Client 1.0");
                    }
                )
                .AddHttpMessageHandler<ClientHttpInterceptorService>();
            builder.Services.AddScoped<ClientHttpInterceptorService>();
            builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("ServerAPI"));

            //...
        }
    }
}
پس از این تنظیمات، در هر قسمتی از برنامه که با HttpClient تزریق شده کار می‌شود، تفاوتی نمی‌کند که چه نوع درخواستی به سمت سرور ارسال می‌شود، هر نوع درخواستی که باشد، تحت نظر قرار گرفته شده و بر اساس پاسخ دریافتی از سمت سرور، واکنش نشان داده خواهد شد. به این ترتیب دیگر نیازی نیست تا switch (response.StatusCode) را که در Interceptor تکمیل کردیم، در تمام قسمت‌های برنامه که با HttpClient کار می‌کنند، تکرار کرد. همچنین مدیریت سراسری افزودن JWT به تمام درخواست‌ها نیز به صورت خودکار انجام می‌شود.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-33.zip
نظرات مطالب
بررسی Bad code smell ها: کلاس بزرگ
مطلبی رو  اینجا دیدم که به نظر طنز هست و مرتبط به مطلب جاری
In the C++ world we don’t create a class witch contains 5 methods and 2 fields like in Java or C#. We do more. A lot more. Our class are consequent and take a lot of stuff inside because it is interesting to have a project with 10 files of 100 KB rather than 300 files of 3 KB organized with fuzzy concepts. We do OOP better. It’s more compact, it’s real, it just works