Recently, in the cnode community, an article about XSS published by @ Wu Zhonghua directly led the community to initiate various attacks on cnode. Here we summarize some of the problems and solutions encountered this time.
File Upload Vulnerability
The logic for nodeclub to upload images is as follows:
- // File name uploaded by the user
- Var filename = Date. now () + '_' + file. name;
- // User folder
- Var userDir = path. join (config. upload_dir, uid );
- // Path for storing the final file
- Var savepath = path. join (userDir, filename );
- // Move the uploaded files from the temporary directory to the final save path
- Fs. rename (file. path, savepath, callback );
-
It seems that there is no problem. Each uploaded file is stored in a folder named after the user UID and prefixed with the current timestamp. However, when a user maliciously constructs the input, the problem arises. When the filename of the File Uploaded by the user is/../xxx, the uploaded file will be rename outside the user folder, so that the user can replace any file on the existing system.
This vulnerability is relatively low-level, but has the most serious consequences. It directly causes the entire system to be controlled by users. The solution is also simple:
- Var filename = Date. now () + '_' + file. name;
-
- Var userDir = path. join (config. upload_dir, uid );
-
- // Obtain the absolute path to which the object is finally saved
- Var savepath = path. resolve (path. join (userDir, filename ));
- // Verify
- If (savepath. indexOf (path. resolve (userDir ))! = 0 ){
- Return res. send ({status: 'foridden '});
- }
- Fs. rename (file. path, savepath, callback );
-
XSS of Rich Text Editor
XSS has been described in detail in @ Wu Zhonghua's article. In the cnode community, users are also using a rich text editor that supports markdown format to publish and reply to topics. No XSS preventive measures have been taken before, so you can directly write:
- <script>alert(123);</script>
- <div onmouseover="alert(123)"></div>
- <a href="javascript:alert(123);">123</a>
-
The content in markdown format does not have URL validity detection, so the XSS of various styles came out again:
[Xss] [1]
[Xss] [2]
! [Xss] [3]
[1]: javascript: alert (123 );
[2]: http://www.baidu.com/# "onclick = 'alert (123 )'
[3]: http://www.baidu.com/img.jpg# "onmouseover = 'alert (123 )'
In this community Application Scenario, HTML tags are introduced only for typographical operations, while other style definitions only make the entire interface messy, not to mention the potential risks of XSS vulnerabilities. Therefore, we do not need to support HTML tags for content formatting. Everything can be replaced by markdown. Then, the XSS risk caused by directly inputting HTML can be eliminated through simple and crude HTML escape.
- function escape(html) {
- return html.replace(/&(?!\w+;)/g, '&')
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/"/g, '"');
- }
However, such a rough escape may cause the special characters in the code entered by the user to be escaped and cannot be correctly displayed. You must extract and save the code segment first, only escape the parts of a non-code segment. The escape function becomes like this:
- function escape(html) {
- var codeSpan = /(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm;
- var codeBlock = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;
- var spans = [];
- var blocks = [];
- var text = String(html).replace(/\r\n/g, '\n')
- .replace('/\r/g', '\n');
- text = '\n\n' + text + '\n\n';
- texttext = text.replace(codeSpan, function(code) {
- spans.push(code);
- return '`span`';
- });
- text += '~0';
- return text.replace(codeBlock, function (whole, code, nextChar) {
- blocks.push(code);
- return '\n\tblock' + nextChar;
- })
- .replace(/&(?!\w+;)/g, '&')
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/"/g, '"')
- .replace(/`span`/g, function() {
- return spans.shift();
- })
- .replace(/\n\tblock/g, function() {
- return blocks.shift();
- })
- .replace(/~0$/,'')
- .replace(/^\n\n/, '')
- .replace(/\n\n$/, '');
- };
-
The URL validity check or xss filtering must be performed for the <a> tag generated by markdown and the href attribute in the tag. This ensures that the HTML code generated through markdown does not have the XSS vulnerability.
Because XSS has many methods, see XSS Filter Evasion Cheat Sheet. Therefore, HTML escape is the safest, but not every place can use markdown to replace HTML code. Therefore, not every place can use HTML escape, in this case, other methods are required to filter out XSS vulnerabilities.
XSS protection can only be implemented by defining a whitelist. For example, only <p> <div> <a> label is allowed and only the href class style attribute is allowed. Then, filter each attribute that may cause XSS.
The existing XSS filter module is node-validator and js-xss written by @ Lei zongmin.
The XSS module cannot prevent arbitrary XSS attacks, but at least it can filter out most of the vulnerabilities that can be imagined. Node-validator's XSS () still has bugs. For codes in the <p on = "> </p> Format, double quotation marks are not closed, leading to HTML element leakage detection.
XSS attacks caused by the template engine
The cnode community uses ejs as the template engine. In ejs, two methods are provided to output dynamic data to the page:
<% = Data %> // output for xss Filtering
<%-Data %> // directly output without Filtering
All filters must have one premise: the values of HTML attributes in the template file must use double quotation marks. For example:
- ' title='<%= reply.author.name %>' />
-
In the preceding two statements, the first sentence uses single quotes. You can cut off the src attribute by constructing an avatar_url with single quotes, and then add javascript code at will.
CSRF attack
CSRF attacks have solutions in node's web development framework connect and express. By storing a random _ csrf field in the guest session, the template engine transmits the _ csrf value to the front end when generating an HTML file, and any POST request submitted by the visitor, this field must be included for verification, ensuring that only the current user can modify the current page.
However, when the page has an XSS vulnerability, the CSRF prevention measures become a cloud. Malicious attackers can use javascript code to obtain the _ csrf value of other users, and directly simulate users' POST requests for server-side data changes.