Securing Java application with trusted and untrusted code - java

Suppose I want to ship a Java application that supports plugins. For security reasons, I want the first party code (i.e. my code) to run basically unrestricted, but I want to limit the operations that the plugins would be able to perform. What's the right way of doing this? Is using the SecurityManager appropriate? Secondarily, is there a way to enforce that the application is started with a particular policy file in place, or should I assume if the end user wants to hack another policy file into place, that's their prerogative (or at least, there's nothing I can do to prevent it?).
The reason I have my doubts about the SecurityManager is because the goal of the class seems to be to prevent the entire application from doing things the end user doesn't want, whereas I'd like to use one to manage subsets of the application, completely opaquely to the end user, if possible.

Related

Scan dependency-jars for 'phoning home'?

Recently we found a jar in our application phoning home. See details: https://github.com/nextgenhealthcare/connect/issues/3431)
This is very undesired behaviour.
What would be the best approach to detect this in one of our depedencies?
ps. I can unzip all the jars and scan for HttpClient or UrlConnection, but there are many ways to connect to external systems, and preferably I don't want to reinvent the wheel.
I am aware of the OWASP Dependency-Check, but phoning home is not a CVE per se.
If you scan your jar's, and they do have network connectivity, then what can do then? You can't recompile the source, as you don't have it. A case of finding something you can do nothing about (apart from find an alternative).
The only way is it firewall your application, or network, use containers, and have a fine grained control of what you application talk to. Basically run your jars with zero trust!
I guess it really boils down to trusting your jar files, and that means in turn trusting the humans that make that everything that goes into jar file. (design, coding, build, distribution, maintenance ). The whole SDLC
If you approach the problem of zero trust, you can either get the JVM (security manager), The operating system (SELINUX/System Cap's/Docker) or the network (firewall/proxy/ids) (or all three) to control and audit access attempts..and either deny or permit these access depending on a policy that you set.
Scanning the jars for network calls can be done, but i'm sure if a jar really wants to obfuscate it's network behaviour, it will be able to, especially if it can run shell commands, or dynamically load jar's itself.
A jar you scan today, might not have the same behaviour on the next update? The classic supply chain attack.
If you don't trust you jar's, then if you must establish that trust, either thought scanning, auditing the source code.
There are many tools for this. I'm not sure if i'm allowed to recommend a particular product here that i've had success with, so i won't.

Is it possible to limit file access of certain classes to a certain directory?

I am creating a plugin system which dynamically loads classes, and since the plugins are user created, I want to restrict their access to everywhere but a working directory. Is there a solution for this in Java?
Basically the JVM offers the Security manager architecture.
One part of that are FilePermissions, which enable some sort of control here.
But note: this is really an advanced topic, and easy to get wrong, but your primary use case of isolating plugins coming from different packages should be doable.

can access to standard java libraries be blocked by prohibiting import statements and java.* strings?

suppose I want to allow people run simple console java programs on my server without ability to access the file system, the network or other IO except via my own highly restricted API. But, I don't want to get too deep into operating system level restrictions, so for the sake of the current discussion I want to consider code level sanitization methods.
So suppose I try to achieve this restriction as follows. I will prohibit all "import" statements except for those explicitly whitelisted (let's say "import SanitizedSystemIO." is allowed while "import java.io." is not) and I will prohibit the string "java.*" anywhere in the code. So this way the user would be able to write code referencing File class from SanitizedSystemIO, but he will not be able to reference java.io.File. This way the user is forced to use my sanitized wrapper apis, while my own framework code (which will compile and run together with user's code, such as in order to provide the IO functionality) can access all regular java apis.
Will this approach work? Or is there a way to hack it to get access to the standard java api?
ETA: ok, first of all, it should of course be java.* strings not system.*. I think in C#, basically...
Second, ok, so people say, "use security manager" or "use class loader" approaches. But what, if anything, is wrong with the code analysis approach? One benefit of it to my mind is the sheer KISS simplicity - instead of figuring out all the things to check and sanitize in SecurityManager we just allow a small whitelist of functionality and block everything else. Implementation-wise this is a trivial exercise for people with minimal knowledge of java.
And to reiterate my original question, so can this be hacked? Is there some java language construct that would allow access to the underlying api despite such code restrictions?
In your shoes I'd rather run the loaded apps inside a custom ClassLoader.
Maybe I'm mistaken, but if he wants to allow limited access to IO through his own functions, wouldn't SecurityManager prevent those as well? With a custom ClassLoader, he could provide his SanitizedSystemIO while refusing to load the things he doesn't want people to load.
However, checking for strings inside code is definitely not the way to go.
You need to check the SecurityManager. It is called by lots of JVM classes to check, before they perform their work, if they have the permission needed.
You can implement your own SecurityManager. Tutorial.

Are servlets good to use for my websites login confirmation page?

I am creating a web site in which user will be registered and he/she will be redirected if he/she has account.
So I was thinking to use servlet to link form and database.
Is it safe to use from hacking.?
Which is the best language which has less chances to get hacked than servlets ?
Well I am quite exicited to create it but I really don't want hacking my site very easy task.
As long as you are using Java for web forms, at some point any framework you use is based on servlets. So if you use them properly, yes it is safe.
The main point to keep in mind to prevent cracking your authentication (if database based) is SQL injection. As long as you use Hibernate, JPA or prepared statements you should be safe.
If using Tomcat, you can also start with Tomcat native authentication.
Just like any other framework, it's reasonably safe from hacking and not totally immune. You will be vulnerable to:
mistakes in your code/logic;
vulnerabilities in Tomcat/your servlet runner as they are discovered;
vulnerabilities in the JVM as they are discovered;
vulnerabilities in the OS;
vulnerabilities in... well, you get the idea...
In other words, it's essentially not so different anything else you might decide to use (be it PHP etc). I personally prefer Java to e.g. PHP because I have more confidence that there isn't some random default setting that's going to put the app at risk. But that's also because I'm far more of an expert in Java than PHP-- "use the technology you know best" is also another strategy...
Main things to keep in mind, apart from keeping your infrastructure updated:
don't use "raw" SQL to access the DB: at least use prepared statements, and for good measure implement some sensible validation on user input;
look at the Servlet Session API;
you generally want to check the logged in user for every page request: not sure exactly what you meant by "redirection";
if possible, firewall off "raw" access to the database, so that only localhost/the app server (if hosting DB on a separate server-- guess you won't to start off with) can access your DB (if you need to access remotely, consider a VPN connection).
I also wouldn't necessarily just "dive in": have more of a read round the subject.

Necessary and sufficient validations upon application init

I have a new puzzle for you :-).
I was thinking on how should an application handle his own start up. Like : checking for required libraries, correct versions, database connectivity, database compatibility, etc. To be specific, here is the test case. I use SWT and Log4J, for obvious reasons. Now, the questions :
Should the app check itself for the required dependencies? If yes, should the user be given specific details of what it's missing? Or just a message, and details to the logs?
What if the log4J library is unavailable?
What is the best to do the test? Verifying the file existance (using file.exists(), at specified path), or loading a class, say Class.forName("org.apache.log4j.Logger")? What should be the proper order to do the checks? For instance, if i test for SWT, i have no idea if logger is available or not, and the error will occur when i try to access that. Backwards, if i test for the logger 1st : a) The lib could be unavailable - i cannot log the error; b) SWT could be unavailable - unable to display the user message.
I've discovered apache.commons.lang framework today, and i find very useful the method org.apache.commons.lang.SystemUtils.isJavaVersionAtLeast(Float value)
, and manny others, i am sure. However, importing too much libs to your project dont make it hard to maintain? Versions change, compatibilities are lost, eg. one cannot control a 3rd party developement style or direction.
Thank u for your answers.
I agree with your need. Checking for required runtime environment provides:
immediate feedback, instead of randomly breaking when accessing some functionnality
hopefully more skilled user, as the immediate feedback is available to the guy that is installing the software, hopefully more skilled than an average user, or at least less confident (installing is always a special operation). A more skilled user is less disturbed if the error is coming in the console, he doesn't depend on a graphical interface.
improved reporting : the error message can be explicit (you're in charge), while default error messages come in many flavours (they are not always that helpful on 1. what's wrong 2. suggesting a fix).
But please note that the runtime requirements could be checked in two situations:
when installing : long verifications are always acceptable ; if a library is not here, a required database or WebService is not accessible, it won't be here at runtime either, so you can complain immediately.
when starting the execution : you can verify again (and some verifications may only happen at that point)
This suggests creating an installer for your application.
Potentially, errors would not all be blocking for the installation. Some would rather accumulate as a list of tasks to be done after installation, maybe nicely formatted in a file with all reference information.
Here, we once again hit the notion of error level in validation (similar to what happens for Log4j) : some validation errors are at fatal level, others are errors, possibly also warnings ...
In our projects, we have some sort of initialization and validation going on on startup. Based on our day-to-day experience, I would suggest the following:
When the application gets big, you don't want to have all init centralized in one class, so we have a modular structure.
A small kernel is configured with a list of modules classes. It's whole init sequence is under strict control, ready for any exceptions (translating them to appropriate messages, but memorizing the stack traces that are so useful to the developpers), making no assumption on the available libraries and so on... CheckStyle can be configured specially for this code.
The interface (of course, abstract class is possible) that the modules implement typically have several initialization methods. They could be:
getDependencies : returns a list of modules that this one depends on.
startup : when the whole application is starting. This will be called only once during startup, and cannot be called again.
start : when the module gets ready for regular operation
stop : reverse from start
shutdown : reverse from startup.
The kernel instanciates each of the module in turn. Then he calls one init method on all of them, then another init method and so on as needed. Each init method can:
signal error conditions (using levels, like Log4J).
an exception thrown would be caught by the kernel, and translated to an error condition
consult another module for its status (because dependencies are the general case), and react accordingly. If needed, the dependencies could be made declaratively.
The kernel takes care of module dependencies generically:
He sorts the modules so that dependencies are respected.
He doesn't initialize a module if one of its dependencies couldn't make it.
If asked to stop a module, he will first stop the modules that depends on it.
A nice feature of this kernel approach is that it is easy to aggregate the errors, at various levels (although fatal could stop it), and report all of them at the end, using whatever means is available (SWT or not, Log4J or not ...). So instead of discovering the problems one after the other, and having to start again each time, you could deliver in one blow (nicely prioritized of course).
Concerning your precise questions:
Should the app check itself for the required dependencies?
Yes (see higher)
If yes, should the user be given specific details of what it's missing? Or just a message, and details to the logs?
As said higher, when installing the user is more prepared to deal with this.
When starting, we use an easy message for the end-user, but give access to the full stack traces for the developper (we have a button that copies in the clipboard the application environment, the stack traces and so on).
What if the log4J library is unavailable?
Log without it (see higher).
What is the best to do the test? Verifying the file existance (using file.exists(), at specified path), or loading a class, say Class.forName("org.apache.log4j.Logger")?
I would load a class. But if it failed, I might check the file existence on disk to give a improved message, including "how to fix".
What should be the proper order to do the checks? For instance, if i test for SWT, i have no idea if logger is available or not, and the error will occur when i try to access that. Backwards, if i test for the logger 1st : a) The lib could be unavailable - i cannot log the error; b) SWT could be unavailable - unable to display the user message.
As I said higher, I suggest these low-level errors get accumulated in a small area of code (kernel), where you could use anything that is available to display them. If nothing is available, you could simply log in the console without Log4J.
The short answer is no. The JVM appropriately handles this functionality on initialization, or at runtime. If a required class is not found on the classpath, a ClassNotFoundException will be thrown. If a class was found, but a required method was not, a NoSuchMethodException is thrown.
Regarding 1 through 3 , there are 2 main use cases here:
application packaging is under your control, and can make sure that all required dependencies are packaged properly. Run-time validations are not useful here.
application packaging is not under your control, and you deliver the main jar and the instructions on what the requirements are. Run-time validations might be useful, but someone who wants to package your application usually has enough skill to understand what a ClassNotFoundException: org.apache.logging.LogManager means.
Regarding 4, as long as you keep the same version of the dependency included in your project, you will have no problems in keeping control. Upgrading to a newer version is a conscious decision, which requires thought and testing.

Categories