-
-
Notifications
You must be signed in to change notification settings - Fork 22.8k
Sanitize sensitive fields in request header and body in logger #5279
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,8 @@ PORT=3000 | |
# DEBUG=true | ||
# LOG_PATH=/your_log_path/.flowise/logs | ||
# LOG_LEVEL=info #(error | warn | info | verbose | debug) | ||
LOG_SANITIZE_BODY_FIELDS=password,pwd,pass,secret,token,apikey,api_key,accesstoken,access_token,refreshtoken,refresh_token,clientsecret,client_secret,privatekey,private_key,secretkey,secret_key,auth,authorization,credential,credentials | ||
LOG_SANITIZE_HEADER_FIELDS=authorization,x-api-key,x-auth-token,cookie | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How this will be deployed in the Flowise cloud? |
||
# TOOL_FUNCTION_BUILTIN_DEP=crypto,fs | ||
# TOOL_FUNCTION_EXTERNAL_DEP=moment,lodash | ||
# ALLOW_BUILTIN_DEP=false | ||
|
@@ -180,4 +182,4 @@ JWT_REFRESH_TOKEN_EXPIRY_IN_MINUTES=43200 | |
############################################################################################################ | ||
|
||
# PUPPETEER_EXECUTABLE_FILE_PATH='C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' | ||
# PLAYWRIGHT_EXECUTABLE_FILE_PATH='C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' | ||
# PLAYWRIGHT_EXECUTABLE_FILE_PATH='C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -193,25 +193,63 @@ requestLogger = createLogger({ | |
] | ||
}) | ||
|
||
function getSensitiveBodyFields(): string[] { | ||
return (process.env.LOG_SANITIZE_BODY_FIELDS as string) | ||
.toLowerCase() | ||
.split(',') | ||
.map((f) => f.trim()) | ||
} | ||
|
||
function getSensitiveHeaderFields(): string[] { | ||
return (process.env.LOG_SANITIZE_HEADER_FIELDS as string) | ||
.toLowerCase() | ||
.split(',') | ||
.map((f) => f.trim()) | ||
} | ||
|
||
function sanitizeObject(obj: any): any { | ||
if (!obj || typeof obj !== 'object') return obj | ||
|
||
const sensitiveFields = getSensitiveBodyFields() | ||
const sanitized = Array.isArray(obj) ? [...obj] : { ...obj } | ||
Object.keys(sanitized).forEach((key) => { | ||
const lowerKey = key.toLowerCase() | ||
if (sensitiveFields.includes(lowerKey)) { | ||
sanitized[key] = '********' | ||
} else if (typeof sanitized[key] === 'string') { | ||
if (sanitized[key].includes('@') && sanitized[key].includes('.')) { | ||
sanitized[key] = sanitized[key].replace(/([^@\s]+)@([^@\s]+)/g, '**********') | ||
} | ||
} | ||
}) | ||
|
||
return sanitized | ||
} | ||
|
||
export function expressRequestLogger(req: Request, res: Response, next: NextFunction): void { | ||
const unwantedLogURLs = ['/api/v1/node-icon/', '/api/v1/components-credentials-icon/', '/api/v1/ping'] | ||
|
||
if (/\/api\/v1\//i.test(req.url) && !unwantedLogURLs.some((url) => new RegExp(url, 'i').test(req.url))) { | ||
// Create a sanitized copy of the request body | ||
const sanitizedBody = { ...req.body } | ||
if (sanitizedBody.password) { | ||
sanitizedBody.password = '********' | ||
} | ||
const sanitizedBody = sanitizeObject(req.body) | ||
|
||
const sanitizedQuery = sanitizeObject(req.query) | ||
const sanitizedHeaders = { ...req.headers } | ||
|
||
const sensitiveHeaders = getSensitiveHeaderFields() | ||
sensitiveHeaders.forEach((header) => { | ||
if (sanitizedHeaders[header]) { | ||
sanitizedHeaders[header] = '********' | ||
} | ||
}) | ||
|
||
// Use the shared requestLogger with request-specific metadata | ||
const requestMetadata = { | ||
request: { | ||
method: req.method, | ||
url: req.url, | ||
body: sanitizedBody, // Use sanitized body instead of raw body | ||
query: req.query, | ||
body: sanitizedBody, | ||
query: sanitizedQuery, | ||
params: req.params, | ||
headers: req.headers | ||
headers: sanitizedHeaders | ||
} | ||
} | ||
Comment on lines
245
to
254
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even with redaction, logging HTTP request bodies, params and query strings is generally a bad idea because it significantly increases the risk of accidental exposure of sensitive data due to misconfigurations, broken redaction logic, or logs being improperly secured. It also creates a massive compliance burden under regulations like GDPR and CCPA, as these logs can inadvertently capture Personally Identifiable Information that must then be managed and defensibly deleted. Recommend either removing body, params & querystring from here or putting them behind a log level so they're only captured when debug logs are enabled. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. btw, apologies if this changes the requirement from what's on the Jira. The Jira's wording was based on the documented findings. Now that I'm seeing the code and not just the finding, I think the finding's recommendation was not in line with how Workday generally treats areas where sensitive data could slip in. Omitting these specific bits on production request logging is standard practice. |
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did you compile this list? How to validate is there any missing field?