RMI security and design - java

I have a server which allows RMI connections. It exposes a "Server" object on the registry for remote calls, which has a method "authenticate". That method, if successful, returns a "user" object. This user object can then be used by the client to get some data.
It looks like this:
RMIServer server = new RMIServer ();
Naming.rebind("rmi://"+ hostName +"/" + AUTH_OBJECT_BINDING, server);
RMIServer then has a method:
public InterfaceUser Authenticate(String username, String password);
This method checks the username/pw and returns a user object (actually the interface). The user object saves some string which contains the username, and a list of permissions. This can then be checked when calling other methods further down the line.
My question is this:
Someone wants to develop a (another) client for the server, to do this I would give them the interfaces required to interact with it (e.g, the InterfaceUser interface, as well as others). I would need to set up a fake "development" server somewhere to allow them to do this, which they can connect to. Is it possible for that developer to use the interfaces to allow it to get an instance of "InterfaceUser" from the real server without correctly authenticating? For example, Bob logs in to the real server, the server creates a User object which implements InteraceUser. Can Alice, with the interfaces, hijack that object or find it somehow on the registry? All other objects are exported on the same port (if this makes a difference).
Or, can Alice authenticate with the fake server, then somehow use that to access the real server? Let's assume that all usernames/passwords are different on the "real server" and the "development server"
Thanks, any help is appreciated.

Alice can't get any user object unless she can login to a server that provides it.
Any user object Alice does get is bound to the server that created it, and can't be used on another server. It doesn't even survive power cycling of the correct server.
Does that answer your question?

Related

Code showing username and password for connecting to server

I'm writing a method that make it possible for my Java program to create a database connection that will eventually make me able to access it from other classes/methods.
public class DatabaseConnection
{
private Connection databaseLink;
public Connection getConnection()
{
String url = "jdbc:mysql://localhost/DBname";
try
{
Class.forName("com.mysql.cj.jdbc.Driver");
databaseLink = DriverManager.getConnection(url, "fakeUsr", "fakePsw"); //these are not the real username/password
}
catch (Exception e)
{
e.printStackTrace();
}
return databaseLink;
}
}
I've got a couple of issues:
1)people not using my computer will not be able to get into my server since I wrote "localhost":
String url = "jdbc:mysql://localhost/DBname";
2)I've typed the real username and password instead of "fakeUsr" and "fakePsw".
The thing is: I'm quite sure that the average user of my program should NOT be able to access that information. Is there any other way to permit access to a DB without making username and password readable by virtually anyone getting access to my source code?
For issue n. 1: I tried to type my IP address instead of "localhost" here:
String url = "jdbc:mysql://localhost/DBname"; //changed localhost to my IP address
but then I get "Communications link failure".
For issue n. 2: I have literally no idea how to solve this. I've never coded a program that needs access to a DB so I had to improvise a bit for that.
About Issue #2:
There is no secure way of storing the password inside the code itself. You can of course try to encrypt the password, but then your code has to decrypt it when the connection is established and therefore the encryption key is visible virtually "to all that have access to your source code". With this key, it is possible to get to the real password, just a little bit more complicated.
The only secure way is to have the user enter the login credentials by his own. Either low level (program arguments when starting your application) or by some form of "login dialog", if the application has a GUI.
A third option would be to create a technical user with restricted DB access, depending on the application you are working on. But this usually causes security issues.
You could create your application such that it sends an https request and authenticate itself against a webserver. What you use to authenticate is up to you: Client IP, username, password, client certificates, ...
Once authenticated, your webserver could transfer a one-time username/password that the client uses to login into your database.
The advantage here is that you can still control whether the user gets full or restricted access, or gets no password any more for whatever reason. And there is no security hole in your application.
1⁠) Most Internet providers don’t allow ordinary users to accept incoming socket connections, both for security reasons and because the network traffic can quickly overwhelm consumer grade networks. You will have to either purchase a commercial Internet connection which allows incoming connections, or look for a server you can lease or borrow. I’m afraid I don’t know what options are available.
2⁠) As MrFreeze correctly pointed out, there is no way to safely embed credentials in an application. No matter what you do to obscure your database login credentials, someone can always decompile your program and figure out how you are decrypting those credentials. The only truly safe solution is to tell users you trust what the credentials are, then write your application so the user must enter them.
Side note: Class.forName("com.mysql.cj.jdbc.Driver"); has not been needed for many years. You can remove that line.

RMI connection with wifi and dialup internet

