Spring Security - Token authentication with request host name - java

I have an application implemented with Spring Boot, where I use Spring Security for authentication. I already have "token based" authentication in place, where clients are required to retrieve a token, and then use that token to authenticate in subsequent requests.
I would like to enhance this so that a token could be restricted to a specific hostname, so that is can only be used for requests from that host. This is similar to what the google maps API does with its API keys, where it is possible to restrict them by IP or host name.
Here is the code I have implemented to try to retrieve the request's host name
public String getClientHostName(HttpServletRequest request) {
String hostName = null;
// get the request's IP address
String clientAddress = httpRequest.getRemoteAddr();
String xfHeader = httpRequest.getHeader("X-Forwarded-For");
if (xfHeader != null) {
clientAddress = xfHeader.split(",")[0];
}
// try to resolve the host name from the IP address
try {
InetAddress address = InetAddress.getByName(clientAddress);
hostName = address.getHostName();
} catch (UnknownHostException e) {
logger.error("Failed to get the host name from the request's remote address. ", e);
}
return hostName;
}
I have 2 issues right now:
This code does not always manage to retrieve the hostname. Sometimes it just returns the IP address. I understand this may be down to some IP spoofing check the InetAddress class does.
When testing requests from different hosts, I do not always get the IP address I am expecting. I often get the IP of another host that is forwarding the request (which I thought would be solved by checking "X-Forwarded-For"). This makes me wonder how to even retrieve the IP of the host that is the real originator of the request.
Is there a reliable way to check the host name of the originator of a request?

have you tried getting hostname by String referrer = request.getHeader("referer"); ?
Also, on client side also you can add a snippet to find out the hostname in the headers.
Or you can provide below code to be added on client side and on server you can read the value of domain which will return hostname
<input type="button" value="Register" onClick="call()"/>
<script>
function call(){
var domain=window.location.hostname;
window.open('http://<your-hostname>/register?domain='+domain,'_self');
}
</script>

Related

How do you get Client's IP address? (Spring WebFlux WebSocket)

As the title says, how do you obtain the details of the connection so to speak. Is there a way to get it through the WebSocketSession? I feel like I am missing something...
I need a way in order to ban ip addresses of bad users and also I wanted to display all users who are online on a map (like a dot on a map). I don't need help with the later I need help with getting a client's IP address.
I am using Spring's WebFlux WebSocket.
EDIT: I created a feature request : https://jira.spring.io/browse/SWF-1728
The Servlet-based WebSocketSession object does provide that information. This seems to be missing from the reactive flavor.
You should create a Spring Framework issue to request this as an enhancement.
WebSocketSession has a method called getRemoteAddress(), you can get the remote address using any of its implemetation.
you can find ip address using HttpServletRequest for example
String remoteHost = request.getRemoteHost();
String remoteAddr = request.getRemoteAddr();
if (remoteAddr.equals("0:0:0:0:0:0:0:1")) {
InetAddress localip = java.net.InetAddress.getLocalHost();
remoteAddr = localip.getHostAddress();
remoteHost = localip.getHostName();
}
I did it by
public Mono<ServerResponse> hello(ServerRequest request) {
String remoteAddr = request.remoteAddress().get().getAddress().getHostAddress());
return null;
}

Is HttpServletRequest.getRequestURL() spoofable?

