-
Notifications
You must be signed in to change notification settings - Fork 267
Some security fixes for Giraffe #691
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
841453e
Some security fixes for Giraffe:
Thorium 719fd46
Unit tests added
Thorium eb039c6
Test to fix CI
Thorium 5622ba7
Redirect http handler updates:
64J0 dfb4226
Docs: Add new section for the CSRF helpers
64J0 ea511b6
Doc: Add small text explaining about Giraffe's secure XML parsing
64J0 18686e8
Fix: fantomas
64J0 f51addd
XML comment of redirectTo updated
Thorium e8efe0e
Add Obsolete attribute to redirectTo
64J0 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| namespace Giraffe | ||
|
|
||
| /// <summary> | ||
| /// CSRF (Cross-Site Request Forgery) protection helpers for Giraffe. | ||
| /// Provides anti-forgery token generation and validation. | ||
| /// </summary> | ||
| [<RequireQualifiedAccess>] | ||
| module Csrf = | ||
|
64J0 marked this conversation as resolved.
|
||
| open System | ||
| open System.Security.Cryptography | ||
| open System.Text | ||
| open System.Threading.Tasks | ||
| open Microsoft.AspNetCore.Http | ||
| open Microsoft.Extensions.Logging | ||
| open Microsoft.AspNetCore.Antiforgery | ||
|
|
||
| // Defaults are selected to what developers would expect from ASP.NET Core application. | ||
|
|
||
| /// <summary> | ||
| /// Default CSRF token header name | ||
| /// </summary> | ||
| [<Literal>] | ||
| let DefaultCsrfTokenHeaderName = "X-CSRF-TOKEN" | ||
|
|
||
| /// <summary> | ||
| /// Default CSRF token form field name | ||
| /// </summary> | ||
| [<Literal>] | ||
| let DefaultCsrfTokenFormFieldName = "__RequestVerificationToken" | ||
|
Thorium marked this conversation as resolved.
|
||
|
|
||
| /// <summary> | ||
| /// Validates the CSRF token from the request. | ||
| /// Checks for token in header (X-CSRF-TOKEN) or form field (__RequestVerificationToken). | ||
| /// </summary> | ||
| /// <param name="invalidTokenHandler">Optional custom handler for invalid tokens. If None, returns 403 Forbidden with logged warning.</param> | ||
| /// <param name="next">The next HttpFunc</param> | ||
| /// <param name="ctx">The HttpContext</param> | ||
| /// <returns>HttpFuncResult</returns> | ||
| let validateCsrfTokenExt (invalidTokenHandler: HttpHandler option) : HttpHandler = | ||
| fun (next: HttpFunc) (ctx: HttpContext) -> | ||
| task { | ||
| let antiforgery = ctx.GetService<IAntiforgery>() | ||
|
|
||
| try | ||
| let! isValid = antiforgery.IsRequestValidAsync ctx | ||
|
|
||
| if isValid then | ||
| return! next ctx | ||
| else | ||
| let defaultHandler = | ||
| fun (next: HttpFunc) (ctx: HttpContext) -> | ||
| let logger = ctx.GetLogger("Giraffe.Csrf") | ||
|
|
||
| logger.LogWarning( | ||
| "CSRF token validation failed for request to {Path}", | ||
| ctx.Request.Path | ||
| ) | ||
|
|
||
| ctx.Response.StatusCode <- 403 | ||
| Task.FromResult(Some ctx) | ||
|
|
||
| let handler = invalidTokenHandler |> Option.defaultValue defaultHandler | ||
| return! handler earlyReturn ctx | ||
| with ex -> | ||
| let defaultHandler = | ||
| fun (next: HttpFunc) (ctx: HttpContext) -> | ||
| let logger = ctx.GetLogger("Giraffe.Csrf") | ||
| logger.LogWarning(ex, "CSRF token validation error for request to {Path}", ctx.Request.Path) | ||
| ctx.Response.StatusCode <- 403 | ||
| Task.FromResult(Some ctx) | ||
|
|
||
| let handler = invalidTokenHandler |> Option.defaultValue defaultHandler | ||
| return! handler earlyReturn ctx | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Validates the CSRF token from the request with default error handling. | ||
| /// Checks for token in header (X-CSRF-TOKEN) or form field (__RequestVerificationToken). | ||
| /// Uses default error handling (403 Forbidden) for invalid tokens. | ||
| /// </summary> | ||
| /// <param name="next">The next HttpFunc</param> | ||
| /// <param name="ctx">The HttpContext</param> | ||
| /// <returns>HttpFuncResult</returns> | ||
| let validateCsrfToken: HttpHandler = validateCsrfTokenExt None | ||
|
|
||
| /// <summary> | ||
| /// Alias for validateCsrfToken - validates anti-forgery tokens from requests. | ||
| /// </summary> | ||
| let requireAntiforgeryToken = validateCsrfToken | ||
|
|
||
| /// <summary> | ||
| /// Alias for validateCsrfTokenExt - validates anti-forgery tokens from requests with custom error handler. | ||
| /// </summary> | ||
| let requireAntiforgeryTokenExt = validateCsrfTokenExt | ||
|
|
||
| /// <summary> | ||
| /// Generates a CSRF token and adds it to the HttpContext items for use in views. | ||
| /// The token can be accessed via ctx.Items["CsrfToken"] and ctx.Items["CsrfTokenHeaderName"]. | ||
| /// </summary> | ||
| /// <param name="next">The next HttpFunc</param> | ||
| /// <param name="ctx">The HttpContext</param> | ||
| /// <returns>HttpFuncResult</returns> | ||
| let generateCsrfToken: HttpHandler = | ||
| fun (next: HttpFunc) (ctx: HttpContext) -> | ||
| task { | ||
| let antiforgery = ctx.GetService<IAntiforgery>() | ||
| let tokens = antiforgery.GetAndStoreTokens ctx | ||
|
|
||
| // Store token for view rendering | ||
| ctx.Items.["CsrfToken"] <- tokens.RequestToken | ||
| ctx.Items.["CsrfTokenHeaderName"] <- tokens.HeaderName | ||
|
|
||
| return! next ctx | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns the CSRF token as JSON for AJAX requests. | ||
| /// Response format: { "token": "...", "headerName": "X-CSRF-TOKEN" } | ||
| /// </summary> | ||
| /// <param name="next">The next HttpFunc</param> | ||
| /// <param name="ctx">The HttpContext</param> | ||
| /// <returns>HttpFuncResult</returns> | ||
| let csrfTokenJson: HttpHandler = | ||
| fun (next: HttpFunc) (ctx: HttpContext) -> | ||
| task { | ||
| let antiforgery = ctx.GetService<IAntiforgery>() | ||
| let tokens = antiforgery.GetAndStoreTokens ctx | ||
|
|
||
| let response = | ||
| {| | ||
| token = tokens.RequestToken | ||
| headerName = tokens.HeaderName | ||
| |} | ||
|
|
||
| return! Core.json response next ctx | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns the CSRF token as an HTML hidden input field. | ||
| /// Can be included directly in forms. | ||
| /// </summary> | ||
| /// <param name="next">The next HttpFunc</param> | ||
| /// <param name="ctx">The HttpContext</param> | ||
| /// <returns>HttpFuncResult</returns> | ||
| let csrfTokenHtml: HttpHandler = | ||
| fun (next: HttpFunc) (ctx: HttpContext) -> | ||
| task { | ||
| let antiforgery = ctx.GetService<IAntiforgery>() | ||
| let tokens = antiforgery.GetAndStoreTokens(ctx) | ||
|
|
||
| let html = | ||
| sprintf "<input type=\"hidden\" name=\"%s\" value=\"%s\" />" tokens.HeaderName tokens.RequestToken | ||
|
|
||
| return! Core.htmlString html next ctx | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.