Spring 5.0.3 RequestRejectedException: Yêu cầu đã bị từ chối vì URL không được chuẩn hóa


88

Tôi không chắc đây là lỗi với Spring 5.0.3 hay một tính năng mới để khắc phục mọi thứ.

Sau khi nâng cấp, tôi gặp lỗi này. Điều thú vị là lỗi này chỉ xảy ra trên máy cục bộ của tôi. Mã tương tự trên môi trường thử nghiệm với giao thức HTTPS hoạt động tốt.

Đang tiếp tục ...

Lý do tôi nhận được lỗi này là vì URL của tôi để tải trang JSP kết quả /location/thisPage.jsp. Đánh giá mã request.getRequestURI()cho tôi kết quả /WEB-INF/somelocation//location/thisPage.jsp. Nếu tôi sửa URL của trang JSP thành điều này location/thisPage.jsp, mọi thứ hoạt động tốt.

Vì vậy, câu hỏi của tôi là, tôi có nên xóa /khỏi JSPđường dẫn trong mã không vì đó là những gì được yêu cầu trong tương lai. Hoặc Springđã giới thiệu một lỗi vì sự khác biệt duy nhất giữa máy của tôi và môi trường thử nghiệm là giao thức HTTPso với HTTPS.

 org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL was not normalized.
    at org.springframework.security.web.firewall.StrictHttpFirewall.getFirewalledRequest(StrictHttpFirewall.java:123)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:194)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)


1
Vấn đề được lên kế hoạch giải quyết trong 5.1.0; Hiện tại 5.0.0 không có vấn đề này.
java_dude 19/02/18

Câu trả lời:


67

Tài liệu bảo mật mùa xuân đề cập đến lý do chặn // trong yêu cầu.

Ví dụ: nó có thể chứa các trình tự duyệt qua đường dẫn (như /../) hoặc nhiều dấu gạch chéo về phía trước (//), điều này cũng có thể khiến đối sánh mẫu không thành công. Một số vùng chứa chuẩn hóa chúng trước khi thực hiện ánh xạ servlet, nhưng những vùng chứa khác thì không. Để bảo vệ khỏi những vấn đề như thế này, FilterChainProxy sử dụng chiến lược HttpFirewall để kiểm tra và kết thúc yêu cầu. Các yêu cầu chưa chuẩn hóa sẽ tự động bị từ chối theo mặc định và các thông số đường dẫn và dấu gạch chéo trùng lặp bị xóa vì mục đích phù hợp.

Vì vậy, có hai giải pháp khả thi -

  1. loại bỏ dấu gạch chéo kép (cách tiếp cận ưa thích)
  2. Cho phép // trong Bảo mật mùa xuân bằng cách tùy chỉnh Bức tường nghiêm ngặt bằng cách sử dụng mã bên dưới.

Bước 1 Tạo tường lửa tùy chỉnh cho phép gạch chéo trong URL.

@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
    StrictHttpFirewall firewall = new StrictHttpFirewall();
    firewall.setAllowUrlEncodedSlash(true);    
    return firewall;
}

Bước 2 Và sau đó cấu hình bean này trong websecurity

@Override
public void configure(WebSecurity web) throws Exception {
    //@formatter:off
    super.configure(web);
    web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
....
}

Bước 2 là bước tùy chọn, Spring Boot chỉ cần khai báo kiểu bean HttpFirewall


Có bảo mật theo đường dẫn đã được giới thiệu. Đó là một tính năng mới và điều này có thể gây ra sự cố. Điều mà tôi không chắc lắm vì bạn thấy nó hoạt động trên HTTPS chứ không phải trên HTTP. Tôi thà đợi cho đến khi lỗi này được giải quyết jira.spring.io/browse/SPR-16419
java_dude

rất có thể là một phần của vấn đề của chúng tôi ... nhưng ... người dùng không nhập // nên tôi đang cố gắng tìm ra cách thứ hai / đó được thêm vào ở vị trí đầu tiên ... nếu mùa xuân đang tạo ra jstl url nó không nên thêm nó, hoặc bình thường hóa nó sau khi thêm nó.
xenoterracide

