handleAPIKeySmuggling is called by the proxy path and subdomain handlers to process any "smuggled" API keys in the query parameters. If a smuggled key is found, it is decrypted and the cookie is set, and the user is redirected to strip the query parameter.
(rw http.ResponseWriter, r *http.Request, accessMethod AccessMethod)
| 171 | // If a smuggled key is found, it is decrypted and the cookie is set, and the |
| 172 | // user is redirected to strip the query parameter. |
| 173 | func (s *Server) handleAPIKeySmuggling(rw http.ResponseWriter, r *http.Request, accessMethod AccessMethod) bool { |
| 174 | ctx := r.Context() |
| 175 | |
| 176 | encryptedAPIKey := r.URL.Query().Get(SubdomainProxyAPIKeyParam) |
| 177 | if encryptedAPIKey == "" { |
| 178 | return true |
| 179 | } |
| 180 | |
| 181 | // API key smuggling is not permitted for path apps on the primary access |
| 182 | // URL. The user is already covered by their full session token. |
| 183 | if accessMethod == AccessMethodPath && s.AccessURL.Host == s.DashboardURL.Host { |
| 184 | site.RenderStaticErrorPage(rw, r, site.ErrorPageData{ |
| 185 | Status: http.StatusBadRequest, |
| 186 | Title: "Bad Request", |
| 187 | Description: "Could not decrypt API key. Workspace app API key smuggling is not permitted on the primary access URL. Please remove the query parameter and try again.", |
| 188 | // No retry is included because the user needs to remove the query |
| 189 | // parameter before they try again. |
| 190 | Actions: []site.Action{ |
| 191 | { |
| 192 | URL: s.DashboardURL.String(), |
| 193 | Text: "Back to site", |
| 194 | }, |
| 195 | }, |
| 196 | }) |
| 197 | return false |
| 198 | } |
| 199 | |
| 200 | // Exchange the encoded API key for a real one. |
| 201 | var payload EncryptedAPIKeyPayload |
| 202 | err := jwtutils.Decrypt(ctx, s.APIKeyEncryptionKeycache, encryptedAPIKey, &payload, jwtutils.WithDecryptExpected(jwt.Expected{ |
| 203 | Time: time.Now(), |
| 204 | })) |
| 205 | if err != nil { |
| 206 | s.Logger.Debug(ctx, "could not decrypt smuggled workspace app API key", slog.Error(err)) |
| 207 | site.RenderStaticErrorPage(rw, r, site.ErrorPageData{ |
| 208 | Status: http.StatusBadRequest, |
| 209 | Title: "Bad Request", |
| 210 | Description: "Could not decrypt API key. Please remove the query parameter and try again.", |
| 211 | // No retry is included because the user needs to remove the query |
| 212 | // parameter before they try again. |
| 213 | Actions: []site.Action{ |
| 214 | { |
| 215 | URL: s.DashboardURL.String(), |
| 216 | Text: "Back to site", |
| 217 | }, |
| 218 | }, |
| 219 | }) |
| 220 | return false |
| 221 | } |
| 222 | |
| 223 | // Set the cookie. For subdomain apps, we set the cookie on the whole |
| 224 | // wildcard so users don't need to re-auth for every subdomain app they |
| 225 | // access. For path apps (only on proxies, see above) we just set it on the |
| 226 | // current domain. |
| 227 | domain := "" // use the current domain |
| 228 | if accessMethod == AccessMethodSubdomain { |
| 229 | hostSplit := strings.SplitN(s.Hostname, ".", 2) |
| 230 | if len(hostSplit) != 2 { |
no test coverage detected