Hone logo
Hone
Problems

Implement Cross-Site Request Forgery (CSRF) Protection in Go

Web applications are vulnerable to Cross-Site Request Forgery (CSRF) attacks where an attacker tricks a victim into performing an unwanted action on a web application in which they are currently authenticated. This challenge requires you to implement a robust CSRF protection mechanism in a Go web application to mitigate these attacks.

Problem Description

Your task is to build a simple Go web application that demonstrates and implements CSRF protection for forms that modify data. The application should have at least two routes:

  1. A GET route to display a form for submitting data (e.g., creating a new user, updating a profile). This form should include a hidden CSRF token.
  2. A POST route that handles the form submission. This route must validate the CSRF token before proceeding with the data processing.

You need to use a secure method to generate and validate CSRF tokens. The tokens should be unique per session or request, and invalidated after use (or regenerated for subsequent requests).

Key Requirements:

  • Token Generation: Implement a mechanism to generate unique and unpredictable CSRF tokens.
  • Token Inclusion: The generated token must be embedded within the HTML form, typically as a hidden input field.
  • Token Validation: The POST handler must extract the submitted token and compare it against the token stored on the server (e.g., in the user's session).
  • Error Handling: If the token is missing or invalid, the application should return an appropriate error response (e.g., HTTP 403 Forbidden).
  • Security: Tokens should be difficult to guess or tamper with. Consider using a secure random number generator.
  • Session Management: You will need a basic session management mechanism to store the CSRF token on the server-side, associated with the user's session. For simplicity in this challenge, you can simulate sessions using a global map or a simple in-memory store, but acknowledge that a real application would use more robust session storage.

Expected Behavior:

  • When a user requests the form page (GET), a unique CSRF token is generated and embedded in the form.
  • When a user submits the form with a valid token, the action is processed successfully.
  • When a user submits the form with an invalid, missing, or outdated token, the action is rejected with an error.

Edge Cases to Consider:

  • A user submitting the form without JavaScript enabled (CSRF token will be in the HTML form).
  • A user submitting the form with JavaScript (CSRF token can be sent via AJAX, but for this challenge, focus on form submission).
  • Handling multiple concurrent requests from the same user.
  • Token expiration (though for this challenge, you can focus on validation against the current session token).

Examples

Example 1: Successful Form Submission

Request 1 (GET /form):

Method: GET
Path: /form

Server Response 1:

Status: 200 OK
Content-Type: text/html

<!DOCTYPE html>
<html>
<head>
    <title>Submit Data</title>
</head>
<body>
    <form action="/submit" method="post">
        <input type="hidden" name="_csrf_token" value="generated_secure_token_abc123">
        <label for="data">Data:</label>
        <input type="text" id="data" name="data"><br><br>
        <input type="submit" value="Submit">
    </form>
</body>
</html>

Explanation: The server generates a unique CSRF token (generated_secure_token_abc123), stores it in the user's session, and embeds it as a hidden field in the HTML form.

Request 2 (POST /submit):

Method: POST
Path: /submit
Content-Type: application/x-www-form-urlencoded

_csrf_token=generated_secure_token_abc123&data=some_value

Server Response 2:

Status: 200 OK
Content-Type: text/plain

Data submitted successfully!

Explanation: The server retrieves the CSRF token from the session, compares it with the submitted token (generated_secure_token_abc123). Since they match, the submission is processed.

Example 2: Failed Form Submission (Invalid Token)

Request 1 (GET /form):

Method: GET
Path: /form

Server Response 1:

Status: 200 OK
Content-Type: text/html

<!DOCTYPE html>
<html>
<head>
    <title>Submit Data</title>
</head>
<body>
    <form action="/submit" method="post">
        <input type="hidden" name="_csrf_token" value="generated_secure_token_xyz789">
        <label for="data">Data:</label>
        <input type="text" id="data" name="data"><br><br>
        <input type="submit" value="Submit">
    </form>
</body>
</html>

Explanation: A CSRF token (generated_secure_token_xyz789) is generated and embedded.

Request 2 (POST /submit):

Method: POST
Path: /submit
Content-Type: application/x-www-form-urlencoded

_csrf_token=tampered_or_old_token_456&data=some_value

Server Response 2:

Status: 403 Forbidden
Content-Type: text/plain

CSRF token validation failed.

Explanation: The server compares the submitted token (tampered_or_old_token_456) with the one stored in the session (generated_secure_token_xyz789). They do not match, so the request is rejected.

Example 3: Failed Form Submission (Missing Token)

Request 1 (GET /form):

Method: GET
Path: /form

Server Response 1:

Status: 200 OK
Content-Type: text/html

<!DOCTYPE html>
<html>
<head>
    <title>Submit Data</title>
</head>
<body>
    <form action="/submit" method="post">
        <input type="hidden" name="_csrf_token" value="generated_secure_token_pqr111">
        <label for="data">Data:</label>
        <input type="text" id="data" name="data"><br><br>
        <input type="submit" value="Submit">
    </form>
</body>
</html>

Explanation: A CSRF token (generated_secure_token_pqr111) is generated and embedded.

Request 2 (POST /submit):

Method: POST
Path: /submit
Content-Type: application/x-www-form-urlencoded

data=some_value

Server Response 2:

Status: 403 Forbidden
Content-Type: text/plain

CSRF token validation failed.

Explanation: The server expects a CSRF token in the form data, but it is missing. The request is rejected.

Constraints

  • The Go application must be runnable using standard Go tooling.
  • You are expected to use the standard net/http package for handling HTTP requests and responses.
  • For token generation, use crypto/rand for cryptographically secure randomness.
  • Session management can be simplified using an in-memory store (e.g., map[string]string) for this challenge. Avoid using external session libraries if possible to focus on the CSRF logic itself.
  • The CSRF token should be approximately 32 bytes in length (or an equivalent secure random string).

Notes

  • Consider using a library like github.com/gorilla/csrf for inspiration on how to structure your CSRF logic, but implement the core token generation and validation yourself for this challenge.
  • Think about how the CSRF token should be associated with a user's session. For a simple challenge, a cookie could be used to identify the session.
  • The server-side storage of the CSRF token is crucial. It should be stored securely and be accessible for validation during POST requests.
  • Ensure your token generation is truly random and not predictable.
  • When regenerating tokens, consider the trade-offs between security and user experience. For this challenge, regenerating a token for each new form display is sufficient.
Loading editor...
go