European ASP.NET 4.5 Hosting BLOG

BLOG about ASP.NET 4, ASP.NET 4.5 Hosting and Its Technology - Dedicated to European Windows Hosting Customer

European ASP.NET Core 9.0 Hosting - HostForLIFE :: ASP.NET Web.Config: Redirects, Security, and URL Rewriting Explained

clock October 2, 2025 07:20 by author Peter

In ASP.NET applications, the Web.config file is the heart of configuration. It allows us to define application settings, connection strings, error handling, session timeouts, security rules, and even URL rewriting without touching our C# backend code.

In this blog, we’ll explore:

  • The concept of Web.config
  • Redirect usage through Web.config
  • Page protection and security settings
  • URL rewriting for clean SEO-friendly URLs
  • Frontend usages (single name, double name, third parameter, redirects)

And finally, we’ll go line by line through a real-world Web.config example.

What is Web.config?

  • A special XML file used in ASP.NET applications.
  • It defines configuration settings such as database connections, authentication, authorization, custom errors, and security headers.
  • Stored at the root of the application.

What is <customErrors> in ASP.NET?

  • <customErrors> is a configuration element in Web.config that controls how errors are handled and displayed in ASP.NET applications.
  • Instead of showing raw ASP.NET/YELLOW ERROR PAGES (with stack trace), we can show friendly error pages (like Errorpage.aspx ).
  • This improves user experience and also protects sensitive error details from hackers.

1. Three Ways (Modes) of Using <customErrors>
customErrors with Mode="Off" and Status Codes Defined

<customErrors mode="Off">
  <error statusCode="400" redirect="Errorpage.aspx" />
  <error statusCode="403" redirect="Errorpage.aspx" />
  <error statusCode="404" redirect="Errorpage.aspx" />
  <error statusCode="500" redirect="Errorpage.aspx" />
</customErrors>


Explanation

  • mode="Off" → Application will display detailed ASP.NET error pages (stack trace, line numbers).
  • But here, specific error code redirections are defined.
  • If you visit a page that doesn’t exist → it redirects to Errorpage.aspx .
  • This is generally useful in development environments, where you need to debug errors but also want some controlled redirections.
  • If a 404 Not Found error occurs → User is redirected to Errorpage.aspx .
  • If a 500 Internal Server Error occurs → Same redirection.

Purpose: Developer debugging + handling user-friendly redirects.

2. customErrors with Mode="Off" (Single Line)
<customErrors mode="Off"/>

Explanation

  • Application will always show detailed error messages .
  • No redirection is done.
  • Useful only in local development/testing — never recommended for production.

Purpose: Debugging only (not secure).

3. customErrors with Mode="On"
<customErrors mode="On"/>

Explanation

  • Application will hide detailed error messages.
  • Instead, it will show friendly custom error pages (like Errorpage.aspx).
  • Secure approach for production environments.

Purpose: Protect sensitive error details from users/hackers, show professional error pages.

 

Quick Comparison

Mode ValueWhat HappensBest Used In
Off (with mapping) Shows detailed ASP.NET errors but allows redirection for specific status codes Development/Debugging
Off (without mapping) Shows raw errors always Local debugging only
On Shows user-friendly custom error pages (e.g., Errorpage.aspx) Production (secure)

Final Takeaway:

 

  • Mode=Off → Developer Mode (debugging)
  • Mode=On → Production Mode (secure, user-friendly)
  • Mode=Off with status codes → Debugging + Controlled Redirection

Securing Pages with Web.config
Code Section

<validation validateIntegratedModeConfiguration="false"/>
<httpProtocol>
  <customHeaders>
    <remove name="X-AspNet-Version"/>
    <remove name="X-AspNetMvc-Version"/>
    <remove name="X-Powered-By"/>
    <add name="Body-Count" value="Ice-T"/>
    <add name="Access-Control-Allow-Credentials" value="true"/>
    <add name="Access-Control-Allow-Headers" value="content-type"/>
    <add name="X-Content-Type-Options" value="nosniff"/>
    <add name="X-XSS-Protection" value="1; mode=block"/>
    <add name="Cache-Control" value="no-cache, no-store, must-revalidate"/>
    <add name="X-Frame-Options" value="SAMEORIGIN"/>
    <add name="X-Permitted-Cross-Domain-Policies" value="none"/>
    <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains"/>
    <add name="Permissions-Policy" value="accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"/>
  </customHeaders>
</httpProtocol>

Line-by-Line Explanation
1. Validation Mode

<validation validateIntegratedModeConfiguration="false"/>

  • Concept: Controls validation of the IIS Integrated Pipeline configuration.
  • Why Required: Sometimes, older Web.config settings are not compatible with IIS 7+ Integrated Pipeline mode.
  • Purpose: Setting false tells ASP.NET not to validate older settings → prevents unnecessary runtime errors.

2. Removing Headers (Information Disclosure Prevention)
<remove name="X-AspNet-Version"/>
<remove name="X-AspNetMvc-Version"/>
<remove name="X-Powered-By"/>

  • Concept: By default, ASP.NET/IIS sends these headers in HTTP responses.
  • Why required: Hackers can identify framework versions and target known vulnerabilities.
  • Purpose: Security through obfuscation → don’t expose technology stack details.

3. Adding Custom Headers
a) Fake Header (Obfuscation / Branding)
<add name="Body-Count" value="Ice-T"/>

  • Concept: Adds a custom header with an arbitrary value.
  • Why Required: Not mandatory, but can be used for tracking or branding.
  • Purpose: Demonstration/fun — here it’s "Ice-T" (artist’s name), doesn’t affect functionality.

b) CORS Headers (Cross-Origin Resource Sharing)
<add name="Access-Control-Allow-Credentials" value="true"/>
<add name="Access-Control-Allow-Headers" value="content-type"/>


Concept: Defines rules for cross-origin requests.
Why required: If your API is called from JavaScript on another domain.


Purpose

  • Allow-Credentials=true → lets cookies/auth headers be sent in cross-domain requests.
  • Allow-Headers=content-type → allows Content-Type header in requests.

c) Content Security Headers
<add name="X-Content-Type-Options" value="nosniff"/>

  • Concept : Stops browsers from MIME-type sniffing.
  • Why Required: Prevents malicious file uploads from being misinterpreted (e.g., .jpg running as script).
  • Purpose: Protects against content-type-based attacks.

d) XSS Protection
<add name="X-XSS-Protection" value="1; mode=block"/>
Concept: Activates the browser’s built-in XSS (Cross-Site Scripting) filter.

  • Why Required: Prevents malicious scripts from executing in the browser.
  • Purpose: Block pages if XSS is detected.

e) Cache Control
<add name="Cache-Control" value="no-cache, no-store, must-revalidate"/>

  • Concept : Prevents caching of sensitive content.
  • Why Required : Avoids storing confidential pages in browser/proxy cache.
  • Purpose : Ensures always fresh content, good for banking/financial apps.

f) Clickjacking Protection
<add name="X-Frame-Options" value="SAMEORIGIN"/>

  • Concept: Controls iframe embedding.
  • Why Required: Attackers can load your site inside a hidden iframe and trick users (clickjacking).
  • Purpose: Allows embedding only from the same domain.

