'How to resolve XSRF Cross-Site Request Forgery (CSRF) in API controller after SAST Checkmarx

I have done the scan my project java spring boot with Checkmarx tool. The tool found about 23 XSRF occurrences with Medium severity.

The issue found is marked on Rest API method POST on @RequestBody List<String> lineups

In attached the screen-shoot for description result:

enter image description here

    @RequestMapping(value = "/rules/lineup/{ruleMode}", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Object> getRulesByRuleModeAndLineup(@PathVariable Integer ruleMode,
            @RequestBody List<String> lineups) throws Exception {
        
        LOGGER.info("[getRulesByRuleModeAndLineup] ENTER type: " + ruleMode + " lineup: " + lineups);
        
        ResponseEntity<Object> output = null;
        List<Rule> rules = new ArrayList<Rule>();

        try {
            
            for (String lineup : lineups) {
                
                String lineupSanitized = HtmlUtils.htmlEscape(lineup);
                rules.addAll(uiService.getRulesByRuleModeAndLineup(ruleMode, lineupSanitized));

            }

            output = new ResponseEntity<>(rules, HttpStatus.OK);

        } catch (Exception e) {

            LOGGER.error(e, e);
            output = new ResponseEntity<>("An error occurred: " + e.getMessage() + "'",
                    HttpStatus.INTERNAL_SERVER_ERROR);

        }

        return output;

    }

Is there sample fix to resolve the issue ?



Solution 1:[1]

You can choose to have a validation on that field. Here you have two choices:

  1. use a framework that has this already build in, i recommend spring for this: https://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/csrf.html
  2. try to whitelist what strings you want to accept on your api, if you can do that.

The second one means you take the responsibility to keep your list up to date.

Solution 2:[2]

I tried to implement the first point that you told in your answer like followed code

Back-end side:

    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        private static final Logger LOGGER = LogManager.getLogger(SecurityConfig.class);
    
        
        @SuppressWarnings("javadoc")
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            
            http.authorizeRequests().antMatchers("/**").permitAll().anyRequest().authenticated();
            
            http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
            
            http.headers().httpStrictTransportSecurity();
        }
    
    }

Front-end side

I implemented a xhrRequestHandler file name js inside the followed code:

XMLHttpRequest.prototype.origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function () {
    this.origOpen.apply(this, arguments);
    if (document.cookie) {
        var csrfToken = document.cookie.match(new RegExp(`XSRF-TOKEN=([^;]+)`))[1];
        if (csrfToken) {
            this.setRequestHeader("X-XSRF-TOKEN", csrfToken);
        }
    }
    if (arguments[1].includes("socket/info")) {
        this.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    }
};

and on the index.jsp

I imported file js defined above with the followed code:

<script src="<c:url value="/resources/js/xhrRequestHandler.js"/>"></script>

And added meta names rows:

<meta name="_csrf" content="${_csrf.token}"/>
    <meta name="_csrf_header" content="${_csrf.headerName}"/>

but I didn't resolve the issue

Is there something wrong ?

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Opri
Solution 2