4
Điều này thực sự không giải quyết được giải pháp, ít nhất là đối với Spring Security 5.1.1. Bạn phải sử dụng DefaultHttpFirewall nếu bạn cần URL có hai dấu gạch chéo như a / b // c. Phương thức isNormalized không thể được định cấu hình hoặc ghi đè trong Bức tường nghiêm ngặt.
Jason Winnebeck

Bất kỳ cơ hội nào ai đó có thể cho ý kiến ​​về cách thực hiện điều này một mình trong Spring thay vì Boot?
schoon

28

setAllowUrlEncodedSlash(true)không hiệu quả với tôi. Vẫn isNormalizedtrả về phương thức nội bộ falsekhi có dấu gạch chéo kép.

Tôi đã thay thế StrictHttpFirewallbằng DefaultHttpFirewallbằng cách chỉ có mã sau:

@Bean
public HttpFirewall defaultHttpFirewall() {
    return new DefaultHttpFirewall();
}

Làm việc tốt cho tôi.
Bất kỳ rủi ro bằng cách sử dụng DefaultHttpFirewall?


1
Đúng. Chỉ vì bạn không thể tạo chìa khóa dự phòng cho bạn cùng phòng, không có nghĩa là bạn nên đặt chìa khóa duy nhất dưới thảm chùi chân. Không được khuyên. Bảo mật không nên thay đổi.
java_dude

16
@java_dude Tuyệt vời vì bạn không cung cấp thông tin hay lý do nào cả, chỉ là một phép loại suy mơ hồ.
kaqqao

Một tùy chọn khác là phân lớp StrictHttpFirewallđể kiểm soát nhiều hơn một chút đối với việc từ chối URL, như được nêu chi tiết trong câu trả lời này .
vallismortis

1
Điều này làm việc cho tôi nhưng tôi cũng đã có thêm này trong XML đậu của tôi:<sec:http-firewall ref="defaultHttpFirewall"/>
Jason Winnebeck

1
Ý nghĩa của việc sử dụng giải pháp này là gì?
Felipe Desiderati

10

Tôi gặp phải vấn đề tương tự với:

Phiên bản khởi động mùa xuân = 1.5.10
Phiên bản bảo mật mùa xuân = 4.2.4


Sự cố xảy ra trên các điểm cuối, nơi mà ModelAndViewviewName được xác định bằng dấu gạch chéo trước . Thí dụ:

ModelAndView mav = new ModelAndView("/your-view-here");

Nếu tôi loại bỏ dấu gạch chéo, nó hoạt động tốt. Thí dụ:

ModelAndView mav = new ModelAndView("your-view-here");

Tôi cũng đã thực hiện một số thử nghiệm với RedirectView và nó dường như hoạt động với dấu gạch chéo trước.


2
Đó không phải là giải pháp. Điều gì sẽ xảy ra nếu đây là một lỗi ở phía Spring. Nếu họ thay đổi nó, thì bạn sẽ phải hoàn tác lại tất cả các thay đổi. Tôi muốn đợi cho đến khi 5.1 được đánh dấu để được giải quyết vào lúc đó.
java_dude

1
Không, bạn không phải hoàn nguyên thay đổi vì việc xác định viewName mà không có dấu gạch chéo trước hoạt động tốt trên các phiên bản cũ hơn.
Torsten Ojaperv

Đó chính xác là những gì vấn đề là. Nếu nó hoạt động tốt và bạn không thay đổi bất cứ điều gì thì Spring đã đưa ra một lỗi. Đường dẫn phải luôn bắt đầu bằng "/". Kiểm tra bất kỳ tài liệu mùa xuân nào. Hãy xem github.com/spring-projects/spring-security/issues/5007 & github.com/spring-projects/spring-security/issues/5044
java_dude Ngày

