Secure Engineering & Your QRadar App

Ensuring Your QRadar Application is Secure

Application security is vitally important for every software project, especially so for security projects. This is why the validation process for QRadar app submissions goes through a secure engineering review. As a member of the secure development team, this blog post will hopefully give you (the app developer) some insight regarding what to expect during our app validation process. Keep in mind most QRadar apps are based in Python using the flask framework, so the basis for most of the examples will be based on Python and Flask. If you have chosen to use a different technology stack, the concepts outlined in this guide will still be useful, but you’ll have to modify the examples to fit your needs.

Keep in mind there is no one size fits all solution for security, nor is there any tool that can detect all issues. One of the best tools for secure engineering is simply knowing that these problems exist and that you should be worried about them.

How to prevent app submissions from being rejected

Each application undergoes an individual security review as part of the submission process. The tips and discussion in this article are intended to guide users to the types of items we review and take questions about frequently to reduce the chance of an app submission from being rejected based on common issues.

As mentioned, all submitted apps for QRadar on the X-Force App Exchange go through a security review. Our job is to review apps for issues and notify developers when an app fails the secure app validation process.  This blog post includes examples of common issues seen with app submissions that are insecure. Fortunately, many of these issues have some simple fixes and these examples will assist you with writing secure apps and outlines the items reviewed during app validation.

1. SQL Injection (SQLi) in your QRadar App

SQL injection is the number one issue we see when validating security in apps. Ff you don’t know why this statement is bad, then you should go read this OWASP article, or watch this video by Computerphile (YouTube). This attack gives the power to a malicious actor to do almost anything to your database.

# Do NOT do it this way.
cmd = "update people set name='%s' where id='%s'" % (name, id) curs.execute(cmd)
#OR
cmd = "update people set name="+name+" where id="+id curs.execute(cmd)

In general, you should always use parametrized SQL statements (sometimes called prepared statements). Building your query in risky ways, such as string formatting or concatenation above will leave you open to SQL injection attacks when the variables contain untrusted input. Even if you know a given variable comes from somewhere you consider to be safe, keep in mind that in the future that same variable may become user editable, or if you have written reusable code, someone may call your function with unsafe data.

For that reason, it is best practice to always use parametrized SQL statements. The added benefit for app validation is when I see safe parametrized SQL, it is an easy pass. When unsafe query construction is used I must spend time backing through the source code to determine if it is exploitable or not.

There is a form post with more examples and implementation details:https://developer.ibm.com/answers/questions/417797/how-to-prevent-sql-injection-in-an-app/

SQL Injection recap:

  • Use parametrized (prepared) SQL statements for user data input
  • For dynamic query building (instructions) uses an allowlist approach to set
  • Treat all data as untrusted, even if it is trusted now it might not always be, and it makes validation quicker.

2. Cross Site Scripting (XSS) in a QRadar App

Speaking of untrusted data, another very common issue is cross site scripting. Kind of like SQLi, this is caused when the data and instructions get mixed together. OWASP and Computerphile (YouTube) do a great job at explaining these injection attacks. In short, XSS attacks allow a malicious user to inject their own JavaScript into your page and complete nefarious actions.

While the fix for XSS is still quite easy, it’s not quite as cut and dry as SQLi prevention. I’ll highlight some key points, but reading more into OWASP’s XSS Prevention guide is well worth it.

Many popular JS frameworks have some version of XSS prevention, and it’s worth a read to see what your framework has to offer. The Key points are to make sure that data is escaped. A great example is the .text() and .html() functions of jQuery. The .html() function will put the literal contents of a string into the target DOM element. So if you pass “<script>alert(“xss”)</script>” then a script element will get inserted. Using the same string with .text() will result in the following being inserted:

&lt;script&gt;alert(&ldquo;xss&rdquo;)&lt;/script&gt;&rdquo;

The encoded string does not create a script element, and the text displays as intended.

Not escaping QRadar API provided data

Speaking of untrusted data, you shouldn’t implicitly trust all data from the QRadar API. That is because QRadar does not do any sanitation on output of API data. While not every field is untrustable, it is easier to treat it all as untrusted. You are allowed to name a log source <script>alert(“xss!”)</script>, and in QRadar we escape the log source name when we display it.

Not using third-party libraries safely

Steps should also be taken to ensure external resources like JavaScript libraries are loaded in a safe manner. Mechanisms like sub resource integrity (SRI) can be used, but the ideal solution is to serve it locally. This is discussed more here.

Using incorrect content types

Even if you are escaping all data before rendering it to a webpage, you may still be vulnerable to XSS. If you return json from an API as part of your application, you need to ensure that data is handled appropriately. The easiest way to handle this is to use the built in flask.jsonify method to return json. Jsonify automatically sets the content type to application/json mimetype, telling your browser not to interpret that data as HTML. If instead an app returns the string generated by json.dumps (or similar method), the default mimetype is text/html which the browser will try and parse as HTML, and can result in an XSS attack. See OWASP XSS Prevention Rule #3.1 for more. 

3. Cross Site Request Forgery (CSRF / XSRF) in a QRadar App

The last of the big 3 vulnerabilities. Does your application let the user create, modify, or delete any data? Then you need CSRF protection.

Like SQLi and XSS OWASP and Computerphile have some great resources on it. CSRF attacks work by tricking the browser of the victim into sending a legitimate seeming request without the victim intending it.

If you’re using flask, this is a very easy fix with the Flask-WTF library and a few lines in your templates and JavaScript.  There is another form post discussing that.

4. Leaked Credentials in a QRadar App

You should never print off credentials anywhere in your application, but I’ve seen numerous places where developers have leaked credentials in their application.

4a. Hardcoded into source code