The question is self explanatory, I hope. I am setting up a Spring Security enviroment with a CAS-server. Because the exact same application is deployed on the same server, but the server is accessible via different host names (.de domain, .com domain, possibly more than that) and we want to deploy the same application on test systems and the local one as well, I built a dynamic service, where the service URL is derived from request URL.
public static String makeDynamicUrlFromRequest(ServiceProperties serviceProperties, HttpServletRequest request) {
String serviceUrl = "https://backup-url.de/login";
URI uri = null;
try {
uri = new URI(request.getRequestURL().toString());
} catch (URISyntaxException e) {
logger.error("Someone tried accessing a disallowed service!", e);
}
if(uri != null){
serviceUrl = uri.getScheme() + "://" + uri.getHost() + "/login";
}
return serviceUrl;
}
Is it possible to spoof this? If it is, does an additional regex-check provide me with the necessary security against this?
#developerwjk
"If they modified the request url how would you have gotten the request?"
An HTTP server is just a program that listens on a TCP port, waits for some incoming text and writes out some text as a response. (A trivial web server can be written in like 20 lines of code.) It only sees the IP address and port of whatever connected to it. That could even be a proxy, or some other sort of middle-ware. If you don't tell the program "by the way, I reached you through the URL http://my.com/myapp/servlet" then it just doesn't know e.g. how a browser will reach it.
#Schaka
I don't know about your particular setup, but for jetty9, the result of getRequestURL is determined from the request URL in the request header, and - if the former is missing - the URL in the Host parameter. That is, if you connect to my.com and send the following request:
POST http://spoofed1.tld/myapp/servlet HTTP/1.1
Host: spoofed2.tld
(Keep in mind that the Host parameter is mandatory.)
Then getRequestURL will return http://spoofed1.tld/myapp/servlet
And if you send this:
POST /myapp/servlet HTTP/1.1
Host: spoofed2.tld
Then jetty itself will respond with
HTTP/1.1 302 Found
Location: http://spoofed2.tld/myapp/servlet
Content-Length: 0
Server: Jetty(<some version number>)
So the answer is yes, HttpServletRequest.getRequestURL() is spoofable! by modifying the request URL and/or the Host request header.

why ServletRequest.getRemoteAddr cannot be forged?

I am trying to find the client's IP. And I was told that 'request.getHeader("HTTP_X_FORWARDED_FOR")' cannot be trusted since it may be forged and I should use request.getRemoteAddr instead.(In my case it's ok to just get the proxy's IP)
So my question is:
why ServletRequest.getRemoteAddr cannot be forged?
another question:
what's the difference between HTTP_X_FORWARDED_FOR and X_FORWARDED_FOR?
If you do request.getRemoteAddr();
and if the user is behind a proxy server or accessing your web server through a load balancer then the above code will get the IP address of the proxy server or load balancer server, not the original IP address of a client.
So if
In my case it's ok to just get the proxy's IP
you are ok with this then request.getRemoteAddr(); is enough.
But in Ideal case you should try this
//is client behind something?
String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
X_FORWARDED_FOR
The X-Forwarded-For (XFF) HTTP header field is a de facto standard for identifying the originating IP address of a client connecting to a web server through an HTTP proxy or load balancer.
HTTP_X_FORWARDED_FOR
A header HTTP_X_FORWARDED_FOR is set by proxy servers to identify the ip-address of the host that is making the HTTP request through the proxy.
In short they're all the same header, just referred to differently by various implementations. For more view this : HTTP Headers: What is the difference between X-FORWARDED-FOR, X_FORWARDED_FOR and HTTP_X_FORWARDED_FOR?

Rest - how get IP address of caller

