跨源资源共享和Cookie问题整理

浏览器的同源策略 = 协议 + 域名 + 端口相同

1. 跨源资源共享 CROS

简单请求不会触发CROS预检请求。

预检请求(非简单请求)使用OPTIONS发送一个预检请求到服务器。

预检请求不能包含凭据。预检请求的 响应 必须指定 Access-Control-Allow-Credentials: true 来表明可以携带凭据进行实际的请求。

在响应附带身份凭证的请求时:

  • 服务器不能将 Access-Control-Allow-Origin 的值设为通配符“*”,而应将其设置为特定的域,如:Access-Control-Allow-Origin: https://example.com
  • 服务器不能将 Access-Control-Allow-Headers 的值设为通配符“*”,而应将其设置为首部名称的列表,如:Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
  • 服务器不能将 Access-Control-Allow-Methods 的值设为通配符“*”,而应将其设置为特定请求方法名称的列表,如:Access-Control-Allow-Methods: POST, GET

Access-Control-Allow-HeadersAccess-Control-Allow-Methods用于预检请求。

Access-Control-Expose-Headers头让服务器把允许浏览器访问的头放入白名单

对于附带身份凭证的请求(通常是 Cookie),服务器不得设置 Access-Control-Allow-Origin 的值为“*”。

Nginx配置OPTIONS

if ($request_method ~* "OPTIONS"){
    add_header 'Access-Control-Allow-Origin' "$http_origin";
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Headers' 'User-Agent,Keep-Alive,Content-Type';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE, PUT';
    return 204;
 }

PHP服务端

header('Access-Control-Allow-Origin:' . $origin);
header('Access-Control-Allow-Credentials:true');
//预检请求由nginx处理,实际请求只需要返回以上两项
//header('Access-Control-Allow-Headers:Origin, Content-Type, Cookie, X-CSRF-TOKEN, Accept, Authorization, X-XSRF-TOKEN');
//header('Access-Control-Allow-Methods:GET, POST, PATCH, PUT, OPTIONS');
laravel框架使用 fruitcake/laravel-cors中间件,预检请求和实际请求由中间件处理。    
    
config/cors.php配置

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Laravel CORS Options
    |--------------------------------------------------------------------------
    |
    | The allowed_methods and allowed_headers options are case-insensitive.
    |
    | You don't need to provide both allowed_origins and allowed_origins_patterns.
    | If one of the strings passed matches, it is considered a valid origin.
    |
    | If ['*'] is provided to allowed_methods, allowed_origins or allowed_headers
    | all methods / origins / headers are allowed.
    |
    */

    /*
     * You can enable CORS for 1 or multiple paths.
     * Example: ['api/*']
     */
     'paths' => ['api/*'],

    /*
    * Matches the request method. `['*']` allows all methods.
    */
    'allowed_methods' => ['*'],

    /*
     * Matches the request origin. `['*']` allows all origins. Wildcards can be used, eg `*.mydomain.com`
     */
    'allowed_origins' => ['*.gophper.com','*.gophper.com:8080','http://localhost:8080'],

    /*
     * Patterns that can be used with `preg_match` to match the origin.
     */
    'allowed_origins_patterns' => [],

    /*
     * Sets the Access-Control-Allow-Headers response header. `['*']` allows all headers.
     */
    'allowed_headers' => ["*"],

    /*
     * Sets the Access-Control-Expose-Headers response header with these headers.
     */
    'exposed_headers' => [],

    /*
     * Sets the Access-Control-Max-Age response header when > 0.
     */
    'max_age' => 0,
    
    /*
     * Sets the Access-Control-Allow-Credentials header.
     */
    'supports_credentials' => true,
];

Cookie 策略受 SameSite 属性控制,限制第三方Cookie。大多数浏览器设置Lax为默认值。

http://a.dev.gophper.com:8080 访问 http://b.dev.gophper.com接口,可以设置Cookie。

http://a.dev.gophper.com:8080 访问 https://b.dev.gophper.com接口,不可以设置Cookie。

协议相同,主域名相同,端口允许不同。

Cookie的作用域仅仅由domain和path决定,与协议和端口无关。

3. 参考

https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Expose-Headers https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie/SameSite