Unable to understand JWT and Java (Spring Boot).Is this application secure? - angularjs

I'm following a Udemy course in attempt to build my first Spring Boot application. Rather than using Spring Security, they are using JJWT to implement stateless authentication.
The front end is in Angular, and because it runs on it's own server, CORS is used to open everything up so that the Angular app can hit the Java backend API.
I'm worried that this will open the door to CSRF or other security holes.
After some digging I found similar code to what the Udemy course is using here, but I don't know enough about security to know if it's enough.
The code in question is as follows:
Filter:
public class JwtFilter extends GenericFilterBean {
#Override
public void doFilter(final ServletRequest req,
final ServletResponse res,
final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
throw new ServletException("Missing or invalid Authorization header.");
}
final String token = authHeader.substring(7); // The part after "Bearer "
try {
final Claims claims = Jwts.parser().setSigningKey("secretkey")
.parseClaimsJws(token).getBody();
request.setAttribute("claims", claims);
}
catch (final SignatureException e) {
throw new ServletException("Invalid token.");
}
chain.doFilter(req, res);
}
Cors Config:
#Configuration
public class CorsConfig {
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); //usually want this
config.addAllowedOrigin("*");//not sure if secure?
config.addAllowedHeader("*");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
Filter added to main application class:
#SpringBootApplication
public class BackendApplication {
#Bean
public FilterRegistrationBean jwtFilter() {
final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter((Filter) new JwtFilter());
registrationBean.addUrlPatterns("/rest/*");
return registrationBean;
}
public static void main(String[] args) {
SpringApplication.run(BackendApplication.class, args);
}
}
So is this sort of setup secure enough to be used in production code, or is a more robust solution needed?
Or is there a better way entirely to do stateless authentication? If they were both running on the same domain I wouldn't need CORS right?

Do not use JWT for security purposes. JWT contains information other than random keys which should not be present in the authorisation header. Although we can sign a JWT token, yet we should avoid it. Instead save the extra information in the database and query it whenever you receive the token at server side to check authenticity of the token. Also, the algorithm type none can prove to be a security disaster if exploited by an attacker. Use simple random string as token. Fairly simple and more secure. Confidential information resides in database and not in the token and we all know that databases are very secure.

Related

Customizing LockedException of spring security to send new status code to angular.js