I have created a simple database application using rmi. It works fine with my local wireless network. But now i want to connect my client to the server through the internet. I know that, this can be achieved with setting up port forwarding in the router. But i want it to work in any computer which is connected to the internet using wifi connections, dialup
connections etc. How to do that?
what to write here? Naming.lookup ("rmi://?????????????");
As I am quite new to java, Please give me a detailed answer with a simple code example.
Thanks in advance
I hope you are messed up with Java RMI Concept. Irony is that a few days ago I was also thinking up the same except that I was thinking to connect up on my internal network.
There are two kinds of classes that can be used in Java RMI.
A Remote class is one whose instances can be used remotely. An object
of such a class can be referenced in two different ways:
1. Within the address space where the object was constructed, the object is an ordinary object which can be used like any other object.
2. Within other address spaces, the object can be referenced using an object handle. While there are limitations on how one can use an
object handle compared to an object, for the most part one can use
object handles in the same way as an ordinary object.
A Serializable class is one whose instances can be copied from one
address space to another. An instance of a Serializable class will be
called a serializable object. In other words, a serializable object is
one that can be marshaled.
SO, HERE COMES THE ANSWER TO YOUR QUESTION,ASSUMING YOU ARE TALKING ABOUT REMOTE CLASS ON DIFFERENT SYSTEM(SERVER).
The name of a remote object includes the following information:
The Internet name (or address) of the machine that is running the
Object Registry with which the remote object is being registered. If
the Object Registry is running on the same machine as the one that is
making the request, then the name of the machine can be omitted.
The port to which the Object Registry is listening. If the Object
Registry is listening to the default port, 1099, then this does not
have to be included in the name.
The local name of the remote object within the Object Registry.
The URL for a remote object is specified using the usual host, port and name:
rmi://host:port/name
host = host name of registry (defaults to current host)
port = port number of registry (defaults to the registry port number)
name = name for remote object
Assuming that your code is lying on the server having host-name as "XYZ.edu/home/CLasses"(you can give the DNS/IP-Address of server and include the location of the Class file),port-number="1099"(default) and name of the remote Object="abc" for your ABC.java Class in Server. In this way one will be able to call the Remote Object from different machines. Also, you need to keep the whole Server code on the Internet Address so that Clients can access them from the Internet(one can't access the offline-code present in your machine). Then only it can happen!!!
Here is the example Client program:
/**
* Client program for the "Hello, world!" example.
* #param argv The command line arguments which are ignored.
*/
public static void main (String[] argv) {
try {
HelloInterface hello =
(HelloInterface) Naming.lookup ("//ortles.ccs.neu.edu/Hello"); //see here the address of the server hosting the Server file,you can omit port number,it'll take default port 1099.
System.out.println (hello.say());
} catch (Exception e) {
System.out.println ("HelloClient exception: " + e);
}
}

Distributed Erlang, how do I generate unique node names?

I am building an Erlang server-client program. The server is written in Erlang OTP and the client in Java with Jinterface.
When creating a Jinterface connection I have to set a node name in the constructor OtpNode(). The problem is that this is done before connecting to the server (obviously), but each client must have unique names. If a client tries to connect to the server when a client with the same name is already connected, then the new connection fails.
How do I solve this? My first taught was to have a node without name or let the server supply the client with a name, but that does not seems to work...
Sounds like a job for UUID.randomUUID().
"UUID" stands for "Universally Unique ID", and it allows you to assume that any two clients will generate a different UUID without needing to contact each other.

How to HIGHLY restrict users with "specific roles" to access the application from "a certain place"?

I need to restrict specific user's roles to use the application while are accessing it from a specific place lets say "Users with role employee can only access the application from the office or its branches"
IP checking? it is changeable
How about if I follow private/public key? the disadvantage of that is that if I put the key in the cookie they can read it or may clear their cookies.
Based on mac address ? it is changeable
You can't trust IPs and MAC addresses are even more useless, your only friend here is cryptography. Assuming your user will authenticate using his credentials you need to somehow authenticate the machine as well. This is done by placing a different certificate on each machine and having the client prove his "identity" to the server by using his certificate.
If your client-server communication is based on SSL, you can require client authentication - have a look at Java HTTPS client certificate authentication, http://rap.ucar.edu/staff/paddy/cacerts/ or http://docs.oracle.com/cd/E11035_01/wls100/security/SSL_client.html.
If your communication is not based on SSL, or you wish to authenticate on the application level - you can still use the certificate. Load it from the truststore and prove your identity by proving you have access to the private key (usually the server sends a challenge, something random encrypted with the public key, you answer by decrypting it with the private key and sending it back. That way you prove you have the private key without having to show it).
If you do not wish to store certificates, you can just place a different encrypted file on each machine. The client will be able to decrypt it (using a hard coded key) and send something akin to a password to the server.
How do you protect these certificates? Read-only permissions for the users on the file...
Several notes -
You can't really EVER trust a client machine. A resourceful hostile user will break anything. The more resources you "enemies" have the more effort you need to put into your defence.
You didn't specify details regarding your environment. I'm sure there are system level solutions which I'm not aware of. For example - your server may connect to the Active Directory and monitor user logins on specific machines.
Sometimes the best solution may not come from the software level. For example, if your server uses a designated port for your communication. You could allow\block this traffic on your firewall\router\personal firewall - in places more adequate to resolve this issue than your server. If you have application control enforcement, you can allow the client itself to run on only specific machines.
You can also look for ways to create some unique PC fingerprint (motherboard Id, cpu id, SID in Active Directory, HDD id, MAC address...) - your server could then store a list of allowed fingerprints and your client will send the currently calculated fingerprint. This still comes back to - how well do you trust your clients?
Restricting by IP only works if people are coming from places that have static IPs. Anywhere like at home where you have dynamic it doesn't work.
If you can't use static and still want to restrict by IP you could use a service like http://dyn.com/dns/ to assign a FQDN to your IP. Then you could do lookup by FQDN to see if it returns an IP that matches the one in the request. This lookup could be cached so you're only doing every few hours. The tricky part to this is that each location would have to setup of a dynamic DNS client. Some routers now have this built in.
You can't get the MAC address through the HttpServlet class. And if could you'd get the MAC address from the device that is talking to your server which most likely would be something like a router, load balance, switch. MAC address aren't not route'able.
Re: Keys, you can use x509 certs - http://static.springsource.org/spring-security/site/docs/3.0.x/reference/x509.html
I just want to address this part of your Question:
I am trying to implement the IP method but it runs into following error.
java.lang.IncompatibleClassChangeError: com.project.Default and
com.project.Default$IpCheckService disagree on InnerClasses attribute
An IncompatibleClassChangeError means that there is a conflict between what the types were at compile time and what they are at runtime. In this case, it seems to be that you have (had) a nested IpCheckService class that has changed from static to non-static (or vice-versa!), and somehow you've managed to load an old version of one of the classes.
This is a build or deployment problem. If you can figure out what is going wrong here there is a good chance that your code will work. (At least, you won't get this exception any more.)
UPDATE:
The only way to restrict users in a certain place is the following:
You must define Fixed IP's in the Office!
Or, at least, a subnet mask for the office and it's branches.
In your application, check the subnet mask from the request and compare it to fixed preconfigured office subnet mask.
Thus, either you put those fixed IP's into webconf.xml or the subnet mask for the IPs;
In any event, the solution will always be connected to a network solution.
You could try something like this to check a fixed IP:
public class TestFilter implements Filter{
public void destroy() {}
public void init(FilterConfig arg0) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filter) throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
IpAddressMatcher matcher = new IpAddressMatcher("192.168.1.0/24");
try {
if(!matcher.matches(req.getHeader("X-Forwarded-For"))){
res.sendRedirect("AnotherPage.jsp");
}
} catch (UnsupportedOperationException e) {
//Handle IT
}
}
}
Also, you may need check the following, to catch every situation:
request.getHeader("Proxy-Client-IP");
request.getHeader("WL-Proxy-Client-IP");
request.getHeader("HTTP_CLIENT_IP");
request.getHeader("HTTP_X_FORWARDED_FOR");
request.getRemoteAddr();
I've found the following code from here, you can identify them based on their mac address.
This one is also helpful.
package com.mkyong;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
public class App{
public static void main(String[] args){
InetAddress ip;
try {
ip = InetAddress.getLocalHost();
System.out.println("Current IP address : " + ip.getHostAddress());
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
byte[] mac = network.getHardwareAddress();
System.out.print("Current MAC address : ");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mac.length; i++) {
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));
}
System.out.println(sb.toString());
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketException e){
e.printStackTrace();
}
}
}
To set up such kind of authorization rules, the first thing need to be defined is:
What is the definition of "office or its branches" in terms of computer identification?
Usually it is computers from some subnetwork as written in answers above - in that case the solution is obvious.
Second possibility - some guy came to the office with his personal computer or laptop or touchpad, etc. If this is allowed by security policy, the only entity we can authenticate is user. We might still want to differ access from the office (as physical location, e.g. building)/from home. In that case I'd recommend to look at one-time-password generation devices that should be available to user only in the office.
"Users with role employee can only access the application from the office or its branches"
Use a site-to site VPN. This effectively transforms the problem into an intranet login problem, which is trivial to solve.
By having a site-to-site VPN, you can be assured of the identity of the remote sites, since connection setup and authentication is generally performed by routers on site, the configuration of which users at the site do not need to know (or have on their computers, so cannot take away).
Once transformed into an intranet problem, just bind the application to an intranet address and secure it as you would any other intranet resource.

