Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
68 views
in Technique[技术] by (71.8m points)

How to use key instead of `Set-Cookie` in Response Headers with Spring Security?

I am having difficulty to store cookies in a React Native apps.

My goal is to send the JSESSIONID and XSRF-TOKEN as Response Header's keys instead of Set-Cookie, and the client will handle to store it as cookie manually.

  1. I will store the JSESSIONID as cookie with HttpOnly set to true.
  2. I will store the XSRF-TOKEN as cookie with HttpOnly set to false.
question from:https://stackoverflow.com/questions/65885432/how-to-use-key-instead-of-set-cookie-in-response-headers-with-spring-security

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Basic Security Concepts:

  • 1. Session Cookie (JSESSIONID for example).

Should always be HttpOnly, have a domain set (or use the default as the server that provided it). Never be stored anywhere other than the browser handling it. Your JS/HTML should know nothing about the JSESSION Cookie and only just move the user to a login screen if they get a 401 (UnAuthorised) from an endpoint.

  • 2. CSRF Tokens.

Back in the day (5 years a go probably ha!), most sites were rendered on the server. The server created the HTML and then just sent it back via the URI. Like you went to /profile then the server knew who you were and then created the profile page on its server and just fed back the HTML document.

When wanting to get some user input, this HTML rendered by the server would contain a <form/> which would collect user's data (password/bank details etc.) and then with the onSubmit passes it back to the server in a application/x-www-form-urlencoded format

eg. https://thewebsite.com/sendmoney_to?account=512&amount=1milliondollars

By simply sending that link to someone who has an active session with the site thewebsite.com the browser would visit it and carry out the request.

The victim is logged in as far as the site is concerned and will happily run that request sent by the attacker. These links were at one point even follower by simply loading an <img> like by posting on their wall or in an email etc.

So how did they fix this?

By adding some fields to the form called hidden fields. These hidden fields are created by the server when the page is rendered. They contain a value that is the CSRF TOKEN and also something in a CSRF COOKIE. So when the application/x-www-form-urlencoded form is sent, it must have these values produced on the server when rendering the form. The server can then verify the form was the one they created and not some malicious link an attacker created. An attacker can not know/guess these when making their naughty link.

  • 3. Nower Days

With having only JSON requests as many sites, like React Apps, are rendered client side and use Axios/Fetch...CSRF is somewhat redundant. You don't post forms/application/x-www-form-urlencoded...only make POST requests with a JSON body.

Sessions are still important as an XSS attack > CSRF attack. Store the session properly (JWT token or not...don't jump on the JWT band wagon and start throwing that JWT Auth Token around Local Storage which seems to have become some kinda strange default for newer devs).

If you are sure you only have application/json capable endpoints, then the only way an attacker is going to get you to POST their content instead of what you are meant to is via an XSS attack. But once they have an XSS attack it is game over anyway. They are simply then you as far as the server is concerned as they use their naughty injected <script> to manipulate the request before it sent and the server would have no way of knowing it had been manipulated on the fly (usually).

  • 4. Getting your CSRF Token via a Header as it wont be made in the form

The XSRF-TOKEN you may need to expose the XSRF token by using a filter that will extract the CSRF Token (via the session) and adds it to a header on the response entity.

I wrote my own but it is basically the same in a library some use to do this same thing.


@Log4j2
public class CsrfBindingFilter extends OncePerRequestFilter {
    protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";
    protected static final String RESPONSE_HEADER_NAME = "X-CSRF-HEADER";
    protected static final String RESPONSE_PARAM_NAME = "X-CSRF-PARAM";
    protected static final String RESPONSE_TOKEN_NAME = "X-CSRF-TOKEN";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain filterChain) throws ServletException, IOException {
        CsrfToken csrfToken = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);
        log.debug("CRSF Token from session : {}", csrfToken != null ? csrfToken.getToken() : "CsrfToken is NULL");
        if (csrfToken != null) {
            response.setHeader(RESPONSE_HEADER_NAME, csrfToken.getHeaderName());
            response.setHeader(RESPONSE_PARAM_NAME, csrfToken.getParameterName());
            response.setHeader(RESPONSE_TOKEN_NAME, csrfToken.getToken());
        }

        filterChain.doFilter(request, response);
    }
}

As far as the session, the browser should handle that and you should not be messing with it client side unless it is for a very specific reason (and I can't fathom one). The Cookies for Sessions are set as HttpOnly for the specific reason to disallow any JS running in the client to edit/read/add it. One little cheeky advert/xss with some naughty code like get the cookie called JSESSION ID if the host is myvictim.com and post it over here... could mean you are compromised.

Read this for more detail: Add HttpOnly Cookie via JS The browser should handle the Set-Cookie headers as intended and is best practice (out of security reasons and also just for plain simple ease of use).

WebSocket STOMP Authentication

Once a user has been authenticated via your /login, Spring should send a Set-Cookie header with the JSESSIONID Cookie. This cookie will be stored by the browser and should be inaccessible to your front end javascript app.

If you then use the STOMP Spring WebSocket implementation, you can get the principal user name from a STOMP message by getting it via the StompHeaderAccessor in the @MessageMapping Controller params.

stompHeaderAccessor.getUser().getName() would give the Spring Security authenticated user's principal name (usually their username or id, username is default).


 @MessageMapping("/agents/start")
    public void start(StompHeaderAccessor stompHeaderAccessor) {
        log.info("Subscriber Start! {}-{}", stompHeaderAccessor.getUser() != null ? stompHeaderAccessor.getUser().getName() : "ANON", stompHeaderAccessor.getSessionId());
        mysessionstore.addSessionId(stompHeaderAccessor.getSessionId());
    }

If you then want to edit the user's session attributes, you will then need to fetch their session id from the SPRING_SESSION table and you can use the Spring SessionRepository to fetch it.

https://docs.spring.io/spring-session/docs/current/api/org/springframework/session/SessionRepository.html


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...