Same-Origin Policy and Cross-Origin Resource Sharing (CORS) Explained in Detail
I. Knowledge Point Description
The Same-Origin Policy (SOP) is the most fundamental security feature of browsers. It restricts how documents or scripts from one origin (origin) can interact with resources from another origin. This is a crucial isolation mechanism that effectively prevents malicious websites from stealing data. Cross-Origin Resource Sharing (CORS), on the other hand, is a mechanism based on HTTP headers that allows servers to instruct browsers which external origins have permission to access their resources, thereby legitimately bypassing the restrictions of the Same-Origin Policy while ensuring security. Understanding how these two work together is essential for modern web application development.
II. Step-by-Step Explanation
Step 1: Deep Understanding of "Origin"
- Definition: An "origin" consists of three parts: protocol, domain name (or IP address), and port.
- Judgment Logic: The browser considers two URLs to be "same-origin" only when their protocol, domain name, and port are exactly the same. Any mismatch in one of these components results in them being considered "cross-origin".
- Examples:
- Assume the current page URL is:
https://www.example.com:443/docs/page.html - Same Origin:
https://www.example.com:443/docs/another.html(protocol, domain, port all identical) - Different Origin (Cross-Origin):
http://www.example.com/docs/page.html(Protocol differs, HTTP vs HTTPS)https://api.example.com/docs/page.html(Domain differs,wwwvsapi)https://www.example.com:8080/docs/page.html(Port differs, 443 vs 8080)
- Assume the current page URL is:
Step 2: Specific Restrictive Behaviors of the Same-Origin Policy
The Same-Origin Policy primarily governs the following three types of behaviors:
- DOM Access Restriction: Prevents pages from different origins from accessing each other's DOM via JavaScript. For example, if an
iframeembeds a cross-origin page, the parent page's JavaScript cannot read or modify the content inside theiframe. - Network Request (Ajax/Fetch) Restriction: By default, cross-origin HTTP requests initiated using
XMLHttpRequestor theFetch APIare blocked by the browser. This is the most common issue encountered in development. - Cookie, LocalStorage, and IndexedDB Access Restriction: Browsers only allow a page to read data stored for sites from the same origin.
Important Exception: Some HTML tags inherently allow loading resources cross-origin, such as <img>, <script>, <link>, <iframe> (Note: it's about loading resources, not accessing their DOM). This is the basis for technologies like JSONP but can also pose security risks.
Step 3: Why is CORS Needed?
Modern web applications often follow a frontend-backend separation architecture. The frontend application might be deployed at https://webapp.com, while the backend API server resides at https://api.service.com. According to the Same-Origin Policy, the frontend cannot directly call the backend API. To address this legitimate need, the W3C established the CORS standard, allowing servers to explicitly declare which external origins are permitted to access their resources.
Step 4: How CORS Works (The Core is the "Preflight Request")
CORS categorizes requests into two types: Simple Requests and Non-Simple Requests.
-
Simple Request:
- Conditions: The request method is one of GET, HEAD, or POST, and the request headers are limited to a few specific ones (e.g.,
Accept,Accept-Language,Content-Language,Content-Typewith values limited toapplication/x-www-form-urlencoded,multipart/form-data,text/plain). - Process: The browser sends the request directly but automatically adds an
Originheader to the request, indicating the source of the request (e.g.,Origin: https://webapp.com). Upon receiving it, if the server allows access from that origin, it should includeAccess-Control-Allow-Origin: https://webapp.com(or use the wildcard*to allow any origin) in the response headers. The browser checks the response headers; if they match, it allows the frontend application to read the response; otherwise, it throws an error.
- Conditions: The request method is one of GET, HEAD, or POST, and the request headers are limited to a few specific ones (e.g.,
-
Non-Simple Request (Preflighted Request):
- Trigger Conditions: Using methods like PUT or DELETE, or having a Content-Type of
application/json, or including custom headers (e.g.,Authorization). - Process (Two Steps):
- a. Preflight Request:
- Before the actual request is sent, the browser automatically sends a preflight request using the OPTIONS method.
- This request also includes the
Originheader indicating the source. - It also contains two special headers:
Access-Control-Request-Method: Declares the method (e.g.,PUT) that will be used in the actual request.Access-Control-Request-Headers: Declares the custom headers (e.g.,Authorization) that will be carried in the actual request.
- b. Server Responds to Preflight:
- The server must respond correctly to this OPTIONS request.
- The response headers must include:
Access-Control-Allow-Origin: The allowed origin(s).Access-Control-Allow-Methods: The allowed actual request methods (e.g.,PUT, POST).Access-Control-Allow-Headers: The allowed actual request headers (e.g.,Authorization).
- It can also include
Access-Control-Max-Age, which specifies how long the preflight result can be cached to avoid repeated preflight requests.
- c. Actual Request:
- Only after the preflight request passes will the browser send the actual request. The actual request and response process is similar to that of a simple request, also requiring
OriginandAccess-Control-Allow-Originheaders, among others.
- Only after the preflight request passes will the browser send the actual request. The actual request and response process is similar to that of a simple request, also requiring
- a. Preflight Request:
- Trigger Conditions: Using methods like PUT or DELETE, or having a Content-Type of
Step 5: Other Important CORS-Related Headers
Access-Control-Allow-Credentials: When the request needs to carry cookies or HTTP authentication information (requiring the request to havecredentials: 'include'), the server must set this header totrue. Furthermore,Access-Control-Allow-Origincannot be the wildcard*; it must be an explicit origin.Access-Control-Expose-Headers: A whitelist of response headers that frontend JavaScript is allowed to access. By default, the frontend can only access a few basic response headers (e.g., Cache-Control, Content-Language, Content-Type).
Summary and Defense Perspective
- The Same-Origin Policy is the browser's "deny by default" policy, serving as the first line of defense in protecting users.
- CORS is the server-controlled "selective allow" policy. Server developers must configure CORS headers carefully.
- Risk of Misconfiguration: Setting
Access-Control-Allow-Originto*is convenient but means any website can access your resources via frontend scripts (if the request doesn't require credentials). The safest approach is to maintain a whitelist of allowed origins. - For requests requiring credentials, never use
*. - Follow the principle of least privilege: only list necessary items in
Access-Control-Allow-MethodsandAccess-Control-Allow-Headers.
- Risk of Misconfiguration: Setting
By understanding the restrictions of the Same-Origin Policy and the negotiation mechanism of CORS, you can correctly handle cross-origin issues in development and ensure secure configuration of API endpoints.