I have written an Android game. Now, I want to connect the marks of the players.
The code below is the client side program, which send their names, countries and marks to the server. Basically, I use ':' to separate the information.
However, hackers can know my server ip and port by decompiling .apk file. Then, they can create a basic java application to access my server. They could send me "hacker:badserver:123456789". Finally, the top mark becomes "123456789".
Is it more secure method to connect user's data?
Socket client = new Socket(ip, port);
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeUTF("Hacker:HK:3910");
Hackers do not need the APK to ascertain your server's IP. They could simply use a proxy tool such as Fiddler or packet sniffer.
During my brief stint as a game developer (working on the network multiplayer features), you learn to just assume that cheaters and hackers have the source code to your game. Then you design your protocols around that assumption.
So how do you guard your server from being hacked by a rogue client or from cheating? You really can't, but here's some things that make it extremely hard.
Require the user to "log in" (name/password) from the game before allowing it to post anything to your server. If you catch someone cheating, ban their account from playing the game by disallowing them to login. Use a "ticket" or auth token in the header of each HTTP request for the server to validate. If you ban someone, it will be too annoying for them to get a new account to try again.
HTTPS instead of HTTP. Technically a hacker could beat this by simply changing his rogue client to post with HTTPS as well. He could reverse engineer the protocol by debugging your APK. But now you've made it so much harder for him to discover what the protocol is to begin with. With HTTPS, the hacker can't use an easy tool such as Fiddler to observe the network traffic.
Be wary of debug spew in release builds. Make sure your "Log" statements related to anything protocol wise are in debug builds only, never release builds.
Obfuscate everything and use something like HMAC to "sign" every message with a key. This technically isn't security, but will make it A LOT harder for anyone who breaks the encryption to continue. Something like the following:
Example
code:
name = BASE64("Hacker");
messagetype = ROT13("HK");
score = 3910;
tosend = name + ":" + messagetype + ":" + Integer.ToString(score ^ 0xAAAAAAAA) + ":";
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
mac.init(Your_Secret_Key);
byte[] rawHmac = mac.doFinal(data.getBytes());
tosend += Encoding.EncodeBase64(rawHmac);
Where "Your_Secrey_Key" is encoded somewhere in your source code, and only decrypted at runtime.
The above will generate a string resembling the following:
SGFja2Vy:UX:AAAAA5EC:blahblahxyxunreadable
The corresponding server code will decode the message and validate with the secret key before accepting.
Is this perfect security? No.
Should I build an ecommerce solution with these techniques? Definitely not.
Do these techniques ward off 99% of the casual hacker that might be tempted to do cheat? Absolutely.
Does it beat the most determined of hackers that are out to win the high score at all costs? Maybe not. But the "effort to reward" ratio goes way up. It may not be worth their time.
Hope this helps.
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 want to read the DNS requests made by the browser, then extract the name of website from the DNS UDP datagram.
I can capture the datagram, then i used getData() to find the labels of the website, but i don't get clear data.
public static void main(String args[]) throws Exception
{
DatagramSocket serverSocket = new DatagramSocket(53);
byte[] receiveData = new byte[512];
while(true)
{
DatagramPacket receivePacket = new DatagramPacket(receiveData,
receiveData.length);
serverSocket.receive(receivePacket);
byte telegramContent[] = receivePacket.getData();
String sentence = new String( telegramContent);
System.out.println("RECEIVED: " + sentence);
the result i had for www.google.com
my question is how to get the full name of the website clear like www.google.com
You will need to read RFC1034 and 1035. The DNS protocol is far more complicated than that, you will need far more code to parse it.
First, why not using a specific Java DNS library that surely exists and which can both generate and parse DNS packets? Even if you do not want to use it, reading its source may help you understand how the parse is happening.
Second, and if you really want to to it yourself, you will have to understand the generic format of a DNS packet (with a structure separating the question, the authority part, the answer part, the additional part), see section 4 for DNS packet structure, and then for names specifically, you need to understand "DNS compression", see section 4.1.4 of same RFC.
A name does not appear like this in a DNS packet. Each label (the string between two dots) is encoded on the wire with its length and then the label EXCEPT in some cases where pointers are used to point inside another part of the DNS packet in order never to repeat the same label or sequence of labels.
Finally you will need to not just try to grab any string in the DNS packet. A reply can have a lot of other content, like if you get a CNAME reply you will get two "strings", that is two domain names, the owner of the record and the associated RDATA. You will need to follow these CNAME records (if you want to find out the first name requested) and then parse replies of A and AAAA requests. You obviously also need to take into account negative replies (NXDOMAIN) that can happen for any name queried.
As for
i want to read the DNS requests made by the browser
, there is a far easier solution.
Install a recursive nameserver, like unbound and make sure your browser is pointed to it. Then you will easily see all hostnames in the nameserver logfile.
Also another solution with recent browser versions implementing DoH (DNS over HTTPS): again install a nameserver speaking DoH and configure your browser to point to it (see https://www.internetsociety.org/blog/2018/12/dns-privacy-support-in-mozilla-firefox/ for example for Firefox).
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.
I'm trying to make a port of a chat program a friend of mine made with lacewing and multimedia fusion 2 for android device.
I've managed to create a socket connecting to the listening socket of the server successfully, but I cannot seem to be able to send data to login and enter the chat. The login for now just requires a name, but even if I send a String of data, the server doesn't seem to reply or accept that data to get me over the channel.
I know I could easily port this with other way like using the NDK of the multimedia fusion 2 exporter, but I just want to figure out how this works
PS: I'm using Java and libgdx for the development
You need to read the liblacewing relay protocol:
https://github.com/udp/lacewing/blob/0.2.x/relay/current_spec.txt
On initial connection, you have to send byte 0 to identify that you are not an HTTP client. After this, you can exchange normal protocol messages.
The first message you need to send is the connection request (which may be denied by the server with a deny message). This would be:
byte 0 (2.1.0 request)
(1.2 size)
byte 0 (2.1.0.0 connection request)
string "revision 3" (2.1.0.0 connection request -> version)
When the server responds with response 0 (2.2.0.0 Connect), you then have to set a name before you may join any channels. This is done with message 2.1.0.1 SetName, which is the same structure as above but instead of 2.1.0.0's byte 0, it is 2.1.0.1's byte 1, followed by the name as a string instead of the protocol version.
The server should then respond with 2.2.0.1 SetName, assuming it accepted your name change request. You should process this message in case the server gave you a different name than you requested. Finally, once you have a name, you can join a channel with 2.1.0.2 JoinChannel. The flags you specify here will be used if the channel doesn't exist yet (e.g. nobody is in the chat yet) - these should match the ones in the MMF2 project file. The name should also match.
After all that, you're still not done! You have to process more messages, etc. it's almost like writing the RelayClient class yourself. It's a tough task, but with the protocol specification in hand you should be able to work it all out.
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.