How to search and fix injection flaws ===================================== Injection flaws such as SQL Injection, Command injection, NoSQL Injection and even XSS (Cross-Site Scripting) all manifest the same in code. Namely, they combine user input (untrusted) with structured trusted input. This can be in the form of a simple string concatenation, but can also occur when working with buffers, and unsanitized replacements. Searching for untrusted input ----------------------------- .. 1) explain what untrusted input is We label all expressions that have unknown values at compile time as untrusted. In practice these are mutable fields, method invocations, parameters, ... . .. 2) how to search for it We can search for expressions that contain these untrusted sources. The expressions that contain the most risk are usually the ones passed as arguments for method calls or constructors. Simply settings the ``containsUntrustedSources`` option to ``true`` for expressions will yield the desired behavior. The following example uses this mechanism to detect Cross-Site Scripting vulnerabilities. .. code-block:: java public void example(String name) { this.response("

Welcome " + name + "

"); } The ``name`` parameter has been concatenated with the HTML string without proper encoding. This will result in a cross-site scripting vulnerability. A quick example of exploiting this would be to supply the following argument: ````. Detecting this issue is fairly simple, we'll search for a function where the first argument is of the type ``String`` and contains untrusted input. .. code-block:: yaml search: methodcall: name: "response" args: 1: containsUntrustedInput: true .. 3) specify additional trusted sources However, not everything should be considered untrusted. Invoking an encoding routine is syntax wise a method call. We can alter the behavior of the analysis by adding the option ``trustedSources`` to our recipe. This gives us the option to specify which *unknowns* are considered trusted. The following example alters the previous recipe to consider all method calls with the name ``encode`` as trusted. .. code-block:: yaml search: methodcall: name: "response" args: 1: containsUntrustedInput: true trustedSources: - methodcall: name: "encode" Writing suggestions to fix these mistakes ----------------------------------------- These mistakes are solved in two distinct manners, by sanitizing the input or by extracting the untrusted data. Sanitizing input ~~~~~~~~~~~~~~~~ In code, this means passing the original expression through a sanitization routine and use that result instead. An example of such an operation can be seen in the following example. .. code-block:: java public void example(String name) { this.response("

Welcome " + encode(name) + "

"); } Here we see that the untrusted parameter ``name`` has been used as an argument for the routine ``encode`` and that we concatenate the result of this function with the action HTML string. To create such a suggestion we'll use a technique that's been mentioned before. By default, the action operates on the matched element. But this behavior can be changed by using the ``target`` option. If you have used ``containsUntrustedSources`` during the search part of a recipe, you will have the ability to choose ``untrusted`` as your desired target. Since we now can operate on these untrusted elements, a simple rewrite will be sufficient to finalize our example. .. code-block:: yaml :emphasize-lines: 5, 6 availableFixes: - name: "Encode untrusted input" actions: - rewrite: to: "encode({{{ . }}})" target: "untrusted" Extracting untrusted data ~~~~~~~~~~~~~~~~~~~~~~~~~ Most APIs that work with a query language will provide mechanisms to separate your query and your data. A practical example would be the following scenario: .. code-block:: java public void example(String firstname, String lastname) { this.query("SELECT * FROM users WHERE firstname = ? AND lastname = ?", firstname, lastname) } Here we can see that the query itself is passed as the first argument, and the parameters are passed as the remaining arguments. To indicate the API where we want our variables to be used, we use a ``?`` as our placeholder. Such an operation - in a security context - is usually called ``parameterization`` hence the name of the action ``parameterize``. An example configuration would be the following: .. code-block:: yaml availableFixes: - name: "Parameterize untrusted input" actions: - parameterize: placeholderFormat: "?" extractUntrustedInput: array: type: "java.lang.String[]" atArgumentPosition: 2 We've used ``extractUntrustedInput`` to configure where we want our data to be placed. In our example, we've chosen to extract it to an array and pass that array as the second argument of our method call. Import to know is that before performing this extraction, we will first take a look at the method signature. If the second parameter is defined as a *varargs* parameter (e.g. ``String... arguments``) then the action won't create an array, but just pass the data as separate arguments. Another nice feature is that this will work when the code is already partially correct. We'll try to detect the correct position of the untrusted element in the existing array and insert it at the appropriate index. Lastly, most APIs require the placeholders to not be encapsulated with string tokens (``'`` and ``"``). Sensei will detect this when performing the action and automatically remove the string tokens that are defined before and after the untrusted expression.