1
Điều này làm tôi cũng cắn. Cập nhật tất cả các ModelAndView mà không có sự lãnh đạo '/' cố định vấn đề
Nathan Perrier

jira.spring.io/browse/SPR-16740 Tôi đã mở một lỗi, nhưng việc xóa hàng đầu / không phải là cách khắc phục đối với tôi và trong hầu hết các trường hợp, chúng tôi chỉ trả lại tên chế độ xem dưới dạng chuỗi (từ bộ điều khiển) . Cần xem chuyển hướng xem như một giải pháp.
xenoterracide

6

Khi tôi sử dụng dấu gạch chéo kép trong khi gọi API thì tôi gặp lỗi tương tự.

Tôi đã phải gọi http: // localhost: 8080 / getSomething nhưng tôi đã Thích http: // localhost: 8080 // getSomething . Tôi đã giải quyết nó bằng cách loại bỏ dấu gạch chéo thừa.


chúng tôi có thể viết một số xử lý ngoại lệ cho điều này để chúng tôi có thể thông báo cho khách hàng về việc nhập sai của họ không?
YouAreAwesome,

4

Trong trường hợp của tôi, nâng cấp từ mùa xuân-securiy-web 3.1.3 đến 4.2.12, các defaultHttpFirewallđược thay đổi từ DefaultHttpFirewallđể StrictHttpFirewalltheo mặc định. Vì vậy, chỉ cần xác định nó trong cấu hình XML như dưới đây:

<bean id="defaultHttpFirewall" class="org.springframework.security.web.firewall.DefaultHttpFirewall"/>
<sec:http-firewall ref="defaultHttpFirewall"/>

đặt HTTPFirewallnhưDefaultHttpFirewall


1
Vui lòng thêm một số mô tả vào mã của bạn giải thích điều gì đang xảy ra và tại sao. Đây là thực hành tốt. Nếu không, câu trả lời của bạn có nguy cơ bị xóa. Nó đã được gắn cờ là chất lượng thấp.
herrbischoff

3

Giải pháp dưới đây là một giải pháp hoàn hảo, nó không ảnh hưởng đến bảo mật vì chúng tôi đang sử dụng cùng một tường lửa nghiêm ngặt.

Các bước để khắc phục như sau:

BƯỚC 1: Tạo lớp ghi đè lên Tường lửa nghiêm ngặt của lớp như bên dưới.

package com.biz.brains.project.security.firewall;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpMethod;
import org.springframework.security.web.firewall.DefaultHttpFirewall;
import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.RequestRejectedException;

public class CustomStrictHttpFirewall implements HttpFirewall {
    private static final Set<String> ALLOW_ANY_HTTP_METHOD = Collections.unmodifiableSet(Collections.emptySet());

    private static final String ENCODED_PERCENT = "%25";

    private static final String PERCENT = "%";

    private static final List<String> FORBIDDEN_ENCODED_PERIOD = Collections.unmodifiableList(Arrays.asList("%2e", "%2E"));

    private static final List<String> FORBIDDEN_SEMICOLON = Collections.unmodifiableList(Arrays.asList(";", "%3b", "%3B"));

    private static final List<String> FORBIDDEN_FORWARDSLASH = Collections.unmodifiableList(Arrays.asList("%2f", "%2F"));

    private static final List<String> FORBIDDEN_BACKSLASH = Collections.unmodifiableList(Arrays.asList("\\", "%5c", "%5C"));

    private Set<String> encodedUrlBlacklist = new HashSet<String>();

    private Set<String> decodedUrlBlacklist = new HashSet<String>();

    private Set<String> allowedHttpMethods = createDefaultAllowedHttpMethods();

    public CustomStrictHttpFirewall() {
        urlBlacklistsAddAll(FORBIDDEN_SEMICOLON);
        urlBlacklistsAddAll(FORBIDDEN_FORWARDSLASH);
        urlBlacklistsAddAll(FORBIDDEN_BACKSLASH);

        this.encodedUrlBlacklist.add(ENCODED_PERCENT);
        this.encodedUrlBlacklist.addAll(FORBIDDEN_ENCODED_PERIOD);
        this.decodedUrlBlacklist.add(PERCENT);
    }