g) Cross-Domain Policy
<add name="X-Permitted-Cross-Domain-Policies" value="none"/>

  • Concept: Restricts Adobe Flash, PDF, or other plugins from making cross-domain requests.
  • Why Required: Stops unauthorized access from plugins.
  • Purpose: Set to none for strictest security.

h) Strict Transport Security (HSTS)
<add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains"/>

  • Concept: Forces browser to use HTTPS for all requests.
  • Why Required: Prevents downgrade attacks and cookie hijacking on HTTP.
  • Purpose: Enforces HTTPS for 1 year ( 31536000 seconds ).

i) Permissions Policy (Feature Control)
<add name="Permissions-Policy" value="accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"/>

  • Concept: Controls access to powerful browser APIs.
  • Why Required: Protects privacy by blocking unnecessary device features.
  • Purpose: Disables camera, microphone, location, USB, etc., unless explicitly allowed.

Summary Table

SettingWhy RequiredPurpose
validateIntegratedModeConfiguration Avoids IIS config errors Compatibility
Remove X-* headers Prevents info leakage Security
Access-Control-* Allow safe CORS Cross-domain support
X-Content-Type-Options Stop MIME sniffing Content security
X-XSS-Protection Prevent script injection XSS defense
Cache-Control Avoid sensitive data caching Privacy & security
X-Frame-Options Prevent clickjacking UI protection
X-Permitted-Cross-Domain-Policies Restrict plugins Security
Strict-Transport-Security Force HTTPS Encryption
Permissions-Policy Limit device APIs Privacy & control

URL Rewriting in Web.config

Instead of exposing raw .aspx paths, you can provide clean SEO-friendly URLs .

Your rewriter section:

<rewriter>
  <rewrite url="/about-us" to="About.aspx" processing="stop"/>
  <rewrite url="/contact-us" to="/contact_us.aspx" processing="stop"/>
  <rewrite url="/faq" to="faq.aspx" processing="stop"/>
</rewriter>

XML

Usage in frontend:

<ul>
  <li><a href="/faq">FAQ's</a></li>
  <li><a href="/about-us">About Us</a></li>
  <li><a href="/contact-us">Contact Us</a></li>
</ul>

HTML

Even though the user clicks /about-us , it internally loads About.aspx .
Frontend Rewrite Examples
Single Name Rewrite

<rewrite url="/faq" to="faq.aspx" processing="stop"/>

XML

Usage

<a href="/faq">FAQ's</a>

HTML
Double Name Rewrite

<rewrite url="/productlistpage/([^/]+)?$" to="/productlistpage/products.aspx?opt=$1" processing="stop"/>

XML

Usage

<a href="/productlistpage/equity">Equity</a>
<a href="/productlistpage/mutual-fund">Mutual Funds</a>

HTML
Third Parameter Passed

<rewrite url="/productlistpage/([^/]+)/([^/]+)?$" to="/productlistpage/frequency.aspx?opt=$1&amp;freq=$2" processing="stop"/>

XML

Usage

<a href="/productlistpage/delayed/3">Delayed</a>
<a href="/productlistpage/historical/2">Historical</a>
<a href="/productlistpage/eod/1">End of the Day</a>

HTML
Redirect with Multiple Parameters

<rewrite url="/productlistpage/([^/]+)/([^/]+)/([^/]+)/([^/]+)?$"
     to="/indexlist.aspx?section1=$1&amp;subsection1=$2&amp;pagename1=$3&amp;srno1=$4" processing="stop"/>

XML

This allows structured redirection for advanced product details.
<appSettings> Section Examples

The <appSettings> block in Web.config (or App.config) is used to store key–value pairs for application-wide settings.
It makes configuration easier to manage without hardcoding values in your C# code.

Example Breakdown

<appSettings>
    <add key="MailServer" value="1443.708.661.165" />
    <add key="Liveurl" value="https://www.blackbox.ai/chat/aXzGGY0"/>
    <add key="emailWhitelist" value="[email protected]"/>
    <add key="updatehours" value="4"/>
    <add key="AllowedIPs" value="122.168.1.003,122.168.1.888"/>
</appSettings>

Explanation
MailServer → Stores your mail server IP (instead of hardcoding SMTP IP in code).
    Purpose: Used by your app to send emails.

Liveurl → Stores an external service URL.
    Purpose: Centralized URL for API calls or redirections.

emailWhitelist → List of allowed emails.
    Purpose: Security check – only these emails can access/send certain features.

updatehours → Numeric setting (like refresh/update interval)
    Purpose: Defines how often the app should refresh data (in hours).

AllowedIPs → List of IPs that can access the application.
    Purpose: Security filtering based on client IP addresses.

Instead of hardcoding, your C# code retrieves values using:
  string mailServer = ConfigurationManager.AppSettings["MailServer"];


<connectionStrings> Section

The <connectionStrings> block is used to define database connection details.
This helps in connecting your .NET application to SQL Server (or other DBs) without storing credentials in code.

Example Breakdown

<connectionStrings>
    <add name="DBName1" connectionString="Data Source=softsql;Initial Catalog=indiasector;Connect TimeOut=60; Max Pool Size=10000;user id=sa;password=capmark@09" providerName="System.Data.SqlClient"/>
    <add name="DBName2" connectionString="Data Source=softsql;Initial Catalog=CommonDB;Connect TimeOut=60; Max Pool Size=10000;user id=sa;password=capmark@09" providerName="System.Data.SqlClient"/>
    <add name="DBName3" connectionString="Data Source=softsql;Initial Catalog=CmotsAPI;Connect TimeOut=60; Max Pool Size=10000 ;User ID=sa;Password=capmark@09" providerName="System.Data.SqlClient"/>
</connectionStrings>


Explanation
  • name="DBName1" → Identifier used in code.
  • string conn = ConfigurationManager.ConnectionStrings["DBName1"].ConnectionString;
  • Data Source=softsql → SQL Server name or IP.
  • Initial Catalog=indiasector → Database name to connect.
  • Connect Timeout=60 → Timeout in seconds if connection fails.
  • Max Pool Size=10000 → Max concurrent connections allowed in pool.
  • user id / password → Database login credentials.
  • providerName=" System.Data .SqlClient" → Provider type (here it’s SQL Server).
Each <add> here represents one DB connection.
Your application may connect to multiple databases ( indiasector , CommonDB , CmotsAPI ) depending on modules.
<system.web>

<system.web>
    <sessionState timeout="60"></sessionState>
</system.web>


Explanation
<sessionState timeout="60">

Purpose: Controls how long a user’s session remains active (in minutes).
timeout="60" → The session will expire after 60 minutes of inactivity.

Example: If a user logs in and then doesn’t perform any action for 1 hour, their session automatically ends and they may be logged out.
<system.web><rewrite> ... </rewrite></system.web>

<system.web>
  <rewrite>
      <outboundRules>
        <rule name="Remove RESPONSE_Server">
          <match serverVariable="RESPONSE_Server" pattern=".+"/>
          <action type="Rewrite" value=""/>
        </rule>
        <rule name="Remove X-Powered-By">
          <match serverVariable="RESPONSE_X-Powered-By" pattern=".+"/>
          <action type="Rewrite" value="pagename"/>
        </rule>
      </outboundRules>
  </rewrite>

  <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="2147483647"/>
      </requestFiltering>
  </security>
</system.web>

Explanation
<rewrite>