I am using spring 4 + hibernate 4 + spring security (RESTFull Webservice API) and angular at front. (Very new to this all).
My failure handler is as follows:
#Component
public class AuthFailure extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
If my request fails I get 401 response which is as expected. Now I want to add AccountLocked and CredentialsExpiredException functionality. I am setting "false" appropriately when I return "User". Again I do get response with status as 401.
return new User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, AuthorityUtils.NO_AUTHORITIES);
As I always get 401 and I do not get any user object inside response(Or may be don't know how to get it), at front end I am unable to find whether it's due to bad credentials or account locked or credentials expired as I want to redirect to another page. I also tried to catch exceptions and tried to forward different statuses but does not seems to hit this code. I always get 401.
#ExceptionHandler(LockedException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public ModelAndView handleLockedException(Exception e) {
logger.error("Exception occurred => " + e.getMessage());
return new ErrorResponse(HttpStatus.BAD_REQUEST.value(), e.getMessage(), "Spring security exception").asModelAndView();
}
Please help me - how shall I handle at angular end so that I can redirect to appropriate page?
I think solution was very simple, just did not pay attention to AuthFailure method parameters. Modified my AuthFailuar as follows:
#Component
public class AuthFailure extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
if (exception instanceof LockedException) {
response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
}else if {
....
} else {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
Now as I have given different status codes I can easily differentiate at the front. Hope for somebody this would be helpful.

Is Tyrus nessary to implement Java WebSocket authentication?

Although there's very similar post, it is unanswered.
My JavaFX app with WebSocket will
send user id、password to server
keep the session to let user do something like personal data management.
Learning from
Oracle WebSocket、
Tyrus 8.14 Client HTTP Authentication
I have:
#ClientEndPoint
public class loginEndPoint {
final ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().build();
public static void main(String [] args) {
AuthConfig authConfig = AuthConfig.Builder.create().disableBasicAuth().build();
Credentials credentials = new Credentials("ws_user", "password");
client.getProperties().put(ClientProperties.AUTH_CONFIG, authConfig);
client.getProperties().put(ClientProperties.CREDENTIALS, credentials);
client.connectToServer(new Endpoint() {
#Override
public void onOpen(Session session, EndpointConfig config) {
try {
session.addMessageHandler((MessageHandler.Whole<String>) (String message) -> {
System.out.println("Received message: "+message);
messageLatch.countDown();
});
//let user do some data management
} catch (IOException e) {
System.out.println("Connect Fail.");
}
}
}, cec, new URI("ws://localhost/myApp/login"));
}
}
Is these code right to do the authentication? And where could I do the server side authentication on #ServerEndPoint?
#ServerEndpoint
public class loginServerEndPoint {
}
Thanks for help.
No, it is not necessary to use Tyrus as a server implementation.
On the server-side you should secure WebSocket in exactly the same way as you secure servlet or jsp in your servlet container, which can be slightly different from container to container (mapping users to roles).
Look at authentication example
Note that this example shows up how to make authenticated WebSocket request handshake with BASIC auth scheme, but your client code disables it explicitly, so probably you want to use DIGEST scheme.

Enforcing Basic Authentication with RestEasy & TJWS

We use Resteasy to communicate between multiple backend servers & we want to lock this down so not just anyone can attach a client or browser to the restlet server.
We're using Resteasy 3.04 and as our backend services are numerous but very light-weight an embeddded TJWS webserver.
Example Server code:
public class RestEasySSLBasicAuthenticationServer {
static TJWSEmbeddedJaxrsServer webServer;
static class BasicAthenticationSecurityDomain implements SecurityDomain {
#Override
public Principal authenticate(String aUsername, String aPassword) throws SecurityException {
System.out.println("User:" + aUsername + " Password" + aPassword);
if (aPassword.equals("password") == false) {
throw new SecurityException("Access denied to user " + aUsername);
}
return null;
}
#Override
public boolean isUserInRoll(Principal aUsername, String aRole) {
// No role based checks so return true
return true;
}
}
public static void main(String[] args) throws Exception {
// Create embedded TJWS web server
webServer = new TJWSEmbeddedJaxrsServer();
// Set up SSL connections on server
webServer.setSSLPort(8081);
webServer.setSSLKeyStoreFile("K:\\source\\RestEasyTest\\server_localhost.jks");
webServer.setSSLKeyStorePass("krypton");
webServer.setSSLKeyStoreType("JKS");
// Add basic HTTP authentication to the server
webServer.setSecurityDomain( new BasicAthenticationSecurityDomain() );
// Add the restlet resource
webServer.getDeployment().getActualResourceClasses().add(PlayerResource.class);
// Start the web server
webServer.start();
// Run until user presses a key
System.out.print("Web server started. Press a key to stop...");
System.in.read();
// Stop the web server
webServer.stop();
}
}
Example client code:
public class RestEasySSLBasicAuthenticationClient {
public static void main(String[] args) throws Exception {
// Set up the keystore
System.setProperty("javax.net.ssl.keyStore", "K:\\source\\RestEasyTest\\client_localhost.jks");
System.setProperty("javax.net.ssl.keyStoreType", "JKS");
System.setProperty("javax.net.ssl.keyStorePassword", "krypton");
// Create a new Restlet client
Client restletClient = ClientBuilder.newClient();
// *** Even WITHOUT these credentitials we can access the restlet
// restletClient.register(new BasicAuthentication("username", "password"));
// Set up the restlet request target.
WebTarget request = restletClient.target("https://localhost:8081/player/{id}");
request = request.resolveTemplate("id", Long.valueOf(1));
// Build the restlet request
Invocation invocation = request.request("application/xml").buildGet();
// Call the restlet and get returned object
Player result = invocation.invoke( Player.class );
System.out.println(result.toString());
}
}
Using the test client and a registered authentication filter works and as expected I can a 401 access error if I get the password incorrect.
However if no authentication is registered at the client then the server never calls the SecurityDomain check and access is allowed.
How do I enforce a login at the server?
You can ensure all users are authenticated by enabling security on the embedded TJWS web server.
webServer.getDeployment().setSecurityEnabled(true);

NoClassDefFoundError: javax.naming.directory.InitialDirContext is a restricted class. Using CCS (GCM) in Google App Engine

Im trying to implement google's Cloud Connection Server with Google App Engine following this tutorial -
Implementing an XMPP-based App Server. I copied latest smack jars from http://www.igniterealtime.org/projects/smack/ (smack.jar and smackx.jar), put them in WEB-INF/lib and added them to the classpath (im using eclipse).
In the code sample in the first link i posted, the XMPPConnection is initiated in a 'main' method. Since this is not really suitable to GAE i created a ServletContextListener and added it to web.xml.
public class GCMContextListener implements ServletContextListener {
private static final String GCM_SENDER_ID = "*GCM_SENDER_ID*";
private static final String API_KEY = "*API_KEY*";
private SmackCcsClient ccsClient;
public GCMContextListener() {
}
#Override
public void contextInitialized(ServletContextEvent arg0) {
final String userName = GCM_SENDER_ID + "#gcm.googleapis.com";
final String password = API_KEY;
ccsClient = new SmackCcsClient();
try {
ccsClient.connect(userName, password);
} catch (XMPPException e) {
e.printStackTrace();
}
}
#Override
public void contextDestroyed(ServletContextEvent arg0) {
try {
ccsClient.disconnect();
} catch (XMPPException e) {
e.printStackTrace();
}
}
}
web.xml
<web-app>
<listener>
<listener-class>com.myserver.bootstrap.GCMContextListener</listener-class>
</listener>
</web-app>
Now, when i start the GAE server i get the following exception :
java.lang.NoClassDefFoundError: javax.naming.directory.InitialDirContext is a restricted class. Please see the Google App Engine developer's guide for more details.
i searched the "Google App Engine developer's guide for more details" but couldnt find anything about this. can you please help me ?
Google App Engine restricts access to certain JRE classes. In fact they published a whitelist that shows you which classes are useable. It seems to me that the Smack library might require some reference to a directory context (maybe to create the XMPP messages?) and that is why your servlet causes this exception. The javax.naming.directory is not in the whitelist.
I'm currently working on setting up a GCM Server as well. It seems to me that you need to read through the example and see what that main method is doing. What I see is a connection to the GCM server:
try {
ccsClient.connect(userName, password);
} catch (XMPPException e) {
e.printStackTrace();
}
Then a downstream message being sent to a device:
// Send a sample hello downstream message to a device.
String toRegId = "RegistrationIdOfTheTargetDevice";
String messageId = ccsClient.getRandomMessageId();
Map<String, String> payload = new HashMap<String, String>();
payload.put("Hello", "World");
payload.put("CCS", "Dummy Message");
payload.put("EmbeddedMessageId", messageId);
String collapseKey = "sample";
Long timeToLive = 10000L;
Boolean delayWhileIdle = true;
ccsClient.send(createJsonMessage(toRegId, messageId, payload, collapseKey,
timeToLive, delayWhileIdle));
}
These operations would be completed at some point during your application's lifecycle, so your servlet should support them by providing the methods the example is implementing, such as the connect method that appears in the first piece of code that I pasted here. It's implementation is in the example at line 235 if I'm not mistaken.
As the documentation says, the 3rd party application server, which is what you're trying to implement using GAE, should be:
Able to communicate with your client.
Able to fire off properly formatted requests to the GCM server.
Able to handle requests and resend them as needed, using exponential back-off.
Able to store the API key and client registration IDs. The API key is included in the header of POST requests that send messages.
Able to store the API key and client registration IDs.
Able to generate message IDs to uniquely identify each message it sends.