    public void setUnsafeAllowAnyHttpMethod(boolean unsafeAllowAnyHttpMethod) {
        this.allowedHttpMethods = unsafeAllowAnyHttpMethod ? ALLOW_ANY_HTTP_METHOD : createDefaultAllowedHttpMethods();
    }

    public void setAllowedHttpMethods(Collection<String> allowedHttpMethods) {
        if (allowedHttpMethods == null) {
            throw new IllegalArgumentException("allowedHttpMethods cannot be null");
        }
        if (allowedHttpMethods == ALLOW_ANY_HTTP_METHOD) {
            this.allowedHttpMethods = ALLOW_ANY_HTTP_METHOD;
        } else {
            this.allowedHttpMethods = new HashSet<>(allowedHttpMethods);
        }
    }

    public void setAllowSemicolon(boolean allowSemicolon) {
        if (allowSemicolon) {
            urlBlacklistsRemoveAll(FORBIDDEN_SEMICOLON);
        } else {
            urlBlacklistsAddAll(FORBIDDEN_SEMICOLON);
        }
    }

    public void setAllowUrlEncodedSlash(boolean allowUrlEncodedSlash) {
        if (allowUrlEncodedSlash) {
            urlBlacklistsRemoveAll(FORBIDDEN_FORWARDSLASH);
        } else {
            urlBlacklistsAddAll(FORBIDDEN_FORWARDSLASH);
        }
    }

    public void setAllowUrlEncodedPeriod(boolean allowUrlEncodedPeriod) {
        if (allowUrlEncodedPeriod) {
            this.encodedUrlBlacklist.removeAll(FORBIDDEN_ENCODED_PERIOD);
        } else {
            this.encodedUrlBlacklist.addAll(FORBIDDEN_ENCODED_PERIOD);
        }
    }

    public void setAllowBackSlash(boolean allowBackSlash) {
        if (allowBackSlash) {
            urlBlacklistsRemoveAll(FORBIDDEN_BACKSLASH);
        } else {
            urlBlacklistsAddAll(FORBIDDEN_BACKSLASH);
        }
    }

    public void setAllowUrlEncodedPercent(boolean allowUrlEncodedPercent) {
        if (allowUrlEncodedPercent) {
            this.encodedUrlBlacklist.remove(ENCODED_PERCENT);
            this.decodedUrlBlacklist.remove(PERCENT);
        } else {
            this.encodedUrlBlacklist.add(ENCODED_PERCENT);
            this.decodedUrlBlacklist.add(PERCENT);
        }
    }

    private void urlBlacklistsAddAll(Collection<String> values) {
        this.encodedUrlBlacklist.addAll(values);
        this.decodedUrlBlacklist.addAll(values);
    }

    private void urlBlacklistsRemoveAll(Collection<String> values) {
        this.encodedUrlBlacklist.removeAll(values);
        this.decodedUrlBlacklist.removeAll(values);
    }

    @Override
    public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
        rejectForbiddenHttpMethod(request);
        rejectedBlacklistedUrls(request);

        if (!isNormalized(request)) {
            request.setAttribute("isNormalized", new RequestRejectedException("The request was rejected because the URL was not normalized."));
        }