This section belongs to IIS URL Rewrite Module.

It allows you to manipulate incoming requests and outgoing responses .

Here you are using outbound rules → which modify the HTTP response headers before they are sent to the client.

1. <rule name="Remove RESPONSE_Server">
Purpose: Removes the Server header from the HTTP response.
<match serverVariable="RESPONSE_Server" pattern=".+"/> → Matches any value of the Server header.
<action type="Rewrite" value=""/> → Rewrites it with an empty value (effectively removing it).


Benefit: Hides server technology (IIS, Apache, etc.) → improves security by preventing attackers from knowing which server you are using.

2. <rule name="Remove X-Powered-By">
Purpose: Modifies the X-Powered-By header in the HTTP response.
<match serverVariable="RESPONSE_X-Powered-By" pattern=".+"/> → Matches any existing value of the X-Powered-By header.
<action type="Rewrite" value="ABCIPO"/> → Replaces it with a custom value "ABCIPO" .


Benefit: Instead of exposing .NET / IIS version , you can mask it with your own text for security through obfuscation.
<security>


The security section applies additional security configurations to requests.

3. <requestFiltering>
Purpose: Defines restrictions on requests to protect your application from malicious uploads or attacks.
<requestLimits maxAllowedContentLength="2147483647"/>

Sets the maximum allowed request size.

Value 2147483647 = 2 GB (maximum limit for IIS in bytes).

This allows very large file uploads (like videos, ZIPs, datasets).

Be careful → very large uploads can impact server performance or be abused for DoS attacks .


European ASP.NET Core 9.0 Hosting - HostForLIFE :: Plan for Responding to Security Vulnerabilities in ASP.NET Core Applications

clock September 23, 2025 08:49 by author Peter

No application is completely impervious to security risks. Even with the finest safe coding techniques, vulnerabilities, configuration errors, and third-party dependencies can still be exploited by attackers. To promptly identify, address, and recover from security breaches, each ASP.NET Core application needs a clear Incident Response Plan (IRP).


The procedures, resources, and sample code for creating an incident response plan designed especially for ASP.NET Core applications are all covered in this article.

What Is an Incident Response Plan?
An Incident Response Plan (IRP) is a documented, structured approach to handling security breaches. Its purpose is to:

  • Detect suspicious or malicious activity quickly.
  • Contain and mitigate damage.
  • Eradicate the root cause of the breach.
  • Recover normal operations.
  • Learn from incidents to prevent future breaches.
  • Key Phases of Incident Response in ASP.NET Core

1. Preparation
Before a breach happens, ensure:

  • Security logging and monitoring are enabled.
  • Alerts are configured for unusual activities (e.g., failed logins, suspicious API calls).
  • Teams know their roles and escalation paths.
  • Backups and recovery procedures are in place.

Example: Enable structured security logging with Serilog or Application Insights:
Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .WriteTo.File("logs/security-.log", rollingInterval: RollingInterval.Day)
    .CreateLogger();


2. Identification
Detect and confirm the breach.
Common signs in ASP.NET Core apps include:

  • Multiple failed login attempts (brute force).
  • Unauthorized access to protected endpoints.
  • Sudden spikes in API traffic (possible DoS).
  • Unexpected changes in configuration or database.

Example: Log and flag repeated failed logins:
_logger.LogWarning("Failed login attempt {Count} for {Username} from {IP}",
                   attemptCount, username, ip);
if (attemptCount > 5)
{
    _logger.LogError("Possible brute-force attack detected from IP {IP}", ip);
    // Trigger an alert or block IP temporarily
}

3. Containment
Limit the scope of the breach while keeping services running.
Disable compromised accounts.

Block malicious IPs temporarily.

Isolate affected microservices or APIs.

Example: Blocking an IP using ASP.NET Core middleware:
public class IpBlockMiddleware
{
    private readonly RequestDelegate _next;
    private static readonly HashSet<string> BlockedIps = new();

    public IpBlockMiddleware(RequestDelegate next) => _next = next;

    public async Task Invoke(HttpContext context)
    {
        var ip = context.Connection.RemoteIpAddress?.ToString();
        if (BlockedIps.Contains(ip))
        {
            context.Response.StatusCode = 403;
            await context.Response.WriteAsync("Access denied.");
            return;
        }

        await _next(context);
    }

    public static void BlockIp(string ip) => BlockedIps.Add(ip);
}


4. Eradication
Remove the root cause of the breach.

  • Patch vulnerable dependencies (e.g., NuGet packages).
  • Fix misconfigured CORS, authentication, or authorization policies.
  • Remove injected malicious code or unauthorized files.

Use OWASP Dependency Check or dotnet list package --outdated to identify vulnerabilities.

5. Recovery

Restore services to normal operations.

  • Rotate compromised keys, tokens, or certificates.
  • Restore data from clean backups if tampered with.
  • Gradually reintroduce blocked IPs/users after ensuring safety.
  • Monitor closely for recurrence.

6. Lessons Learned
After the incident, perform a post-mortem analysis:

  • What was the root cause?
  • How was it detected?
  • Were response times acceptable?
  • What controls can prevent recurrence?

Document findings and improve the incident response playbook.

Automating Incident Response in ASP.NET Core
You can integrate automated workflows for faster response:

  • Azure Sentinel or AWS GuardDuty—Automatically trigger alerts and block malicious IPs.
  • Webhook-based alerts – Notify your team on Slack/Teams when security anomalies occur.
  • Custom ASP.NET Core filters—Enforce consistent logging and security checks across controllers.

Example: Global exception logging with IExceptionFilter:
public class SecurityExceptionFilter : IExceptionFilter
{
    private readonly ILogger<SecurityExceptionFilter> _logger;

    public SecurityExceptionFilter(ILogger<SecurityExceptionFilter> logger)
    {
        _logger = logger;
    }

    public void OnException(ExceptionContext context)
    {
        _logger.LogError(context.Exception,
                         "Security exception at {Path}",
                         context.HttpContext.Request.Path);
    }
}


Incident Response Checklist for ASP.NET Core Apps

  • Enable detailed, structured security logging.
  • Monitor logs using SIEM tools (Azure Sentinel, ELK, Splunk).
  • Configure alerts for brute-force and suspicious activity.
  • Implement containment mechanisms (IP blocking, account disabling).
  • Regularly patch dependencies and frameworks.
  • Practice recovery via backups and incident simulations.
  • Conduct post-incident reviews and update your IRP.

Conclusion
A well-prepared incident response plan ensures that your ASP.NET Core applications can withstand breaches and recover with minimal damage. By combining proactive monitoring, structured logging, automated containment, and post-incident learning, your development and security teams can respond to threats effectively.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: How to Use ASP.NET Core Data Protection APIs?

clock September 18, 2025 08:19 by author Peter

The Data Protection API (DPAPI), which is included into ASP.NET Core, offers developers easy, safe, and expandable cryptographic operations. The framework itself makes extensive use of it internally, for instance, to safeguard OAuth state, CSRF tokens, and authentication cookies. This post describes how to use ASP.NET Core's Data Protection APIs to safeguard your private information.

The Data Protection API: What is it?
ASP.NET Core's Data Protection API is a cryptography framework made to:

  • Encrypt and decrypt data, such as tokens and connection strings.
  • Securely handle keys (with support for key rotation).
  • Assure confidentiality and resistance to tampering.
  • Connect with ASP.NET Core components with ease.

