Đặt cookie cho các yêu cầu nguồn gốc chéo


97

Làm thế nào để chia sẻ cookie chéo nguồn gốc? Cụ thể hơn, làm thế nào để sử dụng Set-Cookietiêu đề kết hợp với tiêu đề Access-Control-Allow-Origin?

Đây là lời giải thích về tình huống của tôi:

Tôi đang cố gắng đặt cookie cho một API đang chạy trên localhost:4000một ứng dụng web được lưu trữ trên đó localhost:3000.

Có vẻ như tôi đang nhận được các tiêu đề phản hồi phù hợp trong trình duyệt, nhưng tiếc là chúng không có tác dụng. Đây là các tiêu đề phản hồi:

HTTP / 1.1 200 OK
Access-Control-Allow-Origin: http: // localhost: 3000
Thay đổi: Nguồn gốc, Chấp nhận-Mã hóa
Set-Cookie: token = 0d522ba17e130d6d19eb9c25b7ac58387b798639f81ffe75bd449afbc3cc715d6b038e426adeac3316f0511dc7fae3f7; Tuổi tối đa = 86400; Tên miền = localhost: 4000; Đường dẫn = /; Expires = Thứ Ba, ngày 19 tháng 9 năm 2017 21:11:36 GMT; HttpOnly
Nội dung-Loại: ứng dụng / json; charset = utf-8
Nội dung-Độ dài: 180
ETag: W / "b4-VNrmF4xNeHGeLrGehNZTQNwAaUQ"
Ngày: Thứ Hai, ngày 18 tháng 9 năm 2017 21:11:36 GMT
Kết nối: giữ cho cuộc sống

Hơn nữa, tôi có thể thấy cookie bên dưới Response Cookieskhi tôi kiểm tra lưu lượng truy cập bằng tab Mạng của các công cụ dành cho nhà phát triển của Chrome. Tuy nhiên, tôi không thể thấy cookie được đặt trong tab Ứng dụng bên dưới Storage/Cookies. Tôi không thấy bất kỳ lỗi CORS nào, vì vậy tôi cho rằng tôi đang thiếu một thứ khác.

Bất kỳ đề xuất?

Cập nhật tôi:

Tôi đang sử dụng mô-đun yêu cầu trong ứng dụng React-Redux để đưa ra yêu cầu tới một /signinđiểm cuối trên máy chủ. Đối với máy chủ tôi sử dụng express.

Máy chủ Express:

res.cookie ('token', 'xxx-xxx-xxx', {maxAge: 86400000, httpOnly: true, domain: 'localhost: 3000'})

Yêu cầu trong trình duyệt:

request.post ({uri: '/ signin', json: {userName: 'userOne', password: '123456'}}, (err, response, body) => {
    // làm công việc
})

Cập nhật II:

Bây giờ tôi đang đặt tiêu đề yêu cầu và phản hồi như điên rồ, đảm bảo rằng chúng có mặt trong cả yêu cầu và phản hồi. Dưới đây là ảnh chụp màn hình. Chú ý các tiêu đề Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-MethodsAccess-Control-Allow-Origin. Xem xét vấn đề tôi tìm thấy trên github của Axios , tôi có ấn tượng rằng tất cả các tiêu đề bắt buộc hiện đã được thiết lập. Tuy nhiên, vẫn không có may mắn ...

nhập mô tả hình ảnh ở đây


4
@PimHeijden hãy xem điều này: developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/… có lẽ việc sử dụng withCredentials là những gì bạn cần?
Kalamarico

2
Ok, bạn đang sử dụng request và tôi nghĩ đây không phải là lựa chọn tốt nhất, hãy xem bài đăng này và câu trả lời, tiên đề tôi nghĩ có thể hữu ích cho bạn. stackoverflow.com/questions/39794895/…
Kalamarico

Cảm ơn! Tôi không nhận thấy rằng requestmô-đun không được sử dụng trong trình duyệt. Axios dường như làm rất tốt cho đến nay. Bây giờ tôi nhận được cả tiêu đề: Access-Control-Allow-Credentials:trueAccess-Control-Allow-Origin:http://localhost:3000(được sử dụng để kích hoạt CORS). Điều này có vẻ đúng nhưng Set-Cookiedoesnt tiêu đề làm bất cứ điều gì ...
Pim Heijden

Vấn đề tương tự nhưng sử dụng trực tiếp Axios: stackoverflow.com/q/43002444/488666 . Mặc dù { withCredentials: true }thực sự được yêu cầu bởi phía Axios, nhưng tiêu đề máy chủ cũng phải được kiểm tra cẩn thận (xem stackoverflow.com/a/48231372/488666 )
Frosty Z

tiêu đề máy chủ nào?
Pim Heijden

Câu trả lời:


156

Bạn cần gì để làm

Để cho phép nhận và gửi cookie theo yêu cầu CORS thành công, hãy làm như sau.

