CSharp - Other

Handling Exceptions Globally in ASP.NET Core

app.UseExceptionHandler(errorApp =>
{
    errorApp.Run(async context =>
    {
        var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
        var exception = exceptionHandlerPathFeature?.Error;

        await context.Response.WriteAsync("An error occurred.");
    });
});

High-Performance SMS Sending with RabbitMQ and MassTransit in .NET

Design SMS Message Payload

Use a structured payload to handle metadata and SMS content:

{
  "api_token": "your-api-token",
  "sid": "SERVICE_ID",
  "msisdn": "88019XXXXXXXX",
  "sms_body": "Hello, your OTP is 123456",
  "csms_id": "unique_id_for_tracking"
}


Create a WebAPI project

dotnet new webapi -n MyWebApiProject


NuGet Packages:

Install the necessary MassTransit and RabbitMQ packages:

dotnet add package MassTransit
dotnet add package MassTransit.RabbitMQ


Config RabbitMQ

Configure the connection to your RabbitMQ instance in appsettings.json:

"RabbitMQ": {
    "HostName": "your_rabbitmq_host",
    "UserName": "your_username",
    "Password": "your_password"
}


Create SMS Message Model

Define a model for your SMS messages:

public class SmsMessage
{
        public string api_token { get; set; }
        public string sid { get; set; }
        public string msisdn { get; set; }
        public string sms_body { get; set; }
        public string csms_id { get; set; }
}


Producer (API Gateway):

Publish SMS messages to the RabbitMQ queue:

public class SmsService : ISmsService
{
    private readonly IPublishEndpoint _publishEndpoint;

    public SmsService(IPublishEndpoint publishEndpoint)
    {
        _publishEndpoint = publishEndpoint;
    }

    public async Task SendSmsAsync(SmsMessage message)
    {
        await _publishEndpoint.Publish(message);
    }
}


Consumer (SMS Worker):

Consume messages from the queue and send SMS using a chosen provider:

public class SmsWorker : IConsumer<SmsMessage>
{
    private readonly ISmsProvider _smsProvider;

    public SmsWorker(ISmsProvider smsProvider)
    {
        _smsProvider = smsProvider;
    }

    public async Task Consume(ConsumeContext<SmsMessage> context)
    {
        var message = context.Message;
        await _smsProvider.SendSmsAsync(message.To, message.From, message.Body);
    }
}


SMS Provider Interface:

Define an interface for interacting with different SMS providers:

public interface ISmsProvider
{
    Task SendSmsAsync(string to, string from, string body);
}


MassTransit Configuration:

Configure MassTransit in your Program.cs:

builder.Services.AddMassTransit(x =>
{
    x.AddConsumer<SmsWorker>();
    x.UsingRabbitMq((context, cfg) =>
    {
        cfg.Host(configuration["RabbitMQ:HostName"], "/", h =>
        {
            h.Username(configuration["RabbitMQ:UserName"]);
            h.Password(configuration["RabbitMQ:Password"]);
        });
        cfg.ReceiveEndpoint("sms_queue", e =>
        {
            e.ConfigureConsumer<SmsWorker>(context);
        });
    });
});
builder.Services.AddMassTransitHostedService();


Run & Test

How a Simple 2-Line Change Made Our .NET App OWASP Top 10 Compliant

Program.cs

app.UseExceptionHandler("/error");
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", false);


ErrorController.cs

[Route("error")]
public class ErrorController : ControllerBase
{
    [HttpGet, HttpPost]
    public IActionResult HandleError()
    {
        return Problem(
            detail: null,
            title: "An unexpected error occurred.",
            statusCode: 500);
    }
}

How can I split a string with a string delimiter ?

string[] tokens = str.Split(new[] { "token" }, StringSplitOptions.None);

How do I get the complete virtual path of an ASP.NET application ?

Request.Url.GetLeftPart(UriPartial.Authority) + Request.ApplicationPath

How do you convert epoch time ?

DateTimeOffset dateTimeOffsetBySecond = DateTimeOffset.FromUnixTimeSeconds(epochSeconds);

DateTimeOffset dateTimeOffsetByMillisecond = DateTimeOffset.FromUnixTimeMilliseconds(epochMilliseconds);

How should I check if a user is authenticated in MVC ?

return (Request.IsAuthenticated) ? true : false;

How to Create a Local Secret Data Vault for .NET Applications: SQLiteCipher with Dapper

Using a Configuration File


You can also store secrets in a local configuration file and encrypt it.


Create a JSON file (e.g., appsettings.secret.json):

{
    "SecretKey": "YourSecretValue"
}


Also, Update appsetting.json

"ConnectionStrings": {
  "DefaultConnection": "localvault.db"
}


Load the Configuration at program.cs:

