1. Introduction to CSRF Protection in WebForms Core
Cross-Site Request Forgery (CSRF) is a web security vulnerability in which an attacker tricks a user's browser into sending an unwanted request to a website where the user is authenticated. If the website relies on automatically attached authentication cookies without properly verifying the request, an attacker may be able to trigger actions on behalf of the victim.
WebForms Core (WFC), introduced by Elanat, provides a server-oriented approach to web interface manipulation. The backend generates commands that WebFormsJS executes in the browser, allowing web interfaces to be manipulated through server-generated instructions without requiring a separate frontend framework.
In this article, we will implement a basic CSRF protection mechanism using WebForms Core and CodeBehind, a backend web framework developed by Elanat. We will generate a secure token, store it in a session, add it to an HTML form, and validate it when the form is submitted.
2. CodeBehind as the Backend
CodeBehind handles HTTP requests, manages the session-based CSRF token, validates submitted data, and generates responses through WebForms Core.
The process follows this sequence:
- The browser requests the page.
- CodeBehind retrieves an existing CSRF token from the session or generates a new one.
- WebForms Core adds the token to the HTML form as a hidden input.
- The browser submits the form with the token.
- CodeBehind compares the submitted token with the token stored in the session.
- WebForms Core generates commands to display the validation result.
The security decision takes place on the backend. WebFormsJS executes the resulting interface commands, but it does not determine whether the CSRF token is valid.
3. Generating a Secure CSRF Token
A CSRF token must be unpredictable so that an attacker cannot easily construct a valid request. C# provides RandomNumberGenerator, which is designed for cryptographically secure random number generation.
The following code generates a token from 32 random bytes and converts it into a hexadecimal string:
using System.Security.Cryptography;
string token = Convert.ToHexString(
RandomNumberGenerator.GetBytes(32));
This produces a 64-character hexadecimal token representing 256 bits of random data.
Unlike a token generated with the ordinary Random class, this value is suitable for security-sensitive purposes. The token should be generated on the server and must not be predictable from public information such as usernames, timestamps, or sequential identifiers.
4. Storing the Token in Session
The token must remain available between the initial page request and the subsequent form submission. In this example, ASP.NET Core Session provides the required storage.
The following method retrieves the existing token or generates and stores one if it does not exist:
private string GetCsrfToken(HttpContext context)
{
string token = context.Session.GetString("csrf_token");
if (string.IsNullOrEmpty(token))
{
token = Convert.ToHexString(
RandomNumberGenerator.GetBytes(32));
context.Session.SetString("csrf_token", token);
}
return token;
}
The session allows the server to associate the token with the user's session and retrieve it when the form is submitted. Session services and middleware must be configured correctly in the ASP.NET Core application.
The session cookie should also be protected with appropriate security settings, including HTTPS and suitable cookie attributes.
5. Adding the Token to the HTML Form with WebForms Core
The CSRF token is included in the form as a hidden input. WebForms Core can generate the required interface command without requiring a separate JavaScript operation to create the field.
The example form is:
<form method="POST" action="/security_csrf">
<label for="email">Email: </label>
<input type="text" name="email" id="email" />
<br />
<button type="submit">Update Email</button>
</form>
In the CodeBehind controller, the token is added to the form using WebForms Core:
WebForms form = new WebForms();
form.AddHidden("<form>", "csrf_token", csrfToken);
Write(form.ExportToHtmlComment());
AddHidden targets the form and adds a hidden field named csrf_token. ExportToHtmlComment() exports the WebForms Core commands in the format expected by the client-side executor.
To execute WebForms Core commands in the browser, the page must also include the WebFormsJS client-side library. For example, if the library is located at /script/web-forms.js, add the following script to the HTML document's <head>:
<script type="module" src="/script/web-forms.js"></script>
Adjust the script path to match the location of the WebFormsJS file in your application.
The browser submits the hidden token along with the other form fields. Although the token is not visible in the interface, it is accessible to the page and must not be treated as a secret from JavaScript executing in that page. Its security depends on its unpredictability and server-side validation, not on the hidden field itself.
Full page management code
public void PageLoad(HttpContext context)
{
string csrfToken = GetCsrfToken(context);
if (context.Request.Method == "POST")
{
Button_Click(context, csrfToken);
return;
}
WebForms form = new WebForms();
form.AddHidden("<form>", "csrf_token", csrfToken);
Write(form.ExportToHtmlComment());
}
6. Validating CSRF Tokens on POST Requests
When the form is submitted, CodeBehind retrieves the token from the request and compares it with the token stored in the session.
The following example checks that both values are non-empty before comparing them:
string formToken =
context.Request.Form["csrf_token"].ToString();
if (!string.IsNullOrEmpty(csrfToken) &&
!string.IsNullOrEmpty(formToken) &&
formToken == csrfToken)
{
// Continue with the protected operation.
}
else
{
// Reject the protected operation.
}
Checking both values is important. If the session token is missing or the submitted form does not contain a token, the request must not be accepted.
The == comparison makes the basic validation logic easy to understand. For applications requiring stronger protection against timing-based comparison attacks, a fixed-time comparison can be used instead, as discussed in the final section.
A successful comparison means that the request contains the expected session token. It does not replace authentication, authorization, or validation of the submitted email address.
7. Handling Validation Results with WebForms Core
After validating the token, the controller uses WebForms Core to generate a response that communicates the result to the user.
private void Button_Click(HttpContext context, string csrfToken)
{
string formToken =
context.Request.Form["csrf_token"].ToString();
string email =
context.Request.Form["email"].ToString();
WebForms form = new WebForms();
if (!string.IsNullOrEmpty(csrfToken) &&
!string.IsNullOrEmpty(formToken) &&
formToken == csrfToken)
{
form.Message(
"CSRF token validation succeeded.",
"success");
form.Message(
"Your Email is: " + new Security().SafeValue(email));
}
else
{
form.Message(
"Security check failed. Please refresh the page and try again.",
"warning");
}
IgnoreAll(); // Transition by bypassing View and Layout
Write(form.Response());
}
The example uses new Security().SafeValue(email) before including the submitted email in a message. This is intended to prevent untrusted email content from injecting executable HTML or script content into the interface. It is important to use the appropriate output-safety method for the rendering context and avoid encoding the same content twice.
Message generates the interface feedback, while Response() generates the WebForms Core response. IgnoreAll() bypasses the view and layout for this response, allowing the command response to be returned directly.
This demonstrates the separation between security decisions and interface updates: CodeBehind validates the request on the server, WebForms Core generates the commands, and WebFormsJS executes them in the browser.
The application does not have to return an HTTP 403 status solely to display a validation warning. In this command-response workflow, a normal response can preserve Action Control execution. The essential requirement is that the protected operation must never run when validation fails.
8. Additional Security Layers and Conclusion
A session-based CSRF token is an important protection, but it should be part of a broader security strategy.
Fixed-time token comparison
For additional protection, the simple == comparison can be replaced with CryptographicOperations.FixedTimeEquals. This method compares equal-length byte arrays in a way designed to reduce information leakage through timing differences.
For example:
private bool FixedTimeEquals(string a, string b)
{
if (string.IsNullOrEmpty(a) ||
string.IsNullOrEmpty(b))
return false;
byte[] aBytes = System.Text.Encoding.UTF8.GetBytes(a);
byte[] bBytes = System.Text.Encoding.UTF8.GetBytes(b);
if (aBytes.Length != bBytes.Length)
return false;
return CryptographicOperations.FixedTimeEquals(
aBytes, bBytes);
}
The empty-value checks ensure that two missing tokens are never treated as a successful match. The length check ensures that the comparison receives byte arrays of equal length.
Other security measures
Depending on the application, additional measures can include:
-
Origin validation: Check the
Originheader against the expected origin for state-changing requests. A carefully implementedReferercheck can be used as a fallback when appropriate. -
SameSite cookies: Configure authentication cookies with an appropriate
SameSitepolicy, such asLaxorStrict,according to the application's requirements. - HTTPS: Protect session cookies and requests in transit using HTTPS and suitable secure-cookie settings.
- Authentication and authorization: Verify that the user is authenticated and permitted to perform the requested operation.
-
Input validation and output safety: Validate submitted data and use
Security.SafeValueor another appropriate context-specific output-encoding mechanism when displaying untrusted content.
These measures complement one another. Origin validation and SameSite cookies provide additional layers of defense, but they should not automatically be treated as replacements for CSRF token validation where it is required.
With CodeBehind handling the backend logic and WebForms Core managing server-generated interface commands, CSRF protection fits naturally into a server-oriented web application. The security decision remains on the server, while the resulting feedback is delivered through the WebForms Core response mechanism.
Related links
On Elanat:
On GitHub:

Top comments (1)
Clear walkthrough of keeping the CSRF decision on the server while WebFormsJS only applies UI commands. One small hardening detail worth calling out near the equality check is using a fixed-time comparison once both tokens have been decoded to the same representation; that keeps the example aligned with the later security note without changing the session-bound design.