        String requestUri = request.getRequestURI();
        if (!containsOnlyPrintableAsciiCharacters(requestUri)) {
            request.setAttribute("isNormalized",  new RequestRejectedException("The requestURI was rejected because it can only contain printable ASCII characters."));
        }
        return new FirewalledRequest(request) {
            @Override
            public void reset() {
            }
        };
    }

    private void rejectForbiddenHttpMethod(HttpServletRequest request) {
        if (this.allowedHttpMethods == ALLOW_ANY_HTTP_METHOD) {
            return;
        }
        if (!this.allowedHttpMethods.contains(request.getMethod())) {
            request.setAttribute("isNormalized",  new RequestRejectedException("The request was rejected because the HTTP method \"" +
                    request.getMethod() +
                    "\" was not included within the whitelist " +
                    this.allowedHttpMethods));
        }
    }

    private void rejectedBlacklistedUrls(HttpServletRequest request) {
        for (String forbidden : this.encodedUrlBlacklist) {
            if (encodedUrlContains(request, forbidden)) {
                request.setAttribute("isNormalized",  new RequestRejectedException("The request was rejected because the URL contained a potentially malicious String \"" + forbidden + "\""));
            }
        }
        for (String forbidden : this.decodedUrlBlacklist) {
            if (decodedUrlContains(request, forbidden)) {
                request.setAttribute("isNormalized",  new RequestRejectedException("The request was rejected because the URL contained a potentially malicious String \"" + forbidden + "\""));
            }
        }
    }

    @Override
    public HttpServletResponse getFirewalledResponse(HttpServletResponse response) {
        return new FirewalledResponse(response);
    }

    private static Set<String> createDefaultAllowedHttpMethods() {
        Set<String> result = new HashSet<>();
        result.add(HttpMethod.DELETE.name());
        result.add(HttpMethod.GET.name());
        result.add(HttpMethod.HEAD.name());
        result.add(HttpMethod.OPTIONS.name());
        result.add(HttpMethod.PATCH.name());
        result.add(HttpMethod.POST.name());
        result.add(HttpMethod.PUT.name());
        return result;
    }

    private static boolean isNormalized(HttpServletRequest request) {
        if (!isNormalized(request.getRequestURI())) {
            return false;
        }
        if (!isNormalized(request.getContextPath())) {
            return false;
        }
        if (!isNormalized(request.getServletPath())) {
            return false;
        }
        if (!isNormalized(request.getPathInfo())) {
            return false;
        }
        return true;
    }

    private static boolean encodedUrlContains(HttpServletRequest request, String value) {
        if (valueContains(request.getContextPath(), value)) {
            return true;
        }
        return valueContains(request.getRequestURI(), value);
    }

    private static boolean decodedUrlContains(HttpServletRequest request, String value) {
        if (valueContains(request.getServletPath(), value)) {
            return true;
        }
        if (valueContains(request.getPathInfo(), value)) {
            return true;
        }
        return false;
    }

    private static boolean containsOnlyPrintableAsciiCharacters(String uri) {
        int length = uri.length();
        for (int i = 0; i < length; i++) {
            char c = uri.charAt(i);
            if (c < '\u0020' || c > '\u007e') {
                return false;
            }
        }

        return true;
    }

    private static boolean valueContains(String value, String contains) {
        return value != null && value.contains(contains);
    }

    private static boolean isNormalized(String path) {
        if (path == null) {
            return true;
        }

        if (path.indexOf("//") > -1) {
            return false;
        }

        for (int j = path.length(); j > 0;) {
            int i = path.lastIndexOf('/', j - 1);
            int gap = j - i;

            if (gap == 2 && path.charAt(i + 1) == '.') {
                // ".", "/./" or "/."
                return false;
            } else if (gap == 3 && path.charAt(i + 1) == '.' && path.charAt(i + 2) == '.') {
                return false;
            }

            j = i;
        }

        return true;
    }

}

BƯỚC 2: Tạo một lớp FirewalledResponse

package com.biz.brains.project.security.firewall;

import java.io.IOException;
import java.util.regex.Pattern;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

class FirewalledResponse extends HttpServletResponseWrapper {
    private static final Pattern CR_OR_LF = Pattern.compile("\\r|\\n");
    private static final String LOCATION_HEADER = "Location";
    private static final String SET_COOKIE_HEADER = "Set-Cookie";

    public FirewalledResponse(HttpServletResponse response) {
        super(response);
    }

