cas client 用于限制匿名用户对某些特定api的访问,在一些特殊的环境下可能会有权限绕过问题。下面分享两个实际生活中遇到的案例。
/api/admin这个url是需要cas登陆才能访问的。
package today.redteam.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class AdminController { @RequestMapping("/api/admin") public String admin(){ return "hello admin"; } }
/api/guest这个是公共页面,所有人都可以看。
package today.redteam.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class GuestController { @RequestMapping("/api/guest") public String guest(){ return "hello guest"; } }
假设此程序的技术比较老旧,为了实现上面这种需求,那么开发可能会在web.xml中这么配置。
<filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>https://battags.ad.ess.rutgers.edu:8443/cas</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://www.acme-client.com</param-value> </init-param> <init-param> <param-name>ignorePattern</param-name> <param-value>/api/guest</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
这样配置看起来似乎没有问题
但是其实可以被花式绕过。
原因有两点,第一org.jasig.cas.client.authentication.AuthenticationFilter,在匹配时候获取了原生url(未处理../),甚至包括?
后的内容。
第二,filter的匹配模式有四种,默认是按正则匹配。
同理如果配置成CONTAINS也会有类似的问题。
同理封装了这个库的三方库也会有问题,比较流行的是cas-client-autoconfig-support ,它常与springboot集成使用,如果有如下配置也会出问题。
cas.ignorePattern=/api/guest
所以正确的配置应该改为
<init-param> <param-name>ignorePattern</param-name> <param-value>^/api/guest$</param-value> </init-param>
个人认为这个可以算洞(没处理../而且contains这种选项就不应该存在),也可以甩锅给开发没仔细看文档。
这里假设/admin系列的路由都不允许访问。
package today.redteam.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController public class AdminController { @RequestMapping( value = {"/admin"}, method = {RequestMethod.GET} ) public String admin(){ return "hello admin"; } @RequestMapping( value = {"/admin/api"}, method = {RequestMethod.GET} ) public String admin1(){ return "hello admin1"; } }
这一次开发认真看了文档做了以下配置。
package today.redteam.config; import net.unicon.cas.client.configuration.CasClientConfigurerAdapter; import net.unicon.cas.client.configuration.EnableCasClient; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Configuration; import today.redteam.aop.CasAspect; @Configuration @EnableCasClient public class CasConfig extends CasClientConfigurerAdapter { public CasConfig() { } public void configureAuthenticationFilter(FilterRegistrationBean authenticationFilter) { super.configureAuthenticationFilter(authenticationFilter); authenticationFilter.addUrlPatterns(new String[]{"/admin/*"}); } static { System.setProperty("cas.serverUrlPrefix", "https://cashost.com/cas"); System.setProperty("cas.serverLoginUrl", "https://cashost.com/cas/login"); System.setProperty("cas.clientHostUrl", "http://localhost:8888/"); System.setProperty("cas.validationType", "CAS"); } }
/admin这个路由看起来似乎也没有什么问题。
常规的绕过方式也不起作用
但在低版本的springboot上还是能绕过(本地环境是1.5.9.RELEASE)。
原理是在springboot 1.x中useSuffixPatternMatch默认为true,springboot会对路由进行正则匹配。
断点下在org.springframework.web.servlet.mvc.condition.PatternsRequestCondition#getMatchingPattern。
/admin.*自然能匹配上/admin.也就绕过了。
如下关闭setUseSuffixPatternMatch或升级到2.x
package today.redteam.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; @Configuration @ComponentScan @EnableWebMvc public class AppConfig { @Bean public HandlerMapping requestMappingHandlerMapping() { RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping(); mapping.setUseSuffixPatternMatch(false); return mapping; } }