You depend on a managed service that takes care of algorithm selection, key management, and lifetime automatically rather than handling cryptography by yourself.

Setting Up Data Protection in ASP.NET Core
The Data Protection service is registered within Program.cs or Startup.cs.
var builder = WebApplication.CreateBuilder(args);

// Register Data Protection services
builder.Services.AddDataProtection();

builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();

This sets up the Data Protection system with in-memory key storage (by default).

Protecting and Unprotecting Data
Create a service that uses the IDataProtector interface:
using Microsoft.AspNetCore.DataProtection;

public class EncryptionService
{
    private readonly IDataProtector _protector;

    public EncryptionService(IDataProtectionProvider provider)
    {
        // Create a protector with a unique purpose string
        _protector = provider.CreateProtector("MyApp.DataProtection.Demo");
    }

    public string Protect(string plainText)
    {
        return _protector.Protect(plainText);
    }

    public string Unprotect(string protectedData)
    {
        return _protector.Unprotect(protectedData);
    }
}

Register it in Program.cs:
builder.Services.AddScoped<EncryptionService>();

Usage in a controller:
[ApiController]
[Route("api/[controller]")]
public class SecureController : ControllerBase
{
    private readonly EncryptionService _encryptionService;

    public SecureController(EncryptionService encryptionService)
    {
        _encryptionService = encryptionService;
    }

    [HttpGet("protect")]
    public string ProtectData(string input)
    {
        return _encryptionService.Protect(input);
    }

    [HttpGet("unprotect")]
    public string UnprotectData(string input)
    {
        return _encryptionService.Unprotect(input);
    }
}

Persisting Keys (Production Scenarios)
By default, keys are stored in-memory and lost when the app restarts. For production, configure persistent key storage.

File System Storage
builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"C:\keys"))
    .SetApplicationName("MyApp");

Azure Blob Storage
builder.Services.AddDataProtection()
    .PersistKeysToAzureBlobStorage(
        new Uri("https://<account>.blob.core.windows.net/keys/key.xml"),
        new DefaultAzureCredential());


Redis (for multiple instances)
builder.Services.AddDataProtection()
    .PersistKeysToStackExchangeRedis(connectionMultiplexer, "DataProtection-Keys");


Key Management and Rotation

  • Keys are automatically rotated every 90 days (configurable).
  • Old keys are retained for decrypting data.
  • You can manually configure key lifetimes:

builder.Services.AddDataProtection()
    .SetDefaultKeyLifetime(TimeSpan.FromDays(30));


Common Use Cases

  • Protecting sensitive settings (e.g., API keys).
  • Encrypting tokens or IDs before sending them in URLs.
  • Securing cookies and session state (handled automatically).
  • Multi-instance apps (persist keys to shared storage).

Best Practices

  • Always persist keys in production.
  • Use a unique purpose string for each protected data type.
  • Store keys in secure locations (Azure Key Vault, Blob Storage, Redis).
  • Rotate keys regularly.
  • Never hardcode secrets—use environment variables or secret managers.

Conclusion
The ASP.NET Core Data Protection API provides a secure and developer-friendly way to handle encryption without needing deep cryptography knowledge. Whether you’re protecting sensitive values in configuration, encrypting tokens, or securing cookies, the Data Protection system ensures your app stays resilient against common cryptographic pitfalls. With persistent key storage, key rotation, and integration with cloud services, it’s ready for enterprise-grade applications.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Rate Limiting and Throttling in ASP.NET Core Web API

clock September 15, 2025 08:22 by author Peter

Thousands of requests per second are frequently made to APIs. Excessive traffic, whether malicious (DoS/DDoS) or accidental (clients flooding with requests), can cause system failure, raise expenses, or impair performance if sufficient controls are not in place. Techniques like rate limitation and throttling are essential for managing API usage, guaranteeing equitable resource allocation, and safeguarding backend services.


This post will explain how to use built-in middleware (from.NET 7+), bespoke solutions, and recommended practices to establish rate restriction in ASP.NET Core Web APIs.

What is rate limiting?
Rate limiting defines how many requests a client (IP, user, or token) can make in a given time window.

For example

  • Limit: 100 requests per minute per client.
  • If exceeded, the API returns HTTP 429 Too Many Requests.


What is throttling?
Throttling goes beyond rate limiting. Instead of outright rejecting requests, it can:

  • Queue or delay requests until capacity is available.
  • Reduce service quality for heavy users while maintaining fairness.
  • Apply dynamic limits based on client type (free vs. premium).

Benefits of Rate Limiting & Throttling

  • Prevent abuse (scraping, brute-force attacks).
  • Protect backend systems from overload.
  • Ensure fair use among consumers.
  • Support tiered plans (e.g., free users: 50 requests/min, premium: 500 requests/min).

Implementing Rate Limiting in ASP.NET Core
1. Using .NET 7+ Built-in Rate Limiting Middleware

ASP.NET Core 7 introduced a dedicated rate-limiting middleware.

Install Package
If you’re on .NET 7+:
dotnet add package Microsoft.AspNetCore.RateLimiting

Configure in Program.cs
using System.Threading.RateLimiting;
using Microsoft.AspNetCore.RateLimiting;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRateLimiter(options =>
{
    // Limit each client to 100 requests per minute
    options.AddFixedWindowLimiter("Fixed", opt =>
    {
        opt.PermitLimit = 100;
        opt.Window = TimeSpan.FromMinutes(1);
        opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        opt.QueueLimit = 2; // Max queued requests
    });
});

var app = builder.Build();

app.UseRateLimiter();

app.MapGet("/api/data", () => "This is a rate-limited endpoint")
   .RequireRateLimiting("Fixed");

app.Run();


2. Token Bucket Limiter Example
This allows burst traffic while enforcing overall limits.
options.AddTokenBucketLimiter("TokenBucket", opt =>
{
    opt.TokenLimit = 50;                 // Max tokens
    opt.QueueLimit = 2;
    opt.ReplenishmentPeriod = TimeSpan.FromSeconds(10);
    opt.TokensPerPeriod = 10;            // Refill rate
    opt.AutoReplenishment = true;
});


3. Sliding Window Limiter Example
Smooths out spikes by distributing requests across sliding intervals.
options.AddSlidingWindowLimiter("SlidingWindow", opt =>
{
    opt.PermitLimit = 100;
    opt.Window = TimeSpan.FromMinutes(1);
    opt.SegmentsPerWindow = 6; // 10 sec segments
    opt.QueueLimit = 2;
});

4. Per-User or Per-API Key Limiting
You can bind rate limits to users, API keys, or IPs.
builder.Services.AddRateLimiter(options =>
{
    options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
    {
        var userId = httpContext.User?.Identity?.Name ?? "anonymous";
        return RateLimitPartition.GetFixedWindowLimiter(userId, _ => new FixedWindowRateLimiterOptions
        {
            PermitLimit = 10,
            Window = TimeSpan.FromSeconds(30)
        });
    });
});


Handling 
Exceeded Limits
When a client exceeds the allowed requests, ASP.NET Core automatically returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 60

You can customize the response:
options.OnRejected = (context, token) =>
{
    context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
    return context.HttpContext.Response.WriteAsync("Too many requests. Please try again later.");
};


