Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

springSecurityService.principal returns Null when deployed as a WAR in tomcat 8.5

Preamble

I'm designing an API gateway for a Grails microservice federation. This issue seems related to a bunch of issues already filed in this repository but nothing provides a solution.

Versions and Configurations

Grails: 3.2.2

Tomcat: 8.5

Plugin versions:

compile 'org.grails.plugins:spring-security-core:3.1.2'
compile "org.grails.plugins:spring-security-rest:2.0.0.M2"

I'm using spring security rest plugin for only token authentication. I'm doing the authorization part myself by returning ROLE_NO_ROLES for all the users in getAuthorities(). I intercept all the requests and authorize the access based on my own authorization schema stored in DB.

Problem:

With these configurations and strategy, my code works as desired when I run it on my local system. When I deploy it on a server as a war file in tomcat, it works fine for all the requests to the gateway, i.e., for all requests of the pattern /umm/controller/action. Spring security context is there and the user is evaluated perfectly.

When I try to call other microservices by redirection with requests of the form /umm/microservice/controller/action, springSecurityService.getCurrentUser() and springSecurityService?.principal?.username start to return null. Although my token gets evaluated perfectly, yet I'm not getting any security context.

For details, have a look on this issue. The details for reproducing the bug are also provided in the aforementioned issue. The whole project is available here.

Update: May 19, 2017

I tried deploying my war in a Tomcat on my local machine. This question and this question provide following solutions.

  • disabling tomcat cache
  • setting grails.plugin.springsecurity.sch.strategyName = org.springframework.security.core.context.SecurityContextHolder.MODE_INHERITABLETHREADLOCAL

Nothing seems to work so far. SecurityContextHolder is returning null anyway. All the user retrieving functions of SpringSecurityService viz. getCurrentUser(), getPrincipal(), getAuthentication() and loadCurrentUser() return null.

Update: May 23, 2017

To narrow down the problem, I executed the standalone war using

java -Dgrails.env=prod -jar build/libs/mywar-0.1.war

Now for any non-umm request, I get a 404, page not found. I think the problem is with the production environment. The app works completely fine in the development.

Also tried grails run-app which works fine. To rule out the problem with the production environment, I created the war using grails dev war but to no avail. Nothing works so far for the war.

Update: May 25, 2017

I should probably ask this http://security.stackexchange.com but for the record, I'm asking it here too.

The answer provided by me below contains a workaround fix. The mechanism by which the fix works is explained in the answer. My question is:

  1. Does this approach introduce any vulnerability or loophole in the security system?
  2. Is this authorization schema safe or needs to be revised?
  3. I'm authenticating through the plugin but authorizing myself. Can somebody bypass the security filters and hit the authorization interceptor directly? Because if someone can do that, he will only have to give me an admin's username in the same format as the token does and he'll have access to everything.
like image 427
Saqib Ahmed Avatar asked May 16 '17 11:05

Saqib Ahmed


1 Answers

Workaround

I got a workaround fix to the problem. Since I needed only the username, I grabbed the rest token in my interceptor, decoded it and extracted the username out of it.

Here goes:

def extractUsername(def token){
    Base64 coder = new Base64()
    def tok = token - "Bearer "
    def principal = tok.tokenize(".")
    def dec = coder.decode(principal[1])
    def sub = new String(dec)
    def user = sub.tokenize(",")
    def username=user[1].tokenize(":")
    username = username[1]-"\""
    return username-"\""
}

It worked for me because I didn't need to check the springSecurityService.Principal object. Had it been the case, I wouldn't have been able to get the username. springSecurityService.Principal and springSecurityService.getCurrentUser() are still returning null. The issue is not resolved yet. I'm answering because I didn't get even a single comment despite having a bounty. Answer to the original question is still welcome if anybody can explain why spring-security-plugin is behaving this way.

Edit: May 25, 2017

The workaround I used is based on the structure of the token and the fact that username is embedded in the token and token is simply base64 encoded.

This is the original token generated by spring security REST plugin for grails:

