AJAX - AJAX with HTTP Cookies and SameSite Policies
1. Introduction
AJAX (Asynchronous JavaScript and XML) allows web applications to communicate with a server in the background without reloading the entire webpage. Modern applications use AJAX to retrieve data, submit forms, update user profiles, load notifications, and perform many other operations dynamically.
When an application requires users to log in, the browser must often send authentication information with AJAX requests so that the server can identify the user and determine whether the requested operation is permitted. HTTP cookies are one common mechanism used to maintain this authentication state.
However, cookies also introduce security considerations. Browsers apply rules that determine when cookies are stored and sent with requests. One important rule is the SameSite cookie policy, which helps reduce certain cross-site request forgery (CSRF) attacks.
Understanding how AJAX, HTTP cookies, and SameSite policies work together is essential for developing secure web applications.
2. What Are HTTP Cookies?
HTTP cookies are small pieces of data that a website asks the browser to store. The browser can send these cookies back to the relevant server in subsequent HTTP requests, subject to the cookie's settings and browser policies.
Cookies are commonly used for:
-
Maintaining login sessions.
-
Remembering user preferences.
-
Supporting shopping cart functionality.
-
Storing session identifiers.
-
Remembering language and regional settings.
-
Supporting certain authentication and personalization mechanisms.
For example, after a user logs in to an online shopping website, the server may create a session and send a session cookie to the browser. The browser can then include that cookie in later requests to retrieve the user's order history or update their shopping cart.
A cookie might look like this in an HTTP response:
Set-Cookie: sessionId=abc123; Path=/; Secure; HttpOnly; SameSite=Lax
This example illustrates several cookie attributes:
-
sessionId=abc123: The cookie name and value. -
Path=/: Makes the cookie available to requests matching the root path and its subpaths. -
Secure: Restricts the cookie to secure HTTPS connections. -
HttpOnly: Prevents ordinary JavaScript from reading the cookie throughdocument.cookie. -
SameSite=Lax: Restricts certain cross-site cookie transmissions.
The example value is illustrative. Real session identifiers should be unpredictable, securely generated, and managed by the server.
3. What Is the SameSite Cookie Policy?
The SameSite attribute controls whether a browser sends a cookie with requests that originate from a different website. It helps reduce the risk of CSRF attacks, in which an attacker attempts to make a browser perform an unwanted action on a website where the user is authenticated.
There are three main SameSite values.
A. SameSite=Strict
The Strict setting provides the strongest restriction on cross-site cookie sending. The browser sends the cookie for requests considered same-site but withholds it from cross-site requests.
For example, suppose a user is logged in to bank.example. If the user visits an unrelated website that attempts to initiate a request to the bank, the browser will not normally attach a Strict session cookie to that cross-site request.
This setting is useful for sensitive sessions where the application prioritizes security over convenience. However, users may find that their session is not immediately available when arriving at the site through an external link.
B. SameSite=Lax
The Lax setting permits cookies in same-site requests and certain cross-site top-level navigations using safe HTTP methods, such as clicking a link that opens the website.
It generally does not send the cookie with cross-site AJAX requests made through fetch() or XMLHttpRequest.
For example, if a user clicks a link from a news website to open their account page, the browser may send a Lax session cookie with the navigation. However, an AJAX request initiated by the news website to modify the user's account would not normally receive that cookie.
This setting is often a practical choice for session cookies because it balances usability and protection against certain CSRF attacks.
C. SameSite=None
The None setting allows the cookie to be sent with both same-site and cross-site requests, subject to other browser rules and cookie restrictions.
It must be combined with the Secure attribute in modern browsers. For example:
Set-Cookie: sessionId=abc123; Path=/; Secure; HttpOnly; SameSite=None
This configuration can be necessary when an application uses cross-site authentication or communicates with an API on a different site. However, browsers may still block the cookie under third-party cookie restrictions or other privacy policies.
Therefore, SameSite=None does not guarantee that a cookie will be accepted or sent in every cross-site situation.
4. How Cookies Work with AJAX Requests
Consider an online learning platform where a student logs in and opens a dashboard. The dashboard retrieves the student's enrolled courses using AJAX.
The process generally works as follows:
-
The student submits the login form.
-
The server verifies the login credentials.
-
The server creates a session and sends a cookie through the
Set-Cookieresponse header. -
The browser stores the cookie according to its attributes and applicable policies.
-
The webpage sends an AJAX request to retrieve the student's courses.
-
If the cookie is eligible for that request, the browser sends it automatically.
-
The server validates the session and returns the course information.
-
JavaScript processes the response and updates the dashboard without reloading the page.
For example, the browser might send this request:
GET /api/courses HTTP/1.1
Host: learning.example
Cookie: sessionId=abc123
The browser manages the Cookie header automatically. JavaScript cannot manually set this restricted request header using fetch() or XMLHttpRequest.
If the session cookie is missing, expired, or rejected by browser policy, the server may return an authentication error instead of the requested course information.
5. Using Cookies with the Fetch API
The Fetch API is a modern JavaScript interface for making network requests. Its credentials option determines whether credentials, including cookies, are sent with requests and whether applicable Set-Cookie response headers are respected.
Consider this example:
fetch("/api/courses", {
method: "GET",
credentials: "same-origin"
})
.then(response => {
if (!response.ok) {
throw new Error("Unable to retrieve courses");
}
return response.json();
})
.then(data => {
console.log(data);
})
.catch(error => {
console.error("Request failed:", error);
});
In this example:
-
fetch()sends a request to the course API. -
credentials: "same-origin"allows credentials to be included for requests to the same origin. -
response.okchecks whether the HTTP status indicates success. -
response.json()converts the response body into a JavaScript value. -
The final handler reports network or processing errors.
The same-origin option is the default for Fetch requests, so it can be omitted in this particular example. The cookie must still satisfy its own attributes and browser policies.
The three credential modes
|
Credential mode |
Behaviour |
|---|---|
|
|
Does not send credentials or respect response cookies for the request. |
|
|
Sends credentials for same-origin requests only. |
|
|
Allows credentials for cross-origin requests as well, subject to cookie policies and other browser restrictions. |
Setting credentials: "include" does not override a cookie's SameSite=Strict or SameSite=Lax restriction for cross-site requests.
6. Using Cookies with XMLHttpRequest
Before the Fetch API became widely adopted, developers commonly used the XMLHttpRequest object to perform AJAX operations. It is still supported by modern browsers.
For cross-origin requests, the withCredentials property controls whether credentials such as cookies are included.
Example:
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/profile", true);
xhr.withCredentials = true;
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
const profile = JSON.parse(xhr.responseText);
console.log(profile);
} else {
console.error("Request failed:", xhr.status);
}
};
xhr.onerror = function () {
console.error("Network error occurred");
};
xhr.send();
In this example, withCredentials = true allows the browser to include eligible credentials in the cross-origin request and accept applicable cookies from the response. The cookie must still comply with its own settings and the browser's privacy rules.
For a cross-origin request involving cookies, the server must also configure CORS appropriately. For example, it must return the permitted origin explicitly and include Access-Control-Allow-Credentials: true. Using a wildcard origin with credentialed CORS requests is not allowed.
7. Understanding Same-Origin and Cross-Origin Requests
The behaviour of cookies in AJAX depends partly on whether the request is same-origin or cross-origin.
An origin consists of the scheme, hostname, and port. For example, https://app.example.com and https://api.example.com are different origins because their hostnames differ.
However, these two origins are generally considered part of the same site for SameSite cookie purposes when they use the same scheme and share the same registrable domain.
This distinction matters because origin and site are not interchangeable concepts.
|
Request situation |
Cookie behaviour |
|---|---|
|
Same-origin AJAX request |
Eligible cookies are generally included by default with Fetch. |
|
Cross-origin request to another subdomain of the same site |
Depends on credentials settings, cookie scope, and other applicable rules. |
|
Cross-site AJAX request with a |
The cookie is generally withheld. |
|
Cross-site AJAX request with a |
The cookie is withheld. |
|
Cross-site AJAX request with a |
The cookie may be sent if credentials, browser policies, and other requirements permit it. |
For example, an application hosted at https://app.example.com may call an API at https://api.example.com. Although the request is cross-origin, it can still be same-site. The API's cookie configuration, the Fetch credentials setting, and CORS configuration determine whether the request succeeds.
8. Relationship Between SameSite and CSRF Attacks
Cross-Site Request Forgery (CSRF) occurs when an attacker tricks a user's browser into making an unwanted request to a website where the user is authenticated.
Imagine that a user is logged in to a banking website. An attacker creates a webpage containing an action that attempts to transfer money from the user's account. If the browser automatically attaches the bank's session cookie and the server does not adequately validate the request, the action might succeed.
SameSite policies help reduce this risk by restricting when cookies accompany cross-site requests.
However, SameSite is not a complete replacement for other security controls. Applications should also consider:
-
Using anti-CSRF tokens for state-changing requests.
-
Validating the
Originheader, and where appropriate theRefererheader. -
Requiring appropriate HTTP methods for state-changing operations.
-
Avoiding state changes through
GETrequests. -
Validating user permissions on the server for every sensitive operation.
These protections work together to reduce the likelihood of unauthorized actions.
9. Best Practices for Secure Cookie-Based AJAX
Developers should follow these practices when building AJAX applications that use cookies.
Use HttpOnly for session cookies. This prevents client-side JavaScript from reading the session cookie through document.cookie. It does not prevent the browser from sending the cookie automatically.
Use Secure. Session cookies should be transmitted over HTTPS rather than unencrypted HTTP.
Choose an appropriate SameSite value. Use Lax or Strict where compatible with the application's requirements. Use None; Secure only when cross-site cookie transmission is genuinely necessary.
Avoid storing sensitive session identifiers in JavaScript-accessible storage. Cookies with the HttpOnly attribute can reduce exposure of session identifiers to certain client-side attacks, although they do not eliminate the risks posed by cross-site scripting.
Configure CORS carefully. If an API requires cookies from another origin, permit only trusted origins and configure credential support explicitly. Never combine credentialed requests with an unrestricted wildcard origin.
Validate requests on the server. A cookie identifies a session, but the server must still verify authorization, validate inputs, and protect sensitive operations against CSRF.
Handle session expiration gracefully. When a session expires, the application should respond appropriately, such as prompting the user to sign in again rather than silently displaying incorrect or incomplete information.
10. Common Problems and Their Solutions
|
Problem |
Possible cause |
Solution |
|---|---|---|
|
AJAX request does not include a cookie |
Credentials setting or cookie policy prevents transmission |
Check the Fetch credentials mode or |
|
Cross-origin request fails |
Incorrect CORS configuration |
Verify the allowed origin and credential response headers. |
|
Cookie is blocked in a cross-site request |
|
Review whether cross-site cookie use is necessary and configure the cookie appropriately. |
|
Cookie is rejected by the browser |
Missing |
Serve the application over HTTPS and set the required attributes. |
|
Session expires unexpectedly |
Cookie expiry, server-side session expiry, or browser restrictions |
Inspect cookie expiration and server session settings. |
|
Authentication works in one browser but not another |
Different privacy settings or third-party cookie policies |
Test browser-specific restrictions and consider a same-site architecture where appropriate. |
Browser developer tools can help diagnose these issues. In the Network panel, developers can inspect request status, request headers, response headers, and browser-reported cookie warnings. Sensitive cookie values should not be copied into public logs or shared debugging reports.
Conclusion
AJAX with HTTP cookies and SameSite policies combines asynchronous communication with browser-managed session authentication. Cookies allow a server to recognize returning users, while SameSite controls help restrict cookie transmission in cross-site situations.
The Fetch API and XMLHttpRequest provide mechanisms for controlling credential inclusion, but neither can override the browser's cookie security policies. Secure implementation requires correctly configured cookie attributes, careful CORS settings, server-side authorization, and suitable CSRF protection.
When these techniques are used together, developers can create responsive web applications that maintain user sessions while reducing security risks.