using LocalVaultWebApi.Data;
using Microsoft.Extensions.DependencyInjection;
using SQLitePCL;
using System;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();

//Batteries_V2.Init(); // Initialize SQLitePCL (needed for SQLCipher)

// Load secrets file
var secretConfig = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.secret.json", optional: true)
    .Build();

var secretValue = secretConfig["SecretKey"];

builder.Services.AddSingleton<DapperContext>(provider =>
    new DapperContext(builder.Configuration, secretValue));

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAll",
        builder =>
        {
            builder.AllowAnyOrigin()
                   .AllowAnyMethod()
                   .AllowAnyHeader();
        });
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
else
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseCors("AllowAll");

app.UseHttpsRedirection();

app.MapControllers();

app.Run();


Encrypt the Configuration: You may want to encrypt the sensitive values in this file to prevent unauthorized access.


Using a Local Database (SQLite with SQLiteCipher)


If you require a more complex solution, consider storing secrets in a local database.


Set Up SQLite: Install the SQLite package via NuGet:

dotnet add package Dapper
dotnet add package Microsoft.Data.Sqlite
dotnet add package SQLitePCLRaw.bundle_e_sqlcipher


Create a DapperContext.cs:

using Microsoft.Data.Sqlite;
using System.Data;
using System.Data.SQLite;

namespace LocalVaultWebApi.Data
{
    public class DapperContext
    {
        private readonly IConfiguration _configuration;
        private readonly string _connectionString;
        private readonly string _secretValue;
        public DapperContext(IConfiguration configuration, string secretValue)
        {
            _configuration = configuration;
            _secretValue = secretValue;
            _connectionString = _configuration.GetConnectionString("DefaultConnection");

            // Initialize SQLiteCipher (this can be done here or in a more central location)
            SQLitePCL.Batteries_V2.Init();  // This ensures SQLiteCipher is ready
        }

        public IDbConnection CreateConnection()
        {
            var connectionString = new SqliteConnectionStringBuilder
            {
                DataSource = _connectionString,  // Path to your SQLite database file
                Mode = SqliteOpenMode.ReadWriteCreate  // Allow reading and writing to the DB
            }.ToString();

            var connection = new SqliteConnection(connectionString);
            connection.Open();

            // Set the encryption key (PRAGMA key) after opening the connection
            using (var command = connection.CreateCommand())
            {
                command.CommandText = $"PRAGMA key = '{_secretValue}';";  // Set your encryption password
                command.ExecuteNonQuery();
            }

            return connection;  // Return the opened, encrypted connection ready for Dapper
        }
    }
}


Create Secret Class as a Model:

public class Secret
{
    public int Id { get; set; }
    public string Key { get; set; }
    public string Value { get; set; }
}

public class SecretRequest
{
    public string Key { get; set; }
    public string Value { get; set; }
}


Create a Controller to Access Secret Vualt


[Route("api/[controller]/[action]")]
[ApiController]
public class SecretController : ControllerBase
{
    private readonly DapperContext _dapperContext;

    public SecretController(DapperContext dapperContext)
    {
        _dapperContext = dapperContext;
    }

    [HttpGet]
    public IActionResult CreateDatabase()
    {
        var result = 0;

       using var connection = _dapperContext.CreateConnection();
       try
        {
            string sql2 = "CREATE TABLE IF NOT EXISTS Secret (Id INTEGER PRIMARY KEY, key TEXT,value TEXT)";

            connection.Execute(sql2);
        }
        catch (Exception ex) 
        { 
        
        }
        return Ok(true);
    }

    [HttpGet]
    public IActionResult GetSecret(string key)
    {
        var result =new Secret();


        using (var connection = _dapperContext.CreateConnection())
        {              

            string sql = "SELECT * From Secret Where key=@key";

            result = connection.QueryFirstOrDefault<Secret>(sql, new { key });
        }

        return Ok(result);
    }

    [HttpPost]
    public IActionResult StoreSecret(SecretRequest request_data)
    {
        var result = 0;

        using (var connection = _dapperContext.CreateConnection())
        {
            
            string sql = "INSERT INTO Secret(Key,Value) VALUES (@Key,@Value)";

            result =  connection.Execute(sql, new { request_data.Key, request_data.Value });
        }

        return Ok(result);
    }

}

How to Hide Header Values in ASP.NET Core MVC Web Applications

Server Header

We can remove the Server header by adding the line “UseKestrel(c => c.AddServerHeader = false)” in the CreateWebHostBuilder method in the Program.cs class.


public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
  WebHost.CreateDefaultBuilder(args)
    .UseKestrel(c => c.AddServerHeader = fasle)
    .UserStartUp<Startup>();


X-Powered-By Header

To remove the X-Powered-By header, we need to add a web configuration file, and in that web config file, we need to add an element that removes the X-Powered-By element under <system.webServer>.


