1 | # Security
|
2 |
|
3 | Executing arbitrary expressions like enabled by the expression parser of
|
4 | mathjs involves a risk in general. When you're using mathjs to let users
|
5 | execute arbitrary expressions, it's good to take a moment to think about
|
6 | possible security and stability implications, especially when running
|
7 | the code server side.
|
8 |
|
9 | ## Security risks
|
10 |
|
11 | A user could try to inject malicious JavaScript code via the expression
|
12 | parser. The expression parser of mathjs offers a sandboxed environment
|
13 | to execute expressions which should make this impossible. It's possible
|
14 | though that there are unknown security vulnerabilities, so it's important
|
15 | to be careful, especially when allowing server side execution of
|
16 | arbitrary expressions.
|
17 |
|
18 | The expression parser of mathjs parses the input in a controlled
|
19 | way into an expression tree or abstract syntax tree (AST).
|
20 | In a "compile" step, it does as much as possible preprocessing on the
|
21 | static parts of the expression, and creates a fast performing function
|
22 | which can be used to evaluate the expression repeatedly using a
|
23 | dynamically passed scope.
|
24 |
|
25 | The parser actively prevents access to JavaScripts internal `eval` and
|
26 | `new Function` which are the main cause of security attacks. Mathjs
|
27 | versions 4 and newer does not use JavaScript's `eval` under the hood.
|
28 | Version 3 and older did use `eval` for the compile step. This is not
|
29 | directly a security issue but results in a larger possible attack surface.
|
30 |
|
31 | When running a node.js server, it's good to be aware of the different
|
32 | types of security risks. The risk whe running inside a browser may be
|
33 | limited though it's good to be aware of [Cross side scripting (XSS)](https://www.wikiwand.com/en/Cross-site_scripting) vulnerabilities. A nice overview of
|
34 | security risks of a node.js servers is listed in an article [Node.js security checklist](https://blog.risingstack.com/node-js-security-checklist/) by Gergely Nemeth.
|
35 |
|
36 | ### Less vulnerable expression parser
|
37 |
|
38 | There is a small number of functions which yield the biggest security
|
39 | risk in the expression parser:
|
40 |
|
41 | - `import` and `createUnit` which alter the built-in functionality and
|
42 | allow overriding existing functions and units.
|
43 | - `evaluate`, `parse`, `simplify`, and `derivative` which parse arbitrary
|
44 | input into a manipulable expression tree.
|
45 |
|
46 | To make the expression parser less vulnerable whilst still supporting
|
47 | most functionality, these functions can be disabled:
|
48 |
|
49 | ```js
|
50 | import { create, all } from 'mathjs'
|
51 |
|
52 | const math = create(all)
|
53 | const limitedEvaluate = math.evaluate
|
54 |
|
55 | math.import({
|
56 | 'import': function () { throw new Error('Function import is disabled') },
|
57 | 'createUnit': function () { throw new Error('Function createUnit is disabled') },
|
58 | 'evaluate': function () { throw new Error('Function evaluate is disabled') },
|
59 | 'parse': function () { throw new Error('Function parse is disabled') },
|
60 | 'simplify': function () { throw new Error('Function simplify is disabled') },
|
61 | 'derivative': function () { throw new Error('Function derivative is disabled') }
|
62 | }, { override: true })
|
63 |
|
64 | console.log(limitedEvaluate('sqrt(16)')) // Ok, 4
|
65 | console.log(limitedEvaluate('parse("2+3")')) // Error: Function parse is disabled
|
66 | ```
|
67 |
|
68 |
|
69 | ### Found a security vulnerability? Please report in private!
|
70 |
|
71 | You found a security vulnerability? Awesome! We hope you don't have bad
|
72 | intentions and want to help fix the issue. Please report the
|
73 | vulnerability in a private way by contacting one of the maintainers
|
74 | via mail or an other private channel. That way we can work together
|
75 | on a fix before sharing the issue with everybody including the bad guys.
|
76 |
|
77 | ## Stability risks
|
78 |
|
79 | A user could accidentally or on purpose execute a
|
80 | heavy expression like creating a huge matrix. That can let the
|
81 | JavaScript engine run out of memory or freeze it when the CPU goes
|
82 | to 100% for a long time.
|
83 |
|
84 | To protect against this sort of issue, one can run the expression parser
|
85 | in a separate Web Worker or child_process, so it can't affect the
|
86 | main process. The workers can be killed when it runs for too
|
87 | long or consumes too much memory. A useful library in this regard
|
88 | is [workerpool](https://github.com/josdejong/workerpool), which makes
|
89 | it easy to manage a pool of workers in both browser and node.js.
|