Hoàng Long
Hoàng Long

Reputation: 10848

Spring Switching User: only allow a user with a specific role switch to another user with specific roles

I'm making a web application with Spring Security with 3 different roles:

Currently my application allow SuperSupporter to "impersonate" DivisionSupporter by using SwitchUserFilter, for him to act as that DivisionSupporter. This is necessary in case a DivisionSupporter meet some problems - and needs supported. This may sound strange, but it's kind of using TeamViewer to examine the computer problem for a friend.

Further more, now we need give the DivisionSupporter the right to "impersonate" the Client, so that the DivisionSupporter can "support" the Client.

But the thing makes me worried is that if we give the DivisionSupporter the right to access j_spring_security_switch_user, malicious DivisionSupporter may use it to impersonate the SuperSupporter (by posting with to the link with the according username).

I have thought of a work around to stop that case:

 <bean id="switchUserFilter" class="org.springframework.security.web.authentication.switchuser.SwitchUserFilter">
        <property name="userDetailsService" ref="userDetailsService" />
        <property name="switchUserUrl" value="/j_spring_security_switch_user" />
        <property name="exitUserUrl" value="/j_spring_security_exit_user" />
        <property name="targetUrl" value="/checkRole.html" />
     </bean>

In /checkRole action (which is the targetUrl), I make another check: if the user role is SuperSupporter and he is impersonate someone, the application will send him to /j_spring_security_exit_user (because a SuperSupporter won't need to impersonate himself).

Though it seems work, but I'm worried that a malicious user may find a way to work around this wall, and hence put our system in danger.

I think that SpringSecurity may have someway to address this needs, but still not able to find it. Is this way really safe? Can the DivisionSupporter work around to impersonate the SuperSupporter with my current solution?

More importantly, is there any better way to address this problem?

Any help would be appreciated.

Upvotes: 3

Views: 5998

Answers (3)

TomDoes
TomDoes

Reputation: 284

Although this is an old question, I recently solved a similar problem in a slightly simpler way than Shaun the Sheeps suggestion by creating my own UserDetailsChecker

public class CustomUserDetailsChecker extends AccountStatusUserDetailsChecker {
    private final Log logger = LogFactory.getLog(this.getClass());
    @Override
    public void check(UserDetails user) {
        if (!user.isAccountNonLocked()) {
            this.logger.debug("Failed to authenticate since user account is locked");
            throw new LockedException(this.messages.getMessage("AccountStatusUserDetailsChecker.locked", "User account is locked"));

// other checks


        } else if(user.getAuthorities().contains(new SimpleGrantedAuthority("your-authority"))) {
            this.logger.debug("Failed to authenticate since user account is a your-authority");
            throw new LockedException(this.messages.getMessage("CustomUserDetailsChecker.cannotBeImpersonated", "User cannot be impersonated"));
        }
    }
}


and set it when you create the SwitchUserFilter bean

    @Bean
    public SwitchUserFilter switchUserFilter(CustomUserDetailsService userDetailsService) {
        SwitchUserFilter filter = new SwitchUserFilter();
        filter.setSecurityContextRepository(new HttpSessionSecurityContextRepository());
        filter.setUserDetailsService(userDetailsService);
        filter.setUserDetailsChecker(new CustomUserDetailsChecker());
        // other setters
        return filter;
    }

Calling the user switch endpoint will then give a 401 status when the target user has a role that cannot be impersonated.

Upvotes: 1

Lord Nighton
Lord Nighton

Reputation: 1720

Use the following example to swith user using Spring Security.

This section sets the main parameters for applying of this functionality:

@Bean
public SwitchUserFilter switchUserFilter() {
    SwitchUserFilter filter = new SwitchUserFilter();

    filter.setUserDetailsService(defaultUserDetailsService);
    filter.setTargetUrl("/");
    filter.setSwitchUserUrl("/switchuserto");
    filter.setUsernameParameter("username");
    filter.setExitUserUrl("/switchuserlogout");
    filter.setSwitchFailureUrl("/index");

    return filter;
}

The following string adds switchUserFilter object into HttpSecurity http object:

.addFilter(switchUserFilter())

Upvotes: 0

Shaun the Sheep
Shaun the Sheep

Reputation: 22742

The targetUrl approach is a bad idea, since the user can just ignore the redirect and request another URL instead. You need to prevent inappropriate switches in the first place.

Your best bet is probably to customize the attemptSwitchUser method on SwitchUserFilter:

protected Authentication attemptSwitchUser(HttpServletRequest request) {
    Authentication switchTo = super.attemptSwitchUser(request);
    Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
    // Inspect currentUser (e.g. authorities) and switchTo to see if valid combination
    // Raise AuthenticationException if not

    return switchTo;
}

That method is called before the SecurityContext is set, so it is the best place to do any checking on the validity of the switch.

Upvotes: 8

Related Questions