-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add to middleware in app's `src/main.zig`: ```zig pub const jetzig_options = struct { pub const middleware: []const type = &.{ jetzig.middleware.AntiCsrfMiddleware, }; }; ``` CSRF token available in Zmpl templates: ``` {{context.authenticityToken()}} ``` or render a hidden form element: ``` {{context.authenticityFormElement()}} ``` The following HTML requests are rejected (403 Forbidden) if the submitted query param does not match the value stored in the encrypted session (added automatically when the token is generated for a template value): * POST * PUT * PATCH * DELETE JSON requests are not impacted - users should either disable JSON endpoints or implement a different authentication method to protect them.
- Loading branch information
Showing
28 changed files
with
694 additions
and
138 deletions.
There are no files selected for viewing
This file contains 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 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 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 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,77 @@ | ||
const std = @import("std"); | ||
const jetzig = @import("jetzig"); | ||
|
||
pub const layout = "application"; | ||
|
||
pub const actions = .{ | ||
.before = .{jetzig.middleware.AntiCsrfMiddleware}, | ||
}; | ||
|
||
pub fn post(request: *jetzig.Request) !jetzig.View { | ||
var root = try request.data(.object); | ||
|
||
const Params = struct { spam: []const u8 }; | ||
const params = try request.expectParams(Params) orelse { | ||
return request.fail(.unprocessable_entity); | ||
}; | ||
|
||
try root.put("spam", params.spam); | ||
|
||
return request.render(.created); | ||
} | ||
|
||
pub fn index(request: *jetzig.Request) !jetzig.View { | ||
return request.render(.ok); | ||
} | ||
|
||
test "post with missing token" { | ||
var app = try jetzig.testing.app(std.testing.allocator, @import("routes")); | ||
defer app.deinit(); | ||
|
||
const response = try app.request(.POST, "/anti_csrf", .{}); | ||
try response.expectStatus(.forbidden); | ||
} | ||
|
||
test "post with invalid token" { | ||
var app = try jetzig.testing.app(std.testing.allocator, @import("routes")); | ||
defer app.deinit(); | ||
|
||
const response = try app.request(.POST, "/anti_csrf", .{}); | ||
try response.expectStatus(.forbidden); | ||
} | ||
|
||
test "post with valid token but missing expected params" { | ||
var app = try jetzig.testing.app(std.testing.allocator, @import("routes")); | ||
defer app.deinit(); | ||
|
||
_ = try app.request(.GET, "/anti_csrf", .{}); | ||
const token = app.session.getT(.string, jetzig.authenticity_token_name).?; | ||
const response = try app.request( | ||
.POST, | ||
"/anti_csrf", | ||
.{ .params = .{ ._jetzig_authenticity_token = token } }, | ||
); | ||
try response.expectStatus(.unprocessable_entity); | ||
} | ||
|
||
test "post with valid token and expected params" { | ||
var app = try jetzig.testing.app(std.testing.allocator, @import("routes")); | ||
defer app.deinit(); | ||
|
||
_ = try app.request(.GET, "/anti_csrf", .{}); | ||
const token = app.session.getT(.string, jetzig.authenticity_token_name).?; | ||
const response = try app.request( | ||
.POST, | ||
"/anti_csrf", | ||
.{ .params = .{ ._jetzig_authenticity_token = token, .spam = "Spam" } }, | ||
); | ||
try response.expectStatus(.created); | ||
} | ||
|
||
test "index" { | ||
var app = try jetzig.testing.app(std.testing.allocator, @import("routes")); | ||
defer app.deinit(); | ||
|
||
const response = try app.request(.GET, "/anti_csrf", .{}); | ||
try response.expectStatus(.ok); | ||
} |
This file contains 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,8 @@ | ||
<form action="/anti_csrf" method="POST"> | ||
{{context.authenticityFormElement()}} | ||
|
||
<label>Enter spam here:</label> | ||
<input type="text" name="spam" /> | ||
|
||
<input type="submit" value="Submit Spam" /> | ||
</form> |
This file contains 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,5 @@ | ||
<h1>Spam Submitted Successfully</h1> | ||
|
||
<h2>Spam:</h2> | ||
|
||
<div>{{$.spam}}</div> |
This file contains 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 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 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 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 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 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 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,25 @@ | ||
const std = @import("std"); | ||
|
||
pub const http = @import("http.zig"); | ||
pub const config = @import("config.zig"); | ||
|
||
/// Context available in every Zmpl template as `context`. | ||
pub const TemplateContext = @This(); | ||
|
||
request: ?*http.Request = null, | ||
|
||
pub fn authenticityToken(self: TemplateContext) !?[]const u8 { | ||
return if (self.request) |request| | ||
try request.authenticityToken() | ||
else | ||
null; | ||
} | ||
|
||
pub fn authenticityFormElement(self: TemplateContext) !?[]const u8 { | ||
return if (self.request) |request| blk: { | ||
const token = try request.authenticityToken(); | ||
break :blk try std.fmt.allocPrint(request.allocator, | ||
\\<input type="hidden" name="{s}" value="{s}" /> | ||
, .{ config.get([]const u8, "authenticity_token_name"), token }); | ||
} else null; | ||
} |
Oops, something went wrong.