Skip to content

Commit 09f2302

Browse files
montymxbflovilmart
authored andcommitted
Scrub Passwords with URL Encoded Characters (#4433)
* scrub passwords with url encoded characters from logs * compose query string from parsed params, redacting based on key if needed
1 parent 5521d41 commit 09f2302

File tree

2 files changed

+107
-5
lines changed

2 files changed

+107
-5
lines changed

spec/LogsRouter.spec.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,93 @@ describe('LogsRouter', () => {
6262
done();
6363
});
6464
});
65+
66+
const headers = {
67+
'X-Parse-Application-Id': 'test',
68+
'X-Parse-REST-API-Key': 'rest',
69+
'X-Parse-Master-Key': 'test'
70+
};
71+
72+
/**
73+
* Verifies simple passwords in GET login requests with special characters are scrubbed from the verbose log
74+
*/
75+
it('does scrub simple passwords on GET login', done => {
76+
reconfigureServer({
77+
verbose: true
78+
}).then(function() {
79+
request.get({
80+
headers: headers,
81+
url: 'http://localhost:8378/1/login?username=test&password=simplepass.com'
82+
}, () => {
83+
request.get({
84+
url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose',
85+
json: true,
86+
headers: headers
87+
}, (error, response, body) => {
88+
expect(response.statusCode).toEqual(200);
89+
// 4th entry is our actual GET request
90+
expect(body[3].url).toEqual('/1/login?username=test&password=********');
91+
expect(body[3].message).toEqual('REQUEST for [GET] /1/login?username=test&password=********: {}');
92+
done();
93+
});
94+
});
95+
});
96+
});
97+
98+
/**
99+
* Verifies complex passwords in GET login requests with special characters are scrubbed from the verbose log
100+
*/
101+
it('does scrub complex passwords on GET login', done => {
102+
reconfigureServer({
103+
verbose: true
104+
}).then(function() {
105+
request.get({
106+
headers: headers,
107+
// using urlencoded password, 'simple @,/?:&=+$#pass.com'
108+
url: 'http://localhost:8378/1/login?username=test&password=simple%20%40%2C%2F%3F%3A%26%3D%2B%24%23pass.com'
109+
}, () => {
110+
request.get({
111+
url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose',
112+
json: true,
113+
headers: headers
114+
}, (error, response, body) => {
115+
expect(response.statusCode).toEqual(200);
116+
// 4th entry is our actual GET request
117+
expect(body[3].url).toEqual('/1/login?username=test&password=********');
118+
expect(body[3].message).toEqual('REQUEST for [GET] /1/login?username=test&password=********: {}');
119+
done();
120+
});
121+
});
122+
});
123+
});
124+
125+
/**
126+
* Verifies fields in POST login requests are NOT present in the verbose log
127+
*/
128+
it('does not have password field in POST login', done => {
129+
reconfigureServer({
130+
verbose: true
131+
}).then(function() {
132+
request.post({
133+
headers: headers,
134+
url: 'http://localhost:8378/1/login',
135+
data: {
136+
username: 'test',
137+
password: 'simplepass.com'
138+
}
139+
}, () => {
140+
request.get({
141+
url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose',
142+
json: true,
143+
headers: headers
144+
}, (error, response, body) => {
145+
expect(response.statusCode).toEqual(200);
146+
// 4th entry is our actual GET request
147+
expect(body[3].url).toEqual('/1/login');
148+
expect(body[3].message).toEqual('REQUEST for [POST] /1/login: {}');
149+
done();
150+
});
151+
});
152+
});
153+
});
65154
});

src/Controllers/LoggerController.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,25 @@ export class LoggerController extends AdaptableController {
4646
}
4747

4848
maskSensitiveUrl(urlString) {
49-
const password = url.parse(urlString, true).query.password;
50-
51-
if (password) {
52-
urlString = urlString.replace('password=' + password, 'password=********');
49+
const urlObj = url.parse(urlString, true);
50+
const query = urlObj.query;
51+
let sanitizedQuery = '?';
52+
53+
for(const key in query) {
54+
if(key !== 'password') {
55+
// normal value
56+
sanitizedQuery += key + '=' + query[key] + '&';
57+
} else {
58+
// password value, redact it
59+
sanitizedQuery += key + '=' + '********' + '&';
60+
}
5361
}
54-
return urlString;
62+
63+
// trim last character, ? or &
64+
sanitizedQuery = sanitizedQuery.slice(0, -1);
65+
66+
// return original path name with sanitized params attached
67+
return urlObj.pathname + sanitizedQuery;
5568
}
5669

5770
maskSensitive(argArray) {

0 commit comments

Comments
 (0)