Defending Against CSRF Attacks. Best practices for API protection.

Learn how to implement CSRF protection and defend your API endpoints against malicious attacks.

The general best practice is to ensure that only the server-side application has the ability to insert records into the database. This is important for maintaining the integrity of the data and preventing unauthorized or malicious modifications.

Developers often use AJAX requests or newer methods like Fetch, which utilize promises or async/await, in their front-end interfaces to retrieve data from API endpoints. It's important to note that when making POST requests to the API, developers sometimes overlook the importance of protecting these requests. Regardless of whether the target database table contains sensitive information or not, it's crucial to safeguard it against malicious attacks that attempt to insert harmful code.

As a web developer, I frequently encounter such attempts, with SQL injection and CSRF attacks being among the most common. Looking ahead, it's likely that the frequency of these attacks will only increase. Whether the attackers are bug bounty hunters, amateurs, or sophisticated actors, it's essential to continuously explore different ways to secure web applications.

CSRF stands for Cross-Site Request Forgery. It is called "cross-site" because the attack involves making a request from one website (the attacker's site) to another website (the victim's site), exploiting the fact that the victim's site trusts requests coming from the user's browser, even if they are initiated by another site. This allows the attacker to forge a request that appears to be legitimate, potentially causing the victim's site to perform an unwanted action on behalf of the user. The CSRF attack can be initiated not only from a website but also from other sources like Postman or any application that can send HTTP requests. As long as the request is sent from the user's browser or an application that can mimic a user's actions, it can be used to carry out a CSRF attack.

Today, I'd like to discuss how to protect your API endpoints, especially when using jQuery's AJAX or JavaScript's fetch requests in your application's front-end.

To prevent unauthorized access to sensitive actions in web applications, developers commonly use CSRF tokens. These tokens are included in requests made by the user's browser to ensure that they originate from the expected user and not from a malicious source. Unlike reCAPTCHA, which verifies user humanity, CSRF tokens verify the authenticity of requests made to a web application.

Creating a CSRF token is straightforward. Simply generate the token and store it securely in the session array. Use a secure method, such as cryptographic functions, to create a random and unpredictable token. Store the CSRF token on the server side and avoid storing it in cookies or local storage on the client side to prevent access by malicious scripts.

$_SESSION['csrf_token'] = bin2hex(random_bytes(32));

To transmit the CSRF token in an AJAX request, include it in the request data. For example:

let csrfToken = '';
$.ajax({
    url: '/api/endpoint',
    type: 'POST',
    data: {
        article: article,
        csrf_token: csrfToken
    },
    // Other AJAX settings
});

On the server side, verify the CSRF token before processing the request. Compare the token sent in the request with the token stored in the session. If the tokens match, process the request; otherwise, handle the error. It's also a good practice to periodically rotate CSRF tokens to reduce the risk of token leakage and reuse in CSRF attacks.

if ($_POST['csrf_token'] === $_SESSION['csrf_token']):
 // CSRF token is valid, process the request
 // Regenerate the token for the next request
 unset($_SESSION['csrf_token']);
else:
 // CSRF token is invalid, handle the error
 http_response_code(403);
 exit('CSRF token validation failed');
endif;

If the CSRF token is unset on the server side, refresh the token on the client side before making another AJAX request. Update the csrfToken variable with the new token value retrieved from the server. It is great to periodically rotate CSRF tokens to reduce the risk of token leakage and reuse in CSRF attacks. It is better to generate a new token for each session or request.

In case of an error, this code handles the response by checking if the status code is 403, indicating an invalid or expired CSRF token. If so, it sends a GET request to an endpoint /refresh_csrf_token to obtain a new token. If the request is successful, it updates the csrfToken variable with the new token. If the request to refresh the token fails, it logs an error message.

error: function(xhr, status, error) {
    // Handle error response
    if (xhr.status === 403) {
        // CSRF token is invalid or expired, refresh the token
        $.ajax({
            url: '/refresh_csrf_token', // Endpoint to refresh CSRF token
            type: 'GET',
            success: function(newToken) {
                csrfToken = newToken; // Update the csrfToken variable with the new token
            },
            error: function() {
                // Handle error while refreshing token
                console.error('Failed to refresh CSRF token');
            }
        });
    }
}

This is just the tip of the iceberg when it comes to CSRF tokens, a powerful tool in your arsenal against CSRF attacks. Keep digging, keep learning, and explore more ways to fortify your web applications. The world of web security is vast and ever-evolving, and your dedication to understanding it better will pay off in safeguarding your projects. Keep pushing the boundaries of your knowledge and skills to protect your users and your work.

Regardless of your interest, learning about cybersecurity is essential. The internet is filled with individuals who may try to disrupt your life and work, making it crucial to understand how to protect yourself and your assets.

Good luck!
May all your endeavors be successful, and may your code always run smoothly.
Best Regards,
Artem

467