Implementing Throttling (Delaying Requests)
Instead of rejecting requests, you can queue and delay them.
options.AddFixedWindowLimiter("Throttle", opt =>
{
    opt.PermitLimit = 5;
    opt.Window = TimeSpan.FromSeconds(10);
    opt.QueueLimit = 5;
    opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
});


Here, extra requests wait in a queue instead of failing immediately.

Best Practices for Rate Limiting

  • Apply limits per identity, not globally (use API key, user, or IP).
  • Different plans for different clients (free vs. premium tiers).
  • Monitor & log 429 responses to detect abuse.
  • Return Retry-After headers so clients can back off.
  • Combine with API Gateway (e.g., YARP, NGINX, Azure API Management) for distributed systems.
  • Always test under load scenarios to fine-tune values.

Example: Full ASP.NET Core Web API with Rate Limiting
using System.Threading.RateLimiting;
using Microsoft.AspNetCore.RateLimiting;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("ApiLimiter", opt =>
    {
        opt.PermitLimit = 100;
        opt.Window = TimeSpan.FromMinutes(1);
        opt.QueueLimit = 0;
    });

    options.OnRejected = async (context, _) =>
    {
        context.HttpContext.Response.StatusCode = 429;
        await context.HttpContext.Response.WriteAsync("Too many requests. Slow down!");
    };
});

var app = builder.Build();

app.UseHttpsRedirection();
app.UseAuthorization();

app.UseRateLimiter();

app.MapControllers().RequireRateLimiting("ApiLimiter");

app.Run();


Conclusion
Rate limiting and throttling are essential to keep your ASP.NET Core Web API secure, stable, and fair. With .NET 7’s built-in middleware, you can easily configure fixed, sliding, or token bucket rate limits. For advanced needs, combine them with per-user throttling, API gateways, and monitoring to maintain performance and reliability.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Stopping Cookie Theft and Session Hijacking in ASP.NET Core

clock September 11, 2025 08:35 by author Peter

Cookies are frequently used by web applications to preserve user sessions following authentication. However, attackers can assume user identities and obtain illegal access if cookies are stolen or compromised. Two of the most prevalent attack methods in contemporary online applications are session hijacking and cookie theft. The recommended methods for preventing session hijacking in ASP.NET Core will be discussed in this post.

What is session hijacking?

  • Session hijacking is when an attacker takes over a valid session by stealing session cookies. This can be done via:
  • XSS (Cross-Site Scripting) → Injecting malicious JavaScript to steal cookies
  • Packet Sniffing → Intercepting cookies over insecure HTTP connections
  • Session Fixation → Forcing a user to use a known session ID
  • Man-in-the-Middle Attacks → Capturing cookies during transmission

Once an attacker gets hold of a session cookie, they can act as a legitimate user.

ASP.NET Core Built-in Protections

ASP.NET Core provides mechanisms to secure cookies and reduce hijacking risks. Let’s see how to configure them.

1. Use Secure Cookies

Ensure cookies are only sent over HTTPS and not accessible via JavaScript.
builder.Services.ConfigureApplicationCookie(options =>
{
    options.Cookie.HttpOnly = true; // Prevent JavaScript access
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // HTTPS only
    options.Cookie.SameSite = SameSiteMode.Strict; // Prevent CSRF
});


Best Practice: Always enforce HTTPS using app.UseHttpsRedirection();.

2. Short Session Lifetimes with Sliding Expiration
Short-lived cookies minimize the attack window.
builder.Services.ConfigureApplicationCookie(options =>
{
    options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
    options.SlidingExpiration = true; // Refresh on activity
});

3. Regenerate Session IDs on Login
Prevent session fixation by regenerating identifiers after login.
await _signInManager.SignInAsync(user, isPersistent: false);
await _context.SaveChangesAsync();


ASP.NET Core Identity automatically issues a new cookie upon sign-in, mitigating fixation attacks.

4. Implement Cookie Authentication Validation
Periodically validate security stamps to invalidate stolen cookies.
builder.Services.Configure<SecurityStampValidatorOptions>(options =>
{
    options.ValidationInterval = TimeSpan.FromMinutes(5);
});


This ensures that if a user changes their password or logs out, old cookies become invalid.

5. Use SameSite Cookies Against CSRF
SameSite prevents cookies from being sent in cross-site requests.
options.Cookie.SameSite = SameSiteMode.Strict;

If your app requires cross-site cookies (e.g., external login providers), consider SameSite=Lax but avoid None unless absolutely necessary (and always use HTTPS).

6. Protect Against XSS (Cross-Site Scripting)
Since XSS is a common way to steal cookies:

  •     Encode all output (@Html.Encode() or Razor automatic encoding).
  •     Use Content Security Policy (CSP).
  •     Sanitize user inputs.

In Program.cs:
app.Use(async (context, next) =>
{
    context.Response.Headers.Add("Content-Security-Policy", "default-src 'self'");
    await next();
});


7. Enforce Re-authentication for Sensitive Actions
For critical operations (password change, payment, deleting account), force users to re-enter credentials or MFA.
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
    return RedirectToAction("Login");
}


8. Use Multi-Factor Authentication (MFA)
Even if cookies are stolen, requiring a second factor makes hijacking much harder.

9. Monitor and Revoke Compromised Sessions
Log user login locations, devices, and IPs. Allow users (and admins) to view and revoke active sessions.
await _signInManager.SignOutAsync(); // Invalidate cookie

Conclusion
Session hijacking and cookie theft are real threats, but ASP.NET Core provides strong tools to mitigate them.
To secure your app:

  • Always use HTTPS with secure, HttpOnly, and SameSite cookies.
  • Shorten cookie lifetimes and use sliding expiration.
  • Regenerate session IDs after login and validate security stamps.
  • Defend against XSS and CSRF attacks.
  • Enforce MFA and re-authentication for sensitive operations.

By combining these techniques, you can significantly reduce the attack surface and protect your users.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Using ASP.NET Core to Guard Against Malformed Payloads and JSON Injection

clock September 8, 2025 07:47 by author Peter

JSON injection: what is it?

  • When an attacker modifies a JSON payload's structure to add unexpected keys, values, or scripts, this is known as JSON injection.
  • Attackers take advantage of
    • Additional fields (such as isAdmin: true).
    • JSON string script injection (which, if done incorrectly, can result in XSS).
    • JSON was altered to avoid validations or to cause parser crashes.

An example of an attack payload

Example Attack Payload
{
"username": "attacker",
"email": "[email protected]",
"isAdmin": true,        // Sensitive field injection
"__proto__": { "polluted": "yes" } // Prototype pollution attack
}


If your API model-binds this blindly into your entity, attackers can escalate privileges or poison objects.

Risks of Malformed JSON

  • Denial of Service (DoS): Sending enormous JSON payloads.
  • Deserialization flaws: Exploiting weak parsers (e.g., type confusion).
  • Validation bypass: Sending unexpected JSON structures.

How to Protect in ASP.NET Core?

1. Use DTOs Instead of Entities

Never accept raw JSON into entity models. Define Data Transfer Objects (DTOs) with strict fields.
public class RegisterDto
{
[Required, StringLength(50)]
public string Username { get; set; }

[Required, EmailAddress]
public string Email { get; set; }

[Required, StringLength(100)]
public string Password { get; set; }
}