How to transfer data from one jsp to another jsp with sockets

I have 3 .jsp's. The first one asks the user for their username. Once the form is submitted it is taken to a 2nd jsp where a unique passcode is created for the user. How would I go about taking this passcode and passing it to a 3rd jsp using a socket?
You can use java.net.URL and java.net.URLConnection to fire and handle HTTP requests programmatically. They make use of sockets under the covers and this way you don't need to fiddle with low level details about the HTTP protocol. You can pass parameters as query string in the URL.
String url = "http://localhost:8080/context/3rd.jsp?passcode=" + URLEncoder.encode(passcode, "UTF-8");
InputStream input = new URL(url).openStream();
// ... (read it, it contains the response)
This way the passcode request parameter is available in the 3rd JSP by ${param.passcode} or request.getParameter("passcode") the usual way.
Better is however to just include that 3rd JSP in your 2nd JSP.
request.setAttribute("passcode", passcode);
request.getRequestDispatcher("3rd.jsp").include(request, response);
This way the passcode is available as request attribute in the 3rd JSP by ${passcode} or request.getAttribute("passcode") the usual way.
See also:
Using java.net.URLConnection to fire and handle HTTP requests
Unrelated to the concrete question, this is however a terribly nasty hack and the purpose of this is beyond me. There's somewhere a serious design flaw in your application. Most likely those JSPs are tight coupled with business logic which actually belongs in normal and reuseable Java classes like servlets and/or EJBs and/or JAX-WS/RS which you just import and call in your Java class the usual Java way. JSPs are meant to generate and send HTML, not to act as business services, let alone web services. See also How to avoid Java code in JSP files?
So, you want the username to be submitted from the first JSP to the second, by submitting a form to the second, right?
But, for interaction between the second and third, you want to avoid using the communication mechanisms behind the the JSP files and use your own, right?
Well, how you might implement doing this depends on where you're sending your communication from and to. For instance, are they on the same machine, or on different machines?
Generally speaking, you'll need a client-server type of relationship to be set up here. I imagine that you would want your third JSP to act as the server.
What the third JSP will do is will sit and wait for a client to try to communicate with it. But, before you can do that, you'll first need to bind a port to your application. Ports are allocated by the Operating System and are given to requesting processes.
When trying to implement this in Java, you might want to try something like the following:
int port_number = 1080;
ServerSocket server = new ServerSocket(port_number);
In the above example, the ServerSocket is already bound to the specified port 1080. It doesn't have to be 1080 - 1080 is just an example.
Next, you will want to listen and wait for a request to come in. You can implement this step in the following:
Socket request = null;
while((request = server.accept()) == null)
{}
This will cause the server socket to keep looping until it finally receives a request. When the request comes in, it will create a new Socket object to handle that request. So, you could come back to your loop later on and continue to wait and accept requests, while a child thread handles communication using your newly created request Socket.
But, for your project, I would guess that you don't need to communicate with more than one client at a time, so it's okay if we just simply stop listening once we receive a request, I suppose.
So, now onto the client application. Here, it's a little bit different from what we had with the server. First off, instead of listening in on the port and waiting for are request, the client's socket will actively try to connect to a remote host on their port. So, if there is no server listening in on that port, then the connection will fail.
So, two things will need to be know, those are:
What's the IP Address of the server?
What port is the server listening in on?
There are short-cuts to getting the connection using the Java Socket class, but I assume that you're going to test this out on the same machine, right? If so, then you will need two separate ports for both your client and server. That's because the OS won't allow two separate processes to share the same port. Once a process binds to the port, no other process is allowed to access it until that port releases it back to the OS.
So, to make the two separate JSP's communicate on the same physical machine, you'll need both a local port for your client, and you'll need the server's port number that it's listening in on.
So, let's try the following for the client application:
int local_port = 1079;
int remote_port = 1080;
InetSocketAddress localhost = new InetSocketAddress(local_port);
Socket client = new Socket(); //The client socket is not yet bound to any ports.
client.bind(localhost); //The client socket has just requested the specified port number from the OS and should be bound to it.
String remoteHostsName = "[put something here]";
InetSocketAddress remotehost = new InetSocketAddress(InetAddress.getByName(remoteHostsName), remote_port); //Performs a DSN lookup of the specified remote host and returns an IP address with the allocated port number
client.connect(remotehost); //Connection to the remote server is being made.
That should help you along your way.
A final note should be made here. You can't actually run these two applications using the same JVM. You'll need two separate processes for client and server applications to run.

Categories