This is a bad security practice in general on many levels. If hard-coded credentials are part of the app, then they should be configurable by the user. If it is a test/validation file, then the file should be removed from the application or removed during the build process.

4b. Commented out in source code

Like above, I’ve seen commented out credentials that were valid and let me log in to the external source.

4c. Printed in logs

Auditing changes is always a good practice. For example, you can log the fact that a parameter or value was changed; however, do not log token values or passwords in your application logs.

Never do anything like this:

logger.info("Changed token to : %s" % token)

Instead, note that the token value was changed without writing the value to the logs:

logger.info("Changed token." )

4d. Query strings

While you should never create, modify, or delete with a GET anyway, one more reason not to do that is be because the query string is often logged so  /app/updateToken/123-abc-xyz shows up in the logs.

4e. Returned in an API or webpage

There should be no way to retrieve any credential from an app. No API responses, nor should it return in a configuration page. Even “hiding” it with a password field is not acceptable. For higher entropy fields (Ex: machine generate tokens like Qradar authorized service tokens) you may reveal the last 4 characters. There is a forum post discussing this further.

4f. Stored in clear text

All secrets should be store encrypted when on disk. The app framework now provides functionality to encrypt secrets. This should be used to store any kind of secrets (passwords, API keys, etc), this makes it even harder for an attacker to access the secrets.

5. Interactions with the File System or Operating System (Added August 2018)

Similar to XSS and SQLi, untrusted input can be a disaster when it comes to interacting with the server your application runs on.

5a. Directory / Path Traversal

An app might allow a user to upload and delete files. If the app simply asks “what file do you want to delete” and allows the user to specify a file name, and the app just appends the file name to a base path, the user could escape or traverse to other files.

file_to_delete = "../logs/audit.log"
file_to_replace = "../../../etc/passwd"
base_directory = "/store/app/user_files/"
os.delete(  base_directory  + file_to_delete )
os.replace( base_directory  + file_to_replace )

If you’re modifying files on disk, you need to ensure they are the files you expect! Check out the forum post detailing more on Directory Traversal Prevention or check out the OWASP guide

5b. Command Injection

Just as bad, if not worse, is command injection. If you need to invoke an external script, or interact with the operating system on your host you could potentially run into this issue. Like many of the issues outlined here, it is because of untrusted input.

import subprocess

ip = "192.168.2.1"
syslog_path = "something"
unique_id = "haxor & touch badFileOrCommand.txt"

subprocess.Popen(['exec python target.py {0} {1} {2}'.format(ip, syslog_path, unique_id)],shell=True)

The above example shows three variables being passed to another python script, the ‘&’ tells the OS we want to run another command, in this case we only create a file, but it could be any command the account has privilege to run. Take a look at the new forum post about it or the OWASP article.

6 Sensitive sample data

With an ever shifting focus on privacy we takes steps to be extra cautious anywhere data may be hard-coded that may be considered personal, sensitive, or confidential. This includes places like documentation, sample data, and screen captures. The guidelines are discussed here, but the short version is you should only use the most obviously fake data in your apps.

7 Improper certificate validation (Added December 2019)

HTTP over TLS (Ie: HTTPS) is a vital component of keeping confidential information safe and secure on the modern web. When HTTPS is not implemented properly data is at risk in a man-in-the-middle (MITM) attack as the client's trust may be misplaced. You can see the form post here detailing the steps to implement.

Other issues in the OWASP Top 10 or SANS Top 25

The items here should not be considered an exhaustive list of all the potential security issues that may cause an application to be rejected. The reality is there are many categories of potential issues, MITRE has over 800 enumerated vulnerabilities types in their Common Weakness Enumeration (CWE) list. While having a deep understanding of each and every CWE is often impractical, a great place to start is the SANS Top 25 and OWASP Top 10 lists. These lists are organized in slightly different ways but do contain a large amount of overlap (SQL Injection and XSS appear in both). Both lists map to CWEs. You should expect any issue found that appears on either of those lists will result in a rejection of your application. This does not mean that all other security issues are fine, but the SANS and OWASP top lists should receive the most scrutiny from app developers.

Issues that extend the app validation review process

a. Excessive files

If you’ve decided to include every JavaScript library in your apps /static folder “just in case” that means I’ll be looking to see what security implementations they have or if you even use them.

b. Undocumented code

When you have large sections of non-trivial code without comments, it takes time to decipher what they do. A few comments go a long way when manually reviewing code.

c. Large codebase

This is something that really cannot be changed, if your app does a lot then it will bigger than an app that does less. Just keep in mind larger apps take longer to validate.

d. Minified / obfuscated code

Code that is not human (or even machine) readable can't be manually reviewed. App validation will reach out if you do not submit a copy of the unminified code. You do not have to ship the unminified code. 

What can I do to make my QRadar app more secure?

Just thinking about secure engineering is one of the best resources. Secure app development is not just getting your app to work, but to look at your app from the perspective of someone who would willingly try to abuse it. It can be hard since as developers we have a finite amount of time and resources to dedicate and security can be an after though. But you must keep in mind that hackers have much more time, and security is their focus.

Depending on your resources you might consider having a resource dedicated to doing secure code review or make security checks part of your code review checklist. If your organization has secure engineering personnel (other names include: product security, application security) you may try reaching out to them for guidance.

No tool can find all potential vulnerabilities, but they can sure help. OWASP has a great list of tools. I recommend Bandit for python flask apps. It’s part of our validation process. It finds issues in your source code like SQLi, and will flag some unsafe libraries, such as pickle. The code analysis tool installation and usage is pretty straight forward.

The following command will generate you an HTML report of potential issues:

pip install bandit

bandit /path/to/code -r -a file -f html -o file.html

If you have follow-up questions, ask in our forums: https://ibm.biz/qradarappdev