Controller
[HttpPost("register")]
public IActionResult Register([FromBody] RegisterDto dto)
{
if (!ModelState.IsValid)
    return BadRequest(ModelState);

// Safe mapping
var user = new User
{
    Username = dto.Username,
    Email = dto.Email,
    IsAdmin = false // Explicitly controlled
};

return Ok(user);
}


Prevents attackers from injecting extra JSON properties, such as isAdmin.

2. Reject Unknown Properties

By default, System. Text.Json ignores unknown fields. Force it to fail on unknown JSON properties.
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Disallow;
options.JsonSerializerOptions.AllowTrailingCommas = false;
options.JsonSerializerOptions.UnknownTypeHandling = System.Text.Json.Serialization.JsonUnknownTypeHandling.Fail;
options.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
});


Or, in Newtonsoft.Json.
builder.Services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Error;
});


Any unexpected JSON field will cause a 400 Bad Request.

3. Input Validation

Use Data Annotations or FluentValidation to validate JSON payloads.
if (!ModelState.IsValid)
return BadRequest(ModelState);


Ensures malformed JSON (wrong type, missing field) gets rejected.

4. Limit Request Size (Prevent DoS)

Attackers may send gigantic JSON payloads to crash your server. Set max request body size.
app.Use(async (context, next) =>
{
context.Request.Headers.ContentLength = Math.Min(context.Request.ContentLength ?? 0, 10_000); // 10 KB
await next();
});

Or in Kestrel
builder.WebHost.ConfigureKestrel(options =>
{
options.Limits.MaxRequestBodySize = 10 * 1024; // 10 KB
});

Protects against payload flooding.

5. Sanitize JSON String Fields

Attackers may inject <script> or SQL-like payloads inside JSON strings. Use HtmlSanitizer or encoding before saving.
var sanitizer = new HtmlSanitizer();
dto.Username = sanitizer.Sanitize(dto.Username);


Prevents XSS when storing/displaying JSON fields.
6. Strong Content-Type Validation
Reject requests that are not application/json.

if (!context.Request.ContentType?.Contains("application/json") ?? true)
{
context.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType;
return;
}


Stops attackers from sneaking payloads with wrong MIME types.
7. Use JsonDocument for Strict Parsing
If you don’t trust the payload at all, parse manually.
using var doc = JsonDocument.Parse(jsonString, new JsonDocumentOptions
{
AllowTrailingCommas = false,
CommentHandling = JsonCommentHandling.Disallow
});

if (!doc.RootElement.TryGetProperty("username", out var username))
return BadRequest("Missing username");


Complete control over parsing logic.

8. Audit & Logging

Log rejected payloads with correlation IDs.
_logger.LogWarning("Invalid JSON payload received: {Payload}", jsonString);

Helps detect attack attempts.

Best Practices Checklist

  • Use DTOs (never bind entities).
  • Fail on unknown JSON properties.
  • Validate input with Data Annotations/FluentValidation.
  • Set max request body size to prevent DoS.
  • Sanitize string fields for XSS safety.
  • Enforce application/json Content-Type.
  • Parse manually ( JsonDocument ) for untrusted payloads.
  • Log and monitor invalid payload attempts.

Conclusion
ASP.NET Core provides strong defenses, but developers must explicitly enable strict JSON parsing and validation.
By using DTOs, rejecting unknown fields, sanitizing input, and setting request limits, you protect your API from.

  • JSON Injection
  • Malformed Payload DoS
  • Prototype Pollution
  • XSS inside JSON


European ASP.NET Core 9.0 Hosting - HostForLIFE :: Utilizing a Secure API to Verify UPI IDs

clock September 4, 2025 08:14 by author Peter

To guarantee accuracy and lower failure rates in digital payment systems, a UPI (Unified Payments Interface) ID must be validated before a transaction is started. Without revealing any critical implementation details, this article describes how to integrate a UPI ID validation API using JavaScript and C# (ASP.NET). To demonstrate, we'll use a fictitious endpoint.

Why Validate UPI IDs?

  • Avoid transaction failures due to incorrect UPI IDs
  • Improve user experience by providing real-time feedback
  • Ensure the UPI ID belongs to the correct customer

Technologies Used

  • C# (ASP.NET WebForms/Backend)
  • JavaScript (AJAX for frontend calls)
  • JSON for data communication
  • Dummy API endpoint (replace with actual provider)

Example API Request
POST https://dummyapi.example.com/v1/payments/validate/vpa
Content-Type: application/json
{
"vpa": "user123@dummyupi"
}


Successful Response
{
  "vpa": "user123@dummyupi",
  "customer_name": "John Doe",
  "success": true
}


Invalid UPI Response
{
  "error": {
    "code": "BAD_REQUEST_ERROR",
    "description": "Invalid VPA. Please enter a valid Virtual Payment Address",
    "source": "customer",
    "step": "payment_initiation",
    "reason": "invalid_vpa"
  }
}


