I have a java web application, which needs to read a file from a network drive. It works perfectly when i run it on a localhost test server, as I am logged in with my windows credentials. It does not however work when deployed on a company server.
I have been trying to implement a way to send user credentials along when trying to access the file, and my current attempt is using The Java CIFS Client Library
I am basing my attempts on the code in this answer, although my code needs to read from a file instead of write to one. I am getting a NullpointerException I cannot explain.
Code:
public static void main(String[] args) {
String filePath = "[myPath]";
String USER = "domain;username:password";
try {
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(USER);
SmbFile sFile = new SmbFile(filePath, auth);
if(sFile.exists()){
InputStream stream = new SmbFileInputStream(sFile); //throws exception
}
} catch (SmbException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Error:
Exception in thread "main" java.lang.NullPointerException
at jcifs.smb.ServerMessageBlock.writeString(ServerMessageBlock.java:213)
at jcifs.smb.ServerMessageBlock.writeString(ServerMessageBlock.java:202)
at jcifs.smb.SmbComNTCreateAndX.writeBytesWireFormat(SmbComNTCreateAndX.java:170)
at jcifs.smb.AndXServerMessageBlock.writeAndXWireFormat(AndXServerMessageBlock.java:101)
at jcifs.smb.AndXServerMessageBlock.encode(AndXServerMessageBlock.java:65)
at jcifs.smb.SmbTransport.doSend(SmbTransport.java:439)
at jcifs.util.transport.Transport.sendrecv(Transport.java:67)
at jcifs.smb.SmbTransport.send(SmbTransport.java:655)
at jcifs.smb.SmbSession.send(SmbSession.java:238)
at jcifs.smb.SmbTree.send(SmbTree.java:119)
at jcifs.smb.SmbFile.send(SmbFile.java:775)
at jcifs.smb.SmbFile.open0(SmbFile.java:989)
at jcifs.smb.SmbFile.open(SmbFile.java:1006)
at jcifs.smb.SmbFileInputStream.<init>(SmbFileInputStream.java:73)
at jcifs.smb.SmbFileInputStream.<init>(SmbFileInputStream.java:65)
at Test.main(Test.java:45)
The user credentials are accepted. I've tried both valid and invalid credentials, and the invalid ones gives user identification errors.
The exception is thrown when creating the inputstream, which normally would make me think that the parameter sFile object, would be null, or have a null field. I do not know which field this might be. Debugging shows that isExists = true. The URL is also valid. Here is a screenshot of my sFile object from the debugger:
What am i missing here? Why do i get a nullpointerexception?
After traversing the source code, I found that the unc variable was the one causing the NullPointerException. Long story short, my struggle was caused by me not following the standard url pattern of smb, and the jcifs library failing to give me information about this. The rules can be found here (right after the initial import statements). Here is a selection:
SMB URL Examples
smb://users-nyc;miallen:mypass#angus/tmp/
This URL references a share called tmp on the server angus as user miallen
who's password is mypass.
smb://Administrator:P%40ss#msmith1/c/WINDOWS/Desktop/foo.txt
A relativly sophisticated example that references a file msmith1's desktop as user Administrator. Notice the '#' is URL encoded with the '%40' hexcode escape.
smb://angus/
This references only a server. The behavior of some methods is different in this context(e.g. you cannot delete a server) however as you might expect the list method will list the available shares on this server.
smb://myworkgroup/
This syntactically is identical to the above example. However if myworkgroup happends to be a workgroup(which is indeed suggested by the name) the list method will return a list of servers that have registered themselves as members of myworkgroup.
smb:// Just as smb://server/ lists shares and smb://workgroup/ lists servers, the smb:// URL lists all available workgroups on a netbios LAN. Again, in this context many methods are not valid and return default values(e.g. isHidden will always return false).
smb://angus.foo.net/d/jcifs/pipes.doc
The server name may also be a DNS name as it is in this example. See Setting Name Resolution Properties for details.
smb://192.168.1.15/ADMIN$/
The server name may also be an IP address. See Setting Name Resolution Properties for details.
smb://domain;username:password#server/share/path/to/file.txt
A prototypical example that uses all the fields.
smb://myworkgroup/angus/ <-- ILLEGAL
Despite the hierarchial relationship between workgroups, servers, and filesystems this example is not valid.
smb://server/share/path/to/dir <-- ILLEGAL
URLs that represent workgroups, servers, shares, or directories require a trailing slash '/'.
smb://MYGROUP/?SERVER=192.168.10.15
SMB URLs support some query string parameters. In this example the SERVER parameter is used to override the server name service lookup to contact the server 192.168.10.15 (presumably known to be a master browser) for the server list in workgroup MYGROUP.
Related
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.
I have set up a local proxy server for request logging but my java code ignores it and connects directly (Windows XP, JDK 1.7). Web browsers work with it. So I wrote test code for discussion that seems to connect directly even if a (bogus) proxy is specified. With the bogus proxy, I would expect connection failure but the code succeeds, connecting directly:
System.setProperty("http.proxyHost", "localhost");
System.setProperty("http.proxyPort", "12345");
System.setProperty("http.nonProxyHosts", "noNonProxyHost.com");
URL url = new URL("http://docs.oracle.com/javase/7/docs/technotes/guides/net/proxies.html");
InputStream in = url.openStream();
System.out.println("Connection via bogus proxy succeeded");
The code is run as standalone Java, no Maven, no applet, no container. I have a direct internet connection.
In your case using java.net.URL(), if the proxy server cannot be reached at http.proxyHost and http.proxyPort then it simply falls back and tries to do a direct connect. If that succeeds, you'll see no exception thrown which is why your code works without error. You should see a pause while it tries to find the proxy though.
This sample code below happily fetches the URL and displays it, without error, even when run with bogus proxy settings. -Dhttp.proxyHost=bogus -Dhttp.proxyPort=2345 but will talk to my local proxy localhost port 8888 if set correctly
import java.io.*;
import java.net.URL;
import java.util.*;
public class URLClient {
private static String sUrl = "http://www.apache.org/";
public static void main(String[] args) {
try {
URL url = new URL(sUrl);
InputStream is = url.openStream();
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
String output = s.hasNext() ? s.next() : "";
System.out.println(output);
} catch(Throwable e) {
System.err.println("exception");
}
}
}
The problem I was originally having with http.proxyHost and http.proxyPort being ignored (Google led me to your question) was that those settings are completely ignored by apache.commons.httpClient because it uses its own sockets, as described here.
http://cephas.net/blog/2007/11/14/java-commons-http-client-and-http-proxies/
I have faced a similar problem recently. First of all, one part of the above answer from Daemon42 explains pretty well, why the bogus proxy server didn't lead to a failure of the program:
if the proxy server cannot be reached at http.proxyHost and http.proxyPort then it simply falls back and tries to do a direct connect. If that succeeds, you'll see no exception thrown which is why your code works without error. You should see a pause while it tries to find the proxy though.
Still, your actual question was, why the proxy server configured via the operating system is not used by the Java application. As stated in the Oracle documentation (https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html), the system proxy settings are not evaluated by Java by default. To do so, you have to set the value of the system property "java.net.useSystemProxies" to the value "true".
You can set that system property on the command line, or you can edit the JRE installation file jre/lib/net.properties, that way you have to change it only once on a given system.
This is my first question at Stack Overflow, so feel free to tell me if I do something wrong :)
I'm working on a project involving EJB and JBoss 4.2.3.GA. In a point, we try to access every node of the cluster, locating an EJB and returning it.
This is the piece of code that does the JNDI lookup:
public static <I> I getCache(Class<I> i, String clusterNode) {
ServiceLocator serviceLocator = ServiceLocator.getInstance();
String jndi = serviceLocator.getRemoteJNDIName(i);
Properties props = new Properties();
props.setProperty(Context.PROVIDER_URL, "jnp://" + clusterNode + ":"
+ jndiPort);
props.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming");
props.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
Object result = null;
try {
InitialContext ctx = new InitialContext(props);
result = ctx.lookup(jndi);
} catch (NamingException e) {
return null;
}
return (I) result;
}
Here:
clusterNode is a simple string with the IP address or the dns name of the node. E.g: "192.168.2.65" or "cluster1".
getRemoteJNDIName returns an String such as this: "MyEARName/MyEJBName/remote"
The problem is that when I call this method with, for example, "127.0.0.1" it works fine. Also, if I call it with an existing and working IP address where a server is up and running, it's OK too.
However if I call the method with a non-existing or non-working address or dns name, instead of throwing the NamingException, it returns the EJB in my own machine. Therefore, I don't know wether the node is up or not.
I guess there may be better ways to do it. I'd like to hear about them, but we cannot make "big" changes to the product due to it being in production for a few years by now.
That's it. Thank you in anticipation and best regards.
However if I call the method with a non-existing or non-working
address or dns name, instead of throwing the NamingException, it
returns the EJB in my own machine
I think this behavior can be explained if you have automatic naming discovery. When the Contex.PROVIDER_URL is not specified or the nodes in the list are not reachable (which is your case) the client is allowed to search in the network for available JNDI services.
However this only works under certain conditions, some of them:all clusters node running in ALL mode, all nodes located in the same subnet.
You can disable this behavior setting the InitialContext property jnp.disableDiscovery=true.
I guess there may be better ways to do it
According to the code, you are not catching the object polled from the JNDI, this implies that every time you need to execute a service, a new lookup (wich is a time consuming operation) has to be done. The ServiceLocator pattern suggests caching the lookup result for improving performance.
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.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to check if internet connection is present in java?
I want to see if anyone has an easy way of detecting if there is an internet connection when using Java. The current app used the "InternetGetConnectedState" method in the WinInit DLL for windows, but my app needs to be cross-platform for mac operation and this way will not work. I do not know JNI at all either to use DLLs in Java and it became frustrating fast.
Only ways I could think of were tring to open a URL connection to a website and if that fails, return false. My other way is below, but I didn't know if this was generally stable. If I unplug my network cable i do get an UnknownHostException when trying to create the InetAddress. Otherwise if the cable is connected I get a valid InetAddress object. I didnt test the below code on a mac yet.
Thanks for any examples or advice you can provide.
UPDATE: Final code block is at the bottom. I decided to take the advice of an HTTP request (in this case Google). Its simple and sends a request to the site for data to be returned. If I cannot get any content from the connection, there is no internet.
public static boolean isInternetReachable()
{
try {
InetAddress address = InetAddress.getByName("java.sun.com");
if(address == null)
{
return false;
}
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
return true;
}
Final Code Block:
//checks for connection to the internet through dummy request
public static boolean isInternetReachable()
{
try {
//make a URL to a known source
URL url = new URL("http://www.google.com");
//open a connection to that source
HttpURLConnection urlConnect = (HttpURLConnection)url.openConnection();
//trying to retrieve data from the source. If there
//is no connection, this line will fail
Object objData = urlConnect.getContent();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
return true;
}
That's a perfectly reasonable approach to solving the problem. The bad thing is that you are really testing DNS rather than testing the whole network, but in practice you can often get by with treating those as equivalent.
The other thing to remember, is that you will need to set a system property to turn off dns caching in the java runtime. Otherwise it may continue to report that the network is up based upon cached data (even though it is down).
Another approach would be to actually open an HTTP request to some network address such as this every so often.
I must add that although the final code block given above is good, it has one flaw - it is possible for it to take a very long time to contact the address specified, but the address is still reachable.
In my instance when testing with one address the method would return true, but take 10 seconds or longer to get a response. In this case the server was reachable, but not for any useful purposes since the connection was so slow. This occurs because the default timeout for HttpURLConnection is 0, or infinite.
For this reason I'd recommend you do the checking off the UI thread, and add urlConnect.setConnectTimeout(1000); before calling urlConnect.getContent();
This way you know the address is reachable, and that it won't take 5 years to download a 10k file.
(You might of course want to change the the timeout time to suit your needs)
Also I'd recommend not checking a generic address (google.com etc) unless your program generally accesses more than a few domains. If you're just accessing one or two then check that domain.
Note that it could return false if the java.sun.com is not responding! In this case you should check another site to be certain.
Haven't tested this, but I suggest looking at java.net.NetworkInterface.getNetworkInterfaces(). This returns an Enumeration of all network interfaces on the machine, or null if there are none.
I'm not sure if it's safe to assume that a non-null response ensures a valid network connection -- depending on your needs, you may or may not need to filter out loopback addresses (which I think you could do with java.net.NetworkInterface.getInetAddresses() on each returned NetworkInterface, and then calling InetAddress.isLoopbackAddress() on each one.)
The only way to be CERTAIN that you can reach a given service, is to do a dummy request to that service. Pings may be blocked by firewalls. Some server may be reachable, others not. If you need to talk to a webservice, have a static page to return for these requests.
Also, remember to ask the user before trying to reach out.
A problem with the first solution is that InetAddress has a cache, so, when you lose the connection for the next few invocation the name is resolved via the java cache.
With the URL connection aproach you have the problem that you use getContent that should fetch html so there you have data consumption. If the invocations are done very often that could be a problem (more so if you dont have an unlimited data plan on the device running the software).
I think the best solution would be to do a TCP connection to the 80 port an close it inmediatly after a successfull connection. That would behave as the final code but would have much less traffic.
The question doesn't really have a meaning. There is no such thing as a 'connection to the Internet'. You have to try to create one. The Windows API referred to only tells you whether your modem is dialled in or not, and I've seen it actually cause dial-ins, which is not exactly the idea. Not that I've had a dial-in for the last 8 years or so.