I am writing a Java Rest Web Service and need the caller's IP Address. I thought I saw this in the cookie once but now I don't see it. Is there a consistent place to get this information?
I saw one example of using an "OperationalContext" to get it but that was not in java.
Inject a HttpServletRequest into your Rest Service as such:
import javax.servlet.http.HttpServletRequest;
#GET
#Path("/yourservice")
#Produces("text/xml")
public String activate(#Context HttpServletRequest req,#Context SecurityContext context){
String ipAddressRequestCameFrom = requestContext.getRemoteAddr();
// header name is case insensitive
String xForwardedForIP = req.getHeader("X-Forwarded-For");
// if xForwardedForIP is populated use it, else return ipAddressRequestCameFrom
String ip = xForwardedForIP != null ? xForwardedForIP : ipAddressRequestCameFrom;
System.out.println("IP is "+ip);
// get the host name the client contacted. If the header `Host` is populated the `Host` header is automatically returned.
// An AWS ALB populated the Host header for you.
String hostNameRequestCameFrom = req.getServerName();
System.out.println("Host is "+hostNameRequestCameFrom);
//Also if security is enabled
Principal principal = context.getUserPrincipal();
String userName = principal.getName();
}
As #Hemant Nagpal mentions, you can also check the X-Forwarded-For header to determine the real source if a load balancer inserts this into the request.
According to this answer, the getHeader() call is case insensitive.
You can also get the servername that the client contacted. This is either the DNS name or the value set in the Host header with an OSI layer 7 load balancer can populate.
1. Example: no headers are populated
curl "http://127.0.0.1:8080/"
returns
IP is 127.0.0.1
Host is 127.0.0.1
2. Example: X-Forwarded-For and Host headers are populated
curl --header "X-Forwarded-For: 1.2.3.4" --header "Host: bla.bla.com:8443" "http://127.0.0.1:8080/"
returns
IP is 1.2.3.4
Host is bla.bla.com
I think you can get the IP through the request object.
If I'm not mistaken, request.getRemoteAddr() or so.
You could do something like this:
#WebService
public class YourService {
#Resource
WebServiceContext webServiceContext;
#WebMethod
public String myMethod() {
MessageContext messageContext = webServiceContext.getMessageContext();
HttpServletRequest request = (HttpServletRequest) messageContext.get(MessageContext.SERVLET_REQUEST);
String callerIpAddress = request.getRemoteAddr();
System.out.println("Caller IP = " + callerIpAddress);
}
}
Assuming you are making your "web service" with servlets, the rather simple method call .getRemoteAddr() on the request object will give you the callers IP address.
If your application is running on a webserver that is located behind a reverse proxy or load balancer, then that proxy can be configured to inject the requested IP address in a request header. Different reverse proxies can inject different headers. Consult the documentation for your proxy server. We listed a couple of the most used in our example below but this is by no means a complete list.
When your client uses a (forward) proxy, then it might insert headers to say what the client IP addres is. Or it might not. And the IP address inserded here might be incorrect.
This means that the value you get by calling request.getRemoteAddr() is the IP address of the immediate upstream source of the request.
As we said, there are many headers for different proxies in use, but x-forwareded-for is most likely to be inserted by a proxy.
As a last note, even if you get an IP address either from the header or from request.getRemoteAddr() it is not guarenteed to be the client IP address. e.g.: if your proxy does not include the IP address of the client then you’ll get the IP address of the proxy or load balancer. If your client works on a private network and connect to the internet via a NAT gateway, then the IP address in the HTTP request will be an address of the NAT server. Or even for a hacker it is quite easy to inject a header with a different IP address. So this means that you cannot reliably find out the IP address of the system that the request originated from.
private static final String[] IP_HEADER_CANDIDATES = {
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_X_FORWARDED_FOR",
"HTTP_X_FORWARDED",
"HTTP_X_CLUSTER_CLIENT_IP",
"HTTP_CLIENT_IP",
"HTTP_FORWARDED_FOR",
"HTTP_FORWARDED",
"HTTP_VIA",
"REMOTE_ADDR" };
public static String getClientIpAddress(HttpServletRequest request) {
for (String header : IP_HEADER_CANDIDATES) {
String ip = request.getHeader(header);
if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
}
return request.getRemoteAddr();
}

How to get the subnet ip adress from http request

I'm writing a web application, I need to do a audit log for all the actions in the application. For this purpose I need to get the IP Address of the client systems.
I'm using request.getRemoteAddr() to get the remote IP Address. But this has a problem, if the client is behind a proxy this method will give the IP of the proxy system.
When I did some search I found a header attribute called 'X-FORWARDED-FOR' in the HttpRequest object.
Can somebody tell me how exactly this header property works and how can I used this header to get the IP address of the client system.
Thank you
getRemoteIP returns the remote IP address of the user (assuming all HTTP intermediaries are well behaved wrt XFF header).
String getRemoteIP(HttpServletRequest request) {
String xff = request.getHeader("X-Forwarded-For");
if (xff != null) {
return xff.split("[\\s,]+")[0];
}
return request.getRemoteAddr();
}
The client's proxy - typically a firewall or somesuch - will populate the x-forwarded-for header with the ip it receives from its client, which is typically, but is not required to be (in the case of a user going through multiple proxies or firewalls) the ip of the user's machine.
'X-FORWARDED-FOR' is used for identifying the originating/actual IP address of a client connecting to a web server through an HTTP proxy.
You can simply use the value for this attribute to find out the originating client IP, even if it's behind a proxy.

Categories

Resources