eyJhbGciOiJIUzI1NiJ9.eyJwcmluY2lwYWwiOiJINHNJQUFBQUFBQUFBSlZTUDBcL2JRQlJcL0RrRkJpbWdCcVpVNjBLVjBxeHlKanBsSVNwRXFLNmthc2xDcDZHSVwvM0lQem5iazdrMlNwTXBXQkFVU0xWSld2d0RlQmhROVF0VU5YNXE1OVp3aE91eUJ1c2ZYdTU5K1wvNTdNcm1EWWFYc2FhY1dIOFZHUXhsNzVKTlpleHdURFQzQTc5ektDTzBPYUl0UnpZcFFsY0g2OEVYZ0FsSGxsWUNMYlpIcXNKSnVOYXU3ZU5vYTBQTkN3ckhkOHdibW1XWUZcL3BIZitXTzFRYVwveEVvcUwzdkphaHN3RHdMUTVWSjIxSnlkWkJ5amRFR3pCV3pRSVU3YnZRb3BCdVVsak5oSnFFVmxLd25NQXFneWpMN1VaRXFSMlBoNGJYWnpISlI2NkN0QnpDVE1tUEkzWDlKT3RaWmRcL2ZPcHFRRXVcL0FKeW9QVW8wUGRQWGRRM1wvSDRUU1VFcGVaS21xV3VURlRFdDdnVEpcLzdSNHZIbDRlbW9Xd0tnVGw3Y1wvVTB4ZjlLQTBmbUhQMFwvem9yM1F3dU1KNndXc1Brakp6WHpCdks3UktmXC80OXZiTHlkWCsreWxTZG9qWDk5XC9IMHNwTmM4T21TbEttbVZVVE95TGFmdG05RTNuamJ2THhGb1oraHllcFFQcWpwTVhvVnFJZ3ByaGxyY1M0Ynd1ejc5ckI2bWFydmVtZUhUZXBzQ2poOGxXRHBCXC9reWQzS1wvRURSd2c1K0gxMGNQdnRKTkc5Z2VvK0pES240dVFMVXlwSWU2czluSjR2VnI3OE84aGpqWFwvb3Z4RG9UU3hZREFBQT0iLCJzdWIiOiJhZG1pbkRCIiwicm9sZXMiOlsiUk9MRV9OT19ST0xFUyJdLCJleHAiOjE0OTU3MTU3OTMsImlhdCI6MTQ5NTcxMjE5M30.MPEXURGhJo5s75LfUSm5ckG99Byc7FCLyj1gYZJu1zk

This is decoded version:

"principal":"H4sIAAAAAAAAAJVSP0/bQBR/DkFBimgBqZU60KV0qxyJjplISpEqK6kaslCp6GI/3IPznbk7k2SpMpWBAUSLVJWvwDeBhQ9QtUNX5q59ZwhOuyBusfXu59+/57MrmDYaXsaacWH8VGQxl75JNZexwTDT3A79zKCO0OaItRzYpQlcH68EXgAlHllYCLbZHqsJJuNau7eNoa0PNCwrHd8wbmmWYF/pHf+WO1Qa/xEoqL3vJahswDwLQ5VJ21JydZByjdEGzBWzQIU7bvQopBuUljNhJqEVlKwnMAqgyjL7UZEqR2Ph4bXZzHJR66CtBzCTMmPI3X9JOtZZd/fOpqQEu/AJyoPUo0PdPXdQ3/H4TSUEpeZKmqWuTFTEt7gTJ/7R4vHl4emoWwKgTl7c/U0xf9KA0fmHP0/zor3QwuMJ6wWsPkjJzXzBvK7RKf/49vbLydX++ylSdojX99/H0spNc8OmSlKmmVUTOyLaftm9E3njbvLxFoZ+hyepQPqjpMXoVqIgprhlrcS4bwuz79rB6marvemeHTepsCjh8lWDpB/kyd3K/EDRwg5+H10cPvtJNG9geo+JDKn4uQLUypIe6s9nJ4vVr78O8hjjX/ovxDoTSxYDAAA=","sub":"adminDB","roles":["ROLE_NO_ROLES"],"exp":1495715793,"iat":1495712193

The principal here is signed and encrypted but the username is not encrypted. So it can be easily extracted by the code given above.

like image 116
Saqib Ahmed Avatar answered Nov 15 '22 09:11

Saqib Ahmed