Backend Implementation (C#)
Here’s a simplified C# function to validate a UPI ID via HTTP POST.
public static string ValidateUPIID(string upi)
{
string responseJson = "";
try
{
    var requestObj = new { vpa = upi };
    string requestJson = new JavaScriptSerializer().Serialize(requestObj);

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://dummyapi.example.com/v1/payments/validate/vpa");
    request.Method = "POST";
    request.ContentType = "application/json";

    byte[] data = Encoding.UTF8.GetBytes(requestJson);
    using (var stream = request.GetRequestStream())
    {
        stream.Write(data, 0, data.Length);
    }

    using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
    {
        responseJson = reader.ReadToEnd();
    }

    if (responseJson.Contains("success"))
    {
        var jsonObj = new JavaScriptSerializer().Deserialize<UPIResponse>(responseJson);
        return jsonObj.success ? $"success - {jsonObj.customer_name}" : "failed - ";
    }
    else if (responseJson.Contains("error"))
    {
        var errorObj = new JavaScriptSerializer().Deserialize<UPIErrorResponse>(responseJson);
        string errDesc = !string.IsNullOrEmpty(errorObj.error.description) ? errorObj.error.description : "Invalid VPA";
        string errReason = !string.IsNullOrEmpty(errorObj.error.reason) ? errorObj.error.reason : "invalid_vpa";
        return $"reason - {errDesc} - {errReason}";
    }

    return "failed - unknown error";
}
catch (Exception ex)
{
    return "error - " + ex.Message;
}
}


public class UPIResponse
{
public string vpa { get; set; }
public string customer_name { get; set; }
public bool success { get; set; }
}
public class UPIErrorResponse
{
public UPIError error { get; set; }
}
public class UPIError
{
public string code { get; set; }
public string description { get; set; }
public string source { get; set; }
public string step { get; set; }
public string reason { get; set; }
}

JavaScript Frontend (AJAX Call)
<div class="bid-content-common bid-content-3">
<label class="upiid-lble">UPI ID</label>
<input id="txtupiid" runat="server" type="text" placeholder="Enter the UPI ID"
       onchange="UPIIDtxtboxchanges()" onblur="handleBlur()" />
<span id="upidtxt"></span>
<input id="upidvalidation" type="hidden" />
<button id="applybtn">Apply</button>
</div>

<script>
function UPIIDtxtboxchanges() {
    var applybtn = document.getElementById("applybtn");
    var upitxt = document.getElementById("upidvalidation");
    var txtbutton = document.getElementById("txtupiid");
    var verifytxt = document.getElementById("upidtxt");

    upitxt.value = 'Verify';
    upitxt.style.backgroundColor = "#F0F0F0";
    upitxt.style.border = "2px solid #979F6E";
    upitxt.style.color = "black";
    verifytxt.innerText = "";
    applybtn.style.pointerEvents = "auto";
}

function handleBlur() {
    // Assuming upivalidationmethod takes a callback function
    upivalidationmethod(function (isValid) {
        if (isValid) {
            return true;
        } else {
            return false;
        }
    });
}

function upivalidationmethod(callback) {
    var upiId = document.getElementById("upiInput").value;

    $.ajax({
        type: "POST",
        url: "/your-page.aspx/ValidateUPI",
        contentType: "application/json; charset=utf-8",
        data: JSON.stringify({ upi: upiId }),
        success: function (res) {
            var status = res.d.split('-');
            if (status[0] === "success") {
                $("#message").text("Valid UPI: " + status[1]).css("color", "green");
                callback(true);
            } else {
                $("#message").text("Invalid UPI").css("color", "red");
                callback(false);
            }
        },
        error: function () {
            $("#message").text("Error validating UPI").css("color", "red");
            callback(false);
        }
    });
}
</script>


Best Practices

  • Always encrypt sensitive credentials used in API headers.
  • Validate and sanitize inputs to avoid injection or malformed requests.
  • Log and monitor UPI validation failures for system analysis.

Conclusion
Including a UPI ID validation phase in your application guarantees a better user experience, fewer unsuccessful transactions, and more seamless payments. This procedure is made more efficient by using an API, which provides error handling and real-time customer name checks. Securely in production, substitute your real provider's information with the fake API URL and login credentials.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Blazor Cheatsheet: An Easy-to-Use Guide

clock September 1, 2025 10:12 by author Peter

Blazor is a web framework developed by Microsoft that allows developers to build interactive web applications using C# instead of JavaScript. It is part of the .NET ecosystem, making it possible to run C# code directly in the browser with the help of WebAssembly, or on the server with a real-time connection. This cheatsheet is a quick guide to the most critical Blazor features, syntax, and usage examples.

Blazor Hosting Models
Blazor Server

  •     Runs on the server.
  •     Uses SignalR to update UI.
  •     Small download size.

Example
@page "/counter"
<h3>Counter</h3>
<p>Count: @count</p>
<button @onclick="Increment">Click</button>

@code {
    private int count = 0;
    private void Increment() => count++;
}


Important: Faster load, but requires a constant connection.

Blazor WebAssembly

  • Runs in the browser.
  • Uses WebAssembly to execute .NET code.
  • Larger download size.
  • Important: Works offline after first load.

Components
Small building blocks of UI.
Written in .razor files.
Example
@code {
    [Parameter] public string Title { get; set; }
}

<h3>@Title</h3>

Important: Use [Parameter] for parent-to-child data.

Data Binding
1. One-way binding
<p>Hello, @name</p>
@code {
    string name = "Peter";
}


Data flows from code to UI.

2. Two-way binding

<input @bind="name" />
<p>@name</p>

@code {
    string name = "Peter";
}

Data syncs between UI and code.

Event Handling

Use @on... attributes.

Example
<button @onclick="ShowMessage">Click</button>

@code {
    void ShowMessage() => Console.WriteLine("Clicked!");
}


Lifecycle Methods

Special methods that control component behavior.

Common ones
protected override void OnInitialized() { }
protected override void OnParametersSet() { }
protected override async Task OnAfterRenderAsync(bool firstRender) { }


Important

  •     OnInitialized: Runs when the component is created.
  •     OnParametersSet: Runs when the parent sends new data.
  •     OnAfterRenderAsync: Runs after rendering UI.

Dependency Injection
Services can be injected into components.

Example
@inject HttpClient Http

<button @onclick="GetData">Fetch Data</button>

@code {
    string data;
    async Task GetData() {
        data = await Http.GetStringAsync("https://api.example.com/data");
    }
}

Important: Register services in the Program.cs file.

Routing

Define a route with @page.

Example
    @page "/about"
    <h3>About Page</h3>


For navigation
<NavLink href="/about">Go to About</NavLink>

Forms and Validation

Use EditForm with models.

Example
    @page "/form"
    <EditForm Model="user" OnValidSubmit="HandleValidSubmit">
        <InputText @bind-Value="user.Name" />
        <ValidationMessage For="@(() => user.Name)" />
        <button type="submit">Submit</button>
    </EditForm>

    @code {
        User user = new();
        void HandleValidSubmit() => Console.WriteLine("Submitted!");
        class User { public string Name { get; set; } }
    }

State Management

  •     Local state (inside component): Use fields/properties.
  •     App-wide state: Use services registered as Scoped.

JavaScript Interop

Call JS from C# or vice versa.
    Example

    @inject IJSRuntime JS

    <button @onclick="CallJS">Run JS</button>

    @code {
        async Task CallJS() {
            await JS.InvokeVoidAsync("alert", "Hello from Blazor");
        }
    }

Reusable Layouts
Use MainLayout.razor.

Example
    @inherits LayoutComponentBase
    <div class="page">
        <div class="sidebar">Menu</div>
        <div class="content">@Body</div>
    </div>

Important: @Body This is where the child page renders.

Error Handling
Use try-c
atch in methods.
    Example
    try {
        await Http.GetStringAsync("bad-url");
    } catch (Exception ex) {
        Console.WriteLine(ex.Message);
    }


Conclusion
Blazor gives developers the ability to use C# rather than JavaScript to construct cutting-edge online applications. Blazor offers the full.NET ecosystem to web development with features including components, data binding, routing, forms, dependency injection, and JavaScript interop. The most important ideas for getting started and creating scalable apps more quickly are covered in this cheat sheet.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: ASP.NET Core Web API Security Using JWT Authentication

clock August 29, 2025 09:07 by author Peter

Mobile apps, SaaS platforms, IoT devices, and business systems are all powered by APIs, which form the foundation of today's digital economy.  However, a significant obstacle that comes with their popularity is security.  

APIs are one of the most popular attack vectors for cybercriminals, according to OWASP, and are frequently taken advantage of through compromised authentication, lax authorization, or data exposure. With the help of a real-world example—a bookstore API—we will walk through the entire process of securing an ASP.NET Core Web API using role-based authorization and JWT authentication in this post.

Why API Security Matters?
Imagine a bookstore API that exposes endpoints to manage books, authors, and sales. Without security:

  • Anyone could list books, update prices, or delete data.
  • Hackers could steal sensitive reports intended for admins only.
  • Attackers could flood the API with requests, causing downtime.

That’s why API security is non-negotiable.

Common API Security Threats

Before jumping into implementation, let’s recall the top threats (from OWASP API Top 10):

  1. Broken Authentication: Weak login mechanisms or missing tokens.
  2. Broken Authorization: Users accessing data or endpoints they shouldn’t.
  3. Injection Attacks (SQL, NoSQL, Command): Malicious input executed by the system.
  4. Sensitive Data Exposure: Sending unencrypted or excessive data.
  5. Rate Limiting & DoS Attacks – Flooding APIs with requests.
  6. Improper Logging & Monitoring – No visibility into attacks.

Best Practices for API Security
To mitigate these threats, adopt a defense-in-depth approach:

  • Use HTTPS everywhere—no plain HTTP.
  • Authentication & Authorization —JWT or OAuth 2.0 with RBAC/Policies.
  • Validate Inputs —prevent SQL injection and NoSQL injection.
  • Rate Limiting & Throttling —prevent abuse and DoS attacks.
  • Centralized Secret Management —Azure Key Vault or AWS Secrets Manager.
  • Error Handling —don’t leak stack traces.
  • Monitoring & Logging —track security events with Serilog or App Insights.

Implementation: Securing a Bookstore API with JWT
We’ll now build a secure Bookstore API in ASP.NET Core Web API with:

  • JWT-based authentication
  • Role-based authorization (admin vs. user)
  • Secure endpoints for books and admin reports

Step 1. Create Project
dotnet new webapi -n BookstoreApi
cd BookstoreApi


Step 2. Add NuGet Package
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

Step 3. Configure Authentication & Authorization ( Program.cs )
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Add Authentication with JWT
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "https://api.bookstore.com",
            ValidAudience = "https://api.bookstore.com"
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes("SuperSecretKey12345")) // use Key Vault in production
        };
    });