Back-end (máy chủ): Đặt Access-Control-Allow-Credentialsgiá trị tiêu đề HTTP thành true. Ngoài ra, hãy đảm bảo rằng các tiêu đề HTTP Access-Control-Allow-OriginAccess-Control-Allow-Headersđược đặt và không có ký tự đại diện* .

Để biết thêm thông tin về cài đặt CORS trong js express, hãy đọc tài liệu tại đây

Front-end (client): Đặt XMLHttpRequest.withCredentialscờ thành true, điều này có thể đạt được theo nhiều cách khác nhau tùy thuộc vào thư viện yêu cầu-phản hồi được sử dụng:

Hoặc là

Tránh phải sử dụng CORS kết hợp với cookie. Bạn có thể đạt được điều này với một proxy.

Nếu bạn vì bất cứ lý do gì đừng tránh nó. Giải pháp là ở trên.

Hóa ra là Chrome sẽ không đặt cookie nếu miền chứa cổng. Đặt nó cho localhost(không có cổng) không phải là một vấn đề. Rất cám ơn Erwin về mẹo này!


2
Tôi nghĩ bạn có vấn đề này chỉ vì sự localhostcheck here này: stackoverflow.com/a/1188145 và cũng này có thể giúp trường hợp của bạn ( stackoverflow.com/questions/50966861/... )
Edwin

5
Câu trả lời này đã giúp tôi rất nhiều! Mất một thời gian dài để tìm thấy nó. Nhưng tôi nghĩ câu trả lời nên đề cập đến việc cài đặt Access-Control-Allow-Originthành một miền rõ ràng, không chỉ "*"là bắt buộc. Sau đó, nó sẽ là câu trả lời hoàn hảo
e.dan

6
đây là câu trả lời hay và tất cả thiết lập cho CORS, tiêu đề, phụ trợ và giao diện người dùng, đồng thời tránh máy chủ cục bộ có ghi đè / etc / hosts cục bộ với miền phụ thực, tôi vẫn thấy người đưa thư hiển thị SET-COOKIE trong tiêu đề phản hồi nhưng gỡ lỗi chrome thì không hiển thị điều này trong tiêu đề phản hồi và cookie cũng không thực sự được đặt trong chrome. Bất kỳ ý tưởng khác để kiểm tra?
bjm88

1
@ bjm88 Bạn đã tìm ra điều này chưa? Tôi cũng đang ở trong hoàn cảnh tương tự. Cookie được đặt đúng cách khi kết nối từ localhost: 3010 đến localhost: 5001 nhưng không hoạt động từ localhost: 3010 thành fakeremote: 5001 (trỏ đến 127.0.0.1 trong tệp máy chủ của tôi). Nó hoàn toàn giống khi tôi lưu trữ máy chủ của mình trên một máy chủ thực với miền tùy chỉnh (kết nối từ localhost: 3010 đến mydomain.com). Tôi đã làm tất cả những gì được đề xuất trong câu trả lời này và tôi đã thử rất nhiều thứ khác.
Biểu mẫu

1
Trong Angular, thay đổi phía máy khách được yêu cầu cũng là thêm withCredentials: true vào các tùy chọn được chuyển đến HttpClient.post, .get, options, v.v.
Marvin

13

Lưu ý cho Trình duyệt Chrome được phát hành vào năm 2020.

Bản phát hành trong tương lai của Chrome sẽ chỉ cung cấp cookie với các yêu cầu trên nhiều trang web nếu chúng được đặt bằng SameSite=NoneSecure.

Vì vậy, nếu máy chủ phụ trợ của bạn không đặt SameSite = None, Chrome sẽ sử dụng SameSite = Lax theo mặc định và sẽ không sử dụng cookie này với các yêu cầu {withCredentials: true}.

Thông tin thêm https://www.chromium.org/updates/same-site .

Các nhà phát triển Firefox và Edge cũng muốn phát hành tính năng này trong tương lai.

Thông số được tìm thấy ở đây: https://tools.ietf.org/html/draft-west-cookie-incrementalism-01#page-8


2
cung cấp samesite = none và cờ an toàn yêu cầu HTTPS. Làm thế nào để đạt được điều này trong một hệ thống cục bộ nơi HTTPS không phải là một tùy chọn? chúng ta có thể bỏ qua bằng cách nào đó không?
nirmal patel

@nirmalpatel Chỉ cần xóa giá trị "Lax" trong bảng điều khiển dành cho nhà phát triển Chome.
LennyLip

3

Để khách hàng có thể đọc cookie từ các yêu cầu có nguồn gốc chéo, bạn cần có:

  1. Tất cả các phản hồi từ máy chủ cần có những điều sau trong tiêu đề của chúng:

    Access-Control-Allow-Credentials: true

  2. Khách hàng cần gửi tất cả các yêu cầu với withCredentials: truetùy chọn

Trong quá trình triển khai với Angular 7 và Spring Boot, tôi đã đạt được điều đó với những điều sau:


Phía máy chủ:

@CrossOrigin(origins = "http://my-cross-origin-url.com", allowCredentials = "true")
@Controller
@RequestMapping(path = "/something")
public class SomethingController {
  ...
}