GAE and GWT - response.sendRedirect is not redirect

I am creating an application that needs an image upload function. I have been following this tutorial and the App Engine Documentation.
The image is uploaded correctly, and the server is redirected to the doPost function of the FileUpload HttpServlet. I can read the blob key from the request and save it in the datastore.
My problem is sending a response back to the client. Everything I've seen points to using the response.sendRedirect function, but that has not been successful yet.
public class FileUpload extends HttpServlet
{
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
Map<String, List<BlobKey>> blobs = blobstoreService.getUploads(request);
BlobKey blobKey = blobs.get("picFileUpload").get(0);
ShipHull shipHull = new ShipHull();
shipHull.setShipHullName(request.getParameter("hullNameText"));
shipHull.setShipImageURL("/shipbuilder/blobService?blob-key=" + blobKey.getKeyString());
PersistenceManager pm = PMF.get().getPersistenceManager();
try
{
pm.makePersistent(shipHull);
}
catch (Exception e)
{
String hi = "hello";
}
finally
{
pm.close();
}
Boolean test = response.isCommitted();
response.sendRedirect("/shipbuilder/FileUpload?gwt.codesvr=127.0.0.1:9997&shipHullName=" + shipHull.getShipHullName());
test = response.isCommitted();
return;
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
{
String id = req.getParameter("shipHullName");
resp.setHeader("Content-Type", "text/html");
resp.getWriter().println(id);
}
}
I am trying to redirect the client back to the doGet in this same servlet. I have tried this and without the gwt.codesvr=127.0.0.1:9997 and the shipHullName=" + shipHull.getShipHullName()) but the doGet function is never reached. I have also tried https://www.google.com.
This is all being done on a development server (haven't tried on the production server yet).
If you have any other methods of returning the status of the image save (like if a filename is already taken), I wouldn't mind trying something different.
Thanks!
Can you try just putting a string for success/failure into resp object.
public void doPost( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException
{
try{
processFileUploads(req)
resp.getWriter().print( "Success" );
}{
catch(Exception e){
resp.getWriter().println( "Unable to upload the file - Upload Failed" );
}
}
I have figured out the problem. I guess I was having the same problem as this post.
I had to click on the GWT Development Mode toolbox icon and add the webserver "ammo-box" (Name of my computer" and Code Server as "127.0.0.1". When I directed my browser to that development link, it all worked, even the answer you gave SSR. This was a domain switching problem.
Thanks for the help.

Resources