// Authorization Policies
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
});

builder.Services.AddControllers();

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();


Step 4. Create Auth Controller ( Controllers/AuthController.cs )
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace BookstoreApi.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class AuthController : ControllerBase
    {
        [HttpPost("login")]
        public IActionResult Login(string username, string password)
        {
            // Demo validation (replace with ASP.NET Identity or DB check in production)
            if (username == "admin" && password == "password")
            {
                var claims = new[]
                {
                    new Claim(ClaimTypes.Name, username),
                    new Claim(ClaimTypes.Role, "Admin")
                };

                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SuperSecretKey12345"));
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

                var token = new JwtSecurityToken(
                    issuer: "https://api.bookstore.com",
                    audience: "https://api.bookstore.com",
                    claims: claims,
                    expires: DateTime.Now.AddMinutes(30),
                    signingCredentials: creds);

                return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
            }

            return Unauthorized();
        }
    }
}

Step 5. Create Secure Data Controller ( Controllers/SecureDataController.cs )
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace BookstoreApi.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class SecureDataController : ControllerBase
    {
        // Accessible by any authenticated user
        [HttpGet("books")]
        [Authorize]
        public IActionResult GetBooks() =>
            Ok("List of available books. (Authenticated users only)");

        // Accessible only by Admin role
        [HttpGet("admin/reports")]
        [Authorize(Policy = "AdminOnly")]
        public IActionResult GetAdminReports() =>
            Ok("Sensitive sales reports. (Admins only)");
    }
}

Testing the API
Log in to get a JWT token
POST https://api.bookstore.com/api/auth/login
Body: { "username": "admin", "password": "password" }

Response
{ "token": "eyJhbGciOiJIUzI1NiIsIn..." }

Access books endpoint. (user-protected)
GET https://api.bookstore.com/api/securedata/books
Authorization: Bearer <token>


Access admin reports endpoint (admin-only)
GET https://api.bookstore.com/api/securedata/admin/reports
Authorization: Bearer <token>


Enhancing Security Further

  • Store SuperSecretKey12345 in Azure Key Vault or AWS Secrets Manager.
  • Use the AspNetCoreRateLimit NuGet package to apply rate limiting.
  • Enforce HTTPS redirection
    • app.UseHttpsRedirection();
  • Monitor API usage with Serilog and Application Insights.
  • Regularly patch dependencies and update the .NET runtime.

Conclusion
API security is not just about authentication; it’s a multi-layered defense strategy. In this article, we:

  • Reviewed the OWASP API Top 10 threats.
  • Implemented JWT authentication in ASP.NET Core.
  • Applied role-based authorization for sensitive endpoints.
  • Discussed enterprise-grade enhancements like rate limiting and secret management.

With these practices, your ASP.NET Core Web APIs will be secure, reliable, and production-ready.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Limiting ASP.NET Login by IP Address

clock August 14, 2025 08:31 by author Peter

In many web applications, especially for sensitive areas like Admin Panels, we don’t want just anyone to access the login page. Even if they have valid credentials, restricting access by IP address adds an extra layer of security. In this post, I’ll show you how to allow login only from specific IP addresses using ASP.NET C# and web.config settings.

Why Restrict Access by IP?

  • Protects sensitive admin functionality from outside networks.
  • Limits exposure to brute-force or password-guessing attacks.
  • Helps in corporate environments where admins work from known locations.

Step 1. Store Allowed IP Addresses in web.config
Open your web.config file and add this under the <configuration> section:

<configuration>
  <appSettings>
    <!-- Comma-separated allowed IP addresses -->
    <add key="AllowedIPs" value="192.168.1.10,203.0.113.25,45.67.89.10" />
  </appSettings>
</configuration>


Notes

  • Multiple IPs are separated by commas.
  • You can add or remove IPs without touching the code — just update web.config.

Step 2. Check IP Address in Code-Behind

In your login.aspx.cs or any protected page’s code-behind:
using System;
using System.Configuration;
using System.Linq;
using System.Web.UI;

public partial class Admin_Login : System.Web.UI.Page
{
    protected void Go_Click(object sender, EventArgs e)
    {
        // Step 1: Get the client's IP address
        string ipaddress = Request.UserHostAddress.ToString();

        // Step 2: Get allowed IPs from web.config
        string allowedIPs = ConfigurationManager.AppSettings["AllowedIPs"];

        // Step 3: Convert string to array and trim spaces
        string[] ipList = allowedIPs.Split(',').Select(ip => ip.Trim()).ToArray();

        // Step 4: Check if IP is in the allowed list
        if (ipList.Contains(ipaddress))
        {
            // Allowed - Continue with login process
            Response.Redirect("dashboard.aspx");
        }
        else
        {
            // Not allowed - Show alert and stop
            string script = "alert('Access Denied! Your IP is not authorized to access this page.');";
            ScriptManager.RegisterStartupScript(this.Page, this.GetType(), "alertScript", script, true);
        }
    }
}


How It Works?

  • Request.UserHostAddress retrieves the IP address of the user trying to log in.
  • The code reads the list of allowed IPs from web.config.
  • It splits the list into an array and trims any spaces.
  • If the user’s IP matches any of the allowed IPs → login proceeds.
  • If not → a JavaScript alert displays "Access Denied!" and stops the process.

Important Tips

  • When testing locally, you might see 127.0.0.1 (IPv4) or ::1 (IPv6 loopback address).
  • That’s normal for localhost — to test real IP restrictions, deploy to a server, or access from another device.
  • For cloud hosting (Azure, AWS, etc.), sometimes you’ll need to check headers, like "HTTP_X_FORWARDED_FOR" for the real client IP.
  • Keep the web.config secure, never expose it publicly.

Conclusion
Restricting access by IP address is a simple yet effective security enhancement for admin login pages in ASP.NET. By keeping the allowed IP list in web.config, you can manage access without redeploying your application.



About HostForLIFE.eu

HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2016 Hosting, ASP.NET Core 2.2.1 Hosting, ASP.NET MVC 6 Hosting and SQL 2017 Hosting.


Tag cloud

Sign in