Phần origins = "http://my-cross-origin-url.com"này sẽ thêm Access-Control-Allow-Origin: http://my-cross-origin-url.comvào mọi tiêu đề phản hồi của máy chủ

Phần allowCredentials = "true"này sẽ thêm Access-Control-Allow-Credentials: truevào mọi tiêu đề phản hồi của máy chủ, đó là những gì chúng tôi cần để khách hàng đọc cookie


Phía khách hàng:

import { HttpInterceptor, HttpXsrfTokenExtractor, HttpRequest, HttpHandler, HttpEvent } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from 'rxjs';

@Injectable()
export class CustomHttpInterceptor implements HttpInterceptor {

    constructor(private tokenExtractor: HttpXsrfTokenExtractor) {
    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // send request with credential options in order to be able to read cross-origin cookies
        req = req.clone({ withCredentials: true });

        // return XSRF-TOKEN in each request's header (anti-CSRF security)
        const headerName = 'X-XSRF-TOKEN';
        let token = this.tokenExtractor.getToken() as string;
        if (token !== null && !req.headers.has(headerName)) {
            req = req.clone({ headers: req.headers.set(headerName, token) });
        }
        return next.handle(req);
    }
}

Với lớp này, bạn thực sự đưa những thứ bổ sung vào tất cả các yêu cầu của bạn.

Phần đầu tiên req = req.clone({ withCredentials: true });, là những gì bạn cần để gửi từng yêu cầu với withCredentials: truetùy chọn. Điều này thực tế có nghĩa là một yêu cầu TÙY CHỌN sẽ được gửi trước, để bạn nhận được cookie của mình và mã thông báo ủy quyền trong số đó, trước khi gửi các yêu cầu POST / PUT / DELETE thực tế, cần mã thông báo này được đính kèm với chúng (trong tiêu đề), trong để máy chủ xác minh và thực hiện yêu cầu.

Phần thứ hai là phần xử lý đặc biệt mã thông báo chống CSRF cho tất cả các yêu cầu. Đọc nó từ cookie khi cần và ghi nó vào tiêu đề của mọi yêu cầu.

Kết quả mong muốn là như thế này:

phản ứng yêu cầu


câu trả lời này bổ sung gì cho câu trả lời hiện có?
Pim Heijden

1
Một triển khai thực tế. Lý do tôi quyết định đăng nó là tôi đã dành rất nhiều thời gian để tìm kiếm cùng một vấn đề và thêm các phần lại với nhau từ các bài viết khác nhau để hiện thực hóa nó. Sẽ dễ dàng hơn nhiều cho một người nào đó làm điều tương tự, có bài đăng này như một sự so sánh.
Stefanos Kargas

Hiển thị cài đặt allowCredentials = "true"trong @CrossOriginchú thích đã giúp tôi.
ponder275

@lennylip được đề cập trong câu trả lời của anh ấy ở trên, nó đang hiển thị lỗi cho samesite và cờ an toàn. Làm thế nào để đạt được điều đó với máy chủ localhost mà không có cờ bảo mật.
nirmal patel

0

Câu trả lời của Pim rất hữu ích. Trong trường hợp của tôi, tôi phải sử dụng

Expires / Max-Age: "Session"

Nếu đó là dateTime, ngay cả khi nó chưa hết hạn, nó vẫn sẽ không gửi cookie đến backend:

Expires / Max-Age: "Thu, 21 May 2020 09:00:34 GMT"

Hy vọng nó sẽ hữu ích cho những người trong tương lai có thể gặp vấn đề tương tự.


0

Đối với express, hãy nâng cấp thư viện express của bạn lên 4.17.1phiên bản ổn định mới nhất. Sau đó;

Trong CorsOption: Set originđể url localhost của bạn hoặc url sản xuất frontend của bạn và credentialsđể true ví dụ

  const corsOptions = {
    origin: config.get("origin"),
    credentials: true,
  };

Tôi đặt nguồn gốc của mình động bằng cách sử dụng mô-đun config npm .

Sau đó, trong res.cookie:

Đối với localhost: bạn không cần phải thiết lập sameSite và lựa chọn an toàn ở tất cả, bạn có thể thiết lập httpOnlyđể truecho http cookie Ngăn chặn XSS tấn công và hữu ích khác tùy chọn tùy thuộc vào trường hợp sử dụng của bạn.

Đối với môi trường sản xuất, bạn cần phải thiết lập sameSiteđể nonecho yêu cầu cross-nguồn gốc và secuređến true. Hãy nhớ chỉ sameSitehoạt động với phiên bản mới nhất hiện tại và phiên bản chrome mới nhất chỉ thiết lập cookie https, do đó cần có tùy chọn bảo mật.

Đây là cách tôi làm cho năng động của tôi

 res
    .cookie("access_token", token, {
      httpOnly: true,
      sameSite: app.get("env") === "development" ? true : "none",
      secure: app.get("env") === "development" ? false : true,
    })
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.