    @Override
    public void sendRedirect(String location) throws IOException {
        // TODO: implement pluggable validation, instead of simple blacklisting.
        // SEC-1790. Prevent redirects containing CRLF
        validateCrlf(LOCATION_HEADER, location);
        super.sendRedirect(location);
    }

    @Override
    public void setHeader(String name, String value) {
        validateCrlf(name, value);
        super.setHeader(name, value);
    }

    @Override
    public void addHeader(String name, String value) {
        validateCrlf(name, value);
        super.addHeader(name, value);
    }

    @Override
    public void addCookie(Cookie cookie) {
        if (cookie != null) {
            validateCrlf(SET_COOKIE_HEADER, cookie.getName());
            validateCrlf(SET_COOKIE_HEADER, cookie.getValue());
            validateCrlf(SET_COOKIE_HEADER, cookie.getPath());
            validateCrlf(SET_COOKIE_HEADER, cookie.getDomain());
            validateCrlf(SET_COOKIE_HEADER, cookie.getComment());
        }
        super.addCookie(cookie);
    }

    void validateCrlf(String name, String value) {
        if (hasCrlf(name) || hasCrlf(value)) {
            throw new IllegalArgumentException(
                    "Invalid characters (CR/LF) in header " + name);
        }
    }

    private boolean hasCrlf(String value) {
        return value != null && CR_OR_LF.matcher(value).find();
    }
}

BƯỚC 3: Tạo Bộ lọc tùy chỉnh để loại bỏ Ngoại lệ bị từ chối

package com.biz.brains.project.security.filter;

import java.io.IOException;
import java.util.Objects;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.security.web.firewall.RequestRejectedException;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RequestRejectedExceptionFilter extends GenericFilterBean {

        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            try {
                RequestRejectedException requestRejectedException=(RequestRejectedException) servletRequest.getAttribute("isNormalized");
                if(Objects.nonNull(requestRejectedException)) {
                    throw requestRejectedException;
                }else {
                    filterChain.doFilter(servletRequest, servletResponse);
                }
            } catch (RequestRejectedException requestRejectedException) {
                HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
                HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
                log
                    .error(
                            "request_rejected: remote={}, user_agent={}, request_url={}",
                            httpServletRequest.getRemoteHost(),  
                            httpServletRequest.getHeader(HttpHeaders.USER_AGENT),
                            httpServletRequest.getRequestURL(), 
                            requestRejectedException
                    );

                httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
            }
        }
}

BƯỚC 4: Thêm bộ lọc tùy chỉnh vào chuỗi bộ lọc mùa xuân trong cấu hình bảo mật

@Override
protected void configure(HttpSecurity http) throws Exception {
     http.addFilterBefore(new RequestRejectedExceptionFilter(),
             ChannelProcessingFilter.class);
}

Bây giờ bằng cách sử dụng bản sửa lỗi ở trên, chúng tôi có thể xử lý RequestRejectedExceptionvới trang Lỗi 404.


Cảm ơn bạn. Đây là cách tiếp cận mà tôi tạm thời sử dụng để cho phép chúng tôi nâng cấp dịch vụ vi mô Java của mình cho đến khi tất cả các ứng dụng front-end đều được nâng cấp. Tôi không cần bước 3 và 4 để cho phép '//' được coi là chuẩn hóa thành công. Tôi vừa nhận xét điều kiện đã kiểm tra dấu gạch chéo kép trong isNormalized và sau đó định cấu hình bean để sử dụng lớp CustomStrictHttpFirewall thay thế.
gtaborga

Có cách giải quyết khác dễ dàng hơn thông qua cấu hình không? Nhưng mà không cần tắt tường lửa ..
Prathamesh dhanawade

0

Trong trường hợp của tôi, sự cố là do không đăng nhập được bằng Postman, vì vậy tôi đã mở một kết nối trong một tab khác với cookie phiên mà tôi lấy từ các tiêu đề trong phiên Chrome của mình.

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.