Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update CSRF token handling #179

Merged
merged 2 commits into from
Nov 1, 2023
Merged

Update CSRF token handling #179

merged 2 commits into from
Nov 1, 2023

Conversation

radito3
Copy link
Contributor

@radito3 radito3 commented Oct 31, 2023

Change the csrf token handling flow.
Keep a timestamp of the last token update time and fetch a new one if the csrf session has expired before sending a request.

fetchTokenRequest, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
return csrf_parameters.CsrfParams{}, err
}
fetchTokenRequest.Header.Set(XCsrfToken, CsrfTokenHeaderFetchValue)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an old code, but I do not think this "Fetch" value is required on the backend


const CookieHeader = "Cookie"

type Csrf struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

csrf is not very descriptive, maybe rename to
CsrfTokenHelper
CsrfTokenUpdater

return nil
}
c.transport.Csrf.Header, c.transport.Csrf.Token = csrfToken.CsrfTokenHeader, csrfToken.CsrfTokenValue
if c.csrf.IsExpired(CsrfSessionTimeout) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is brittle.
Yes, the session timeout is 2 minutes and the token is per session, but there is some time lost during routing, server processing of the returned token, etc. There might be a case when the token has less than 2 minutes of expiration left.
I'd say 1:30 minutes max. We've seen some fetch token requests that took 10 seconds and nobody knows where exactly was the delay and when the token/session was created

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The less the safer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reduced to 1m30s

Comment on lines 18 to 26
csrfTokenManager := NewDefaultCsrfTokenManager(t)
err := csrfTokenManager.updateToken(reqCopy)
if err != nil {
return nil, err
}

log.Tracef("Sending a request with CSRF '%s : %s'\n", reqCopy.Header.Get("X-Csrf-Header"), reqCopy.Header.Get("X-Csrf-Token"))
log.Tracef("Sending a request with CSRF %s\n", reqCopy.Header.Get("X-Csrf-Token"))
log.Tracef("Cookies used are: %s\n", prettyPrintCookies(reqCopy.Cookies()))
res, err := t.OriginalTransport.RoundTrip(reqCopy)
if err != nil {
return nil, err
}
tokenWasRefreshed, err := csrfTokenManager.refreshTokenIfNeeded(res)
if err != nil {
return nil, err
}

if tokenWasRefreshed {
log.Tracef("Response code '%d' from bad token. Must Retry.\n", res.StatusCode)
return nil, &ForbiddenError{}
}
return res, err
return t.Delegate.RoundTrip(reqCopy)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this code handled the case when DS instance is restarted and the token and session are lost.
Now this case is not handled.
The DS will return a header:
"X-CSRF-TOKEN: Required", when Invalid/Missing Csrf exception occurs. We can check this header and re-init the token and re-execute the request.

@radito3 radito3 merged commit 4760f60 into cloudfoundry:master Nov 1, 2023
4 checks passed
h := make(textproto.MIMEHeader)
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, escapeQuotes(fileName)))
h.Set("Content-Type", "application/octet-stream")
h.Set("Content-Length", strconv.FormatInt(fileSize, 10))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will be difference when Content-Legth is set?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If Tomcat was smart, it would read this length and set it in the Part object as its size, instead of persisting the entire file to disk.

Comment on lines +277 to +283
h := make(textproto.MIMEHeader)
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, escapeQuotes(file.Name())))
h.Set("Content-Type", "application/octet-stream")
h.Set("Content-Length", strconv.FormatInt(fileSize, 10))
fileWriter, err := form.CreatePart(h)
if err != nil {
return fmt.Errorf("could not write to HTTP request: %v", err)
return fmt.Errorf("could not create multipart file part: %v", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be split into a new method and reused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants