Skip to content

feat: Implement (but not enforce) CSRF for FE requests #3786

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

Merged
merged 29 commits into from
Sep 13, 2022
Merged

Conversation

Emyrk
Copy link
Member

@Emyrk Emyrk commented Aug 31, 2022

What this does

Implements CSRF in the frontend. All non get requests using cookie auth require CSRF header as well.

Allows using Coder-Session-Token header to bypass CSRF for cli auth.

fixes #3751

Future Work

Enforce CSRF. To be backwards compatible, we are delaying enforcing csrf. In 1/2 releases we can enforce it and force users to update to this version of the cli.

@Emyrk Emyrk requested a review from a team as a code owner August 31, 2022 21:13
@Emyrk Emyrk requested review from Kira-Pilot and removed request for a team August 31, 2022 21:13
Comment on lines +23 to +30
// Exempt all requests that do not require CSRF protection.
// All GET requests are exempt by default.
mw.ExemptPath("/api/v2/csp/reports")

// Top level agent routes.
mw.ExemptRegexp(regexp.MustCompile("api/v2/workspaceagents/[^/]*$"))
// Agent authenticated routes
mw.ExemptRegexp(regexp.MustCompile("api/v2/workspaceagents/me/*"))
Copy link
Member Author

Choose a reason for hiding this comment

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

Pretty sure this is all we need to exempt. If provisionerd needs to make api calls, we should omit that as well.

Copy link
Contributor

@jsjoeio jsjoeio left a comment

Choose a reason for hiding this comment

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

FE: requesting changes for the console.error message when token isn't found. At the bare minimum, we should provide a helpful error message. I don't think we should link to the laravel docs.

@jsjoeio
Copy link
Contributor

jsjoeio commented Sep 1, 2022

I'll see if I can pull this down and fix the FE errors.

@github-actions
Copy link

github-actions bot commented Sep 9, 2022

This Pull Request is becoming stale. In order to minimize WIP, prevent merge conflicts and keep the tracker readable, I'm going close to this PR in 3 days if there isn't more activity.

@github-actions github-actions bot added the stale This issue is like stale bread. label Sep 9, 2022
@jsjoeio
Copy link
Contributor

jsjoeio commented Sep 9, 2022

Chatted with @deansheather offline who said he can help with this

@jsjoeio jsjoeio removed the stale This issue is like stale bread. label Sep 9, 2022
}))

// Exempt all requests that do not require CSRF protection.
// All GET requests are exempt by default.
Copy link
Member

Choose a reason for hiding this comment

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

Is this not an issue? Can't we still send data with a GET?

Copy link
Member Author

Choose a reason for hiding this comment

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

CSRF really only needs to protect endpoints that have side effects. Our router checks the method on all API calls. See https://security.stackexchange.com/questions/115794/should-i-use-csrf-protection-for-get-requests.

If we design our api correctly, there is no need to enforce CSRF on GET requests.

Copy link
Contributor

@f0ssel f0ssel left a comment

Choose a reason for hiding this comment

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

great work as always steven

OAuth2StateKey = "oauth_state"
OAuth2RedirectKey = "oauth_redirect"
SessionTokenKey = "coder_session_token"
// SessionCustomHeader is the custom header to use for authentication.
Copy link
Contributor

Choose a reason for hiding this comment

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

What is a custom header?

Copy link
Member Author

Choose a reason for hiding this comment

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

The cli needs to bypass CSRF (it'd be dumb to use on the cli), so we use a custom header rather than a cookie. The old code required using a -H 'cooke:...' which is kinda odd.

In future we might want to use an Authorization Bearer token to be more standard?

Comment on lines +7 to +17
export const hardCodedCSRFCookie = (): string => {
// This is a hard coded CSRF token/cookie pair for local development.
// In prod, the GoLang webserver generates a random cookie with a new token for
// each document request. For local development, we don't use the Go webserver for static files,
// so this is the 'hack' to make local development work with remote apis.
// The CSRF cookie for this token is "JXm9hOUdZctWt0ZZGAy9xiS/gxMKYOThdxjjMnMUyn4="
const csrfToken =
"KNKvagCBEHZK7ihe2t7fj6VeJ0UyTDco1yVUJE8N06oNqxLu5Zx1vRxZbgfC0mJJgeGkVjgs08mgPbcWPBkZ1A=="
axios.defaults.headers.common["X-CSRF-TOKEN"] = csrfToken
return csrfToken
}
Copy link
Contributor

Choose a reason for hiding this comment

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

great docs on this 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

For local development, we don't use the Go webserver for static files

@Emyrk Dumb question...why don't we use the Go webserver for static files? What do we instead? (Just trying to understand the whole picture).

Copy link
Member Author

Choose a reason for hiding this comment

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

We do, but for the FE team to have a hot reload, we have to use webpack server instead to host the files. So the webpack server has to do what the golang server is doing.

Instead of implementing CSRF completely, we just hardcode it since it's only used for developement.

Copy link
Member

@Kira-Pilot Kira-Pilot left a comment

Choose a reason for hiding this comment

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

✅ FE working fine!

@Emyrk Emyrk enabled auto-merge (squash) September 13, 2022 16:11
@Emyrk Emyrk disabled auto-merge September 13, 2022 16:35
Copy link
Contributor

@jsjoeio jsjoeio left a comment

Choose a reason for hiding this comment

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

Nice work!

}
} else {
// Do not write error logs if we are in a FE unit test.
if (process.env.JEST_WORKER_ID === undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Bummer we have to do this because of Jest but I don't know a better way lol thanks for adding a comment!

Copy link
Member Author

Choose a reason for hiding this comment

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

It is unfortunate :/

@Emyrk Emyrk changed the title feat: Implement CSRF for FE requests feat: Implement (but not enforce) CSRF for FE requests Sep 13, 2022
@Emyrk Emyrk merged commit 9b5ee8f into main Sep 13, 2022
@Emyrk Emyrk deleted the stevenmasley/csrf branch September 13, 2022 19:26
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.

Uniquely name coder cookies that get stripped for applications
5 participants