<system.webServer>
  <httpProtocol>
    <customHeaders>
      <remove name="X-Powered-By" />
    </customHeaders>
  </httpProtocol>
</system.webServer>


X-Aspnet-Version Header

To remove the X-Aspnet-Version header, make the following change in the web configuration file:


<system.web>
  <httpRuntime enableVersionHeader="false" />
</system.web>


X- AspnetMvc-Version Header

To remove the X-AspnetMvc-Version header, add the following line where the application starts in Global.aspx:


protected void Application_Start(object sender, EventArgs e)
{
  MvcHandler.DisableMvcResponseHeader = true;
}

How to integrate Google Authenticator in ASP .NET MVC Project ?

Initialize

  1. Install nuget GoogleAuthenticator.


Models

  • UserLoginModel.cs
public class UserLoginModel
{
   //Field to store the Username
   public string Username { get; set; }

   //Field to store the Password
   public string Password { get; set; }
}


Controllers

  • LoginController.cs
public class LoginController : Controller
{
  private const string key = "dfg7568!@@)(";

  public ActionResult Login()
  {
    return View();
  }

  [HttpPost]
  publiv ActionResult Login(UserLoginModel login)
  {
    string message = "";
    bool status = false;

    if("Corrent credential")
    {
      //It indicates 2FA form
      status = true; 
      message = "2FA Verification";
      Session["Username"] = login.Username;

      //2FA Setup
      TwoFactorAuthenticator tfa = new TwoFactorAuthenticator();
      string uniqueKeyforUser = (login.Username + key);
      Session["UserUniqueKey"] = UserUniqueKey;
      var setupInfo = tfa.GenerateSetupCode("Name display in Google Authenticator Application", login.Username, UserUniqueKey, 300, 300);

      ViewBag.BarcodeImageUrl = setupInfo.QrCodeSetupImageUrl;
      ViewBag.SetupCode = setupInfo.ManualEntryKey;
    }
    else
    {
      message = "Invalid credential";
    }

    ViewBag.Message = message;
    ViewBag.Status = status;

    return View();
  }

  public bool Verify2FA()
  {
    var token = Request["passcode"];

    TwoFactorAuthenticator tfa = new TwoFactorAuthenticator();

    string UserUniqueKey = Session["UserUniqueKey"].toString();

    bool isValid = tfa.ValidateTwoFactorPIN(UserUniqueKey, token);

    if (isValid)
    {
      Session["IsValidAuthentication"] = true;
      return RedirectToAction("MyProfile", "Profile");
    }

    return RedirectToAction("Login", "Home");
  }
}
  • ProfileController.cs
public class ProfileController : Controller
{
  public ActionResult MyProfile
  {
    if (Session["Username"] == null || Session["IsValidAuthentication"] == null || !(bool)Session["IsValidAuthentication"])
    {
      return RedirectToAction("Login", "Home");
    }
    
    ViewBag.Message = "Welcome " + Session["Username"].ToString();
    
    return View();
  }
}


View

  • Login.cshtml
@model GoogleAuthenticator.ViewModel.UserLoginModel
@{
  ViewBag.Title = "UserLogin";
}

<h2>Login</h2>

@if (ViewBag.Status == null || !ViewBag.Status)
{
  <div>@ViewBag.Message</div>
  <div>
    @using (Html.BeginForm())
    {
      <div class="form-group">
        <label for="Username">Username : </label>
        @Html.TextBoxFor(a => a.Username, new { @class = "form-control"})
      </div>
      <div class="form-group">
        <label for="Password">Password : </label>
        @Html.TextBoxFor(a => a.Password, new { @class="form-control", type="password"})
      </div>
      <input type="submit" value="Login" class="btn btn-default" />
    }
  </div>
}
else
{
  <!--Show 2FA verification form here-->
  <div>@ViewBag.Message</div>
  <div> 
    <img src="@ViewBag.BarcodeImageUrl"/> 
  </div>
  <div> Manual Setup Code : @ViewBag.SetupCode </div>
  <div>
    @using (Html.BeginForm("Verify2FA","Home", FormMethod.Post))
    {
      <input type="text" name="passcode" />
      <input type="submit" class="btn btn-success" />
    }
  </div>
}
  • MyProfile.cshtml
@{
  ViewBag.Title = "AuthorizedProfile";
}
<h2>Authorized Profile</h2>

<h5>@ViewBag.Message</h5>

How to update only one field using Entity Framework ?

using (var db = new context())
{
  db.table.Attach(user);
  db.Entry(model).Property(x => x.column).IsModified = true;
  db.SaveChanges();
}

HTTP Cookies

Add

Response.Cookies.Add(new HttpCookie("Name", value));


Get

Request.Cookies["Name"].Value;