Makuhari Development Corporation
6 min read, 1156 words, last updated: 2025/4/1
TwitterLinkedInFacebookEmail

Cross-Origin APIs and Security: A Deep Dive into CORS, CSRF, and XSS Protection

Web applications today rely heavily on APIs that serve multiple origins, but this convenience comes with significant security considerations. Even with robust authentication and HTTPS encryption, unrestricted cross-origin API access can introduce vulnerabilities that developers must carefully address. This deep dive explores the security implications of CORS configurations and provides comprehensive strategies for risk mitigation.

Background: The Cross-Origin Security Landscape

Cross-Origin Resource Sharing (CORS) was introduced to enable controlled access to resources across different domains, relaxing the browser's same-origin policy in a secure manner. However, when APIs are configured to allow all origins (Access-Control-Allow-Origin: *), they essentially become publicly accessible endpoints that can be called from any website.

The question many developers face is: if we have comprehensive authentication and use HTTPS, how significant are the remaining security risks?

Core Security Concepts and Attack Vectors

1. Cross-Site Request Forgery (CSRF)

CSRF attacks exploit the browser's automatic inclusion of credentials (cookies, authentication headers) in cross-origin requests. Even with proper authentication, unrestricted CORS can enable these attacks.

Attack Flow:

1. User logs into legitimate-app.com (receives auth cookie)
2. User visits malicious-site.com while still logged in
3. Malicious site sends request to legitimate-app.com/api/transfer-money
4. Browser automatically includes auth cookie
5. API processes request as authenticated user

Mitigation Strategies:

// Spring Security CSRF Protection
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
            .csrf(csrf -> csrf
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .ignoringRequestMatchers("/api/public/**")
            )
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .build();
    }
}

For Cookie-based authentication, implement SameSite restrictions:

@Bean
public CookieSameSiteSupplier cookieSameSiteSupplier() {
    return CookieSameSiteSupplier.of(SameSite.STRICT);
}

2. Cross-Site Scripting (XSS) Amplification

While XSS primarily affects the frontend, unrestricted CORS can amplify the impact by allowing malicious scripts to interact with APIs from any origin.

Frontend Protection (Vue.js Example):

<template>
  <!-- Safe: Vue automatically escapes content -->
  <div>{{ userInput }}</div>
  
  <!-- Dangerous: Raw HTML injection -->
  <div v-html="sanitizeHtml(userInput)"></div>
</template>
 
<script>
import DOMPurify from 'dompurify';
 
export default {
  methods: {
    sanitizeHtml(input) {
      // Always sanitize before using v-html
      return DOMPurify.sanitize(input);
    }
  }
}
</script>

Backend Protection:

@Component
public class XSSProtectionFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        // Set Content Security Policy
        httpResponse.setHeader("Content-Security-Policy", 
            "default-src 'self'; script-src 'self' 'unsafe-inline'; " +
            "style-src 'self' 'unsafe-inline'; img-src 'self' data: https:");
        
        // Prevent XSS in legacy browsers
        httpResponse.setHeader("X-XSS-Protection", "1; mode=block");
        
        chain.doFilter(request, response);
    }
}

3. Token Exposure and Credential Leakage

When APIs lack origin restrictions, there's increased risk of credentials being exposed in unintended contexts.

Problem Scenario:

// Frontend accidentally sends auth tokens to public APIs
fetch('https://public-api.example.com/data', {
    headers: {
        'Authorization': `Bearer ${localStorage.getItem('authToken')}`, // Unnecessary!
        'Content-Type': 'application/json'
    }
});

Secure Implementation:

class ApiClient {
    constructor(baseUrl, requiresAuth = false) {
        this.baseUrl = baseUrl;
        this.requiresAuth = requiresAuth;
    }
    
    async request(endpoint, options = {}) {
        const headers = {
            'Content-Type': 'application/json',
            ...options.headers
        };
        
        // Only add auth headers when required
        if (this.requiresAuth) {
            const token = localStorage.getItem('authToken');
            if (token) {
                headers['Authorization'] = `Bearer ${token}`;
            }
        }
        
        return fetch(`${this.baseUrl}${endpoint}`, {
            ...options,
            headers
        });
    }
}
 
// Usage
const publicApi = new ApiClient('https://public-api.com', false);
const privateApi = new ApiClient('https://private-api.com', true);

Risk Analysis and Mitigation Framework

The security implications of unrestricted CORS vary significantly based on your authentication mechanism:

JWT with Authorization Headers (Low CSRF Risk):

// Browsers don't automatically send Authorization headers cross-origin
const response = await fetch('https://api.example.com/protected', {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${jwt}`,
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
});

Since browsers don't automatically include Authorization headers in cross-origin requests, CSRF attacks cannot leverage JWT tokens stored in localStorage or sessionStorage.

Cookie-Based Authentication (Higher CSRF Risk):

@RestController
public class SecureController {
    
    @PostMapping("/api/sensitive-action")
    public ResponseEntity<?> sensitiveAction(HttpServletRequest request) {
        // Verify origin for cookie-based auth
        String origin = request.getHeader("Origin");
        String referer = request.getHeader("Referer");
        
        if (!isValidOrigin(origin) || !isValidReferer(referer)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }
        
        // Process request...
        return ResponseEntity.ok().build();
    }
    
    private boolean isValidOrigin(String origin) {
        return Arrays.asList("https://trusted-domain.com", "https://app.company.com")
                    .contains(origin);
    }
}

Comprehensive Security Configuration

Here's a complete Spring Boot security configuration that addresses the discussed vulnerabilities:

@Configuration
@EnableWebSecurity
public class ComprehensiveSecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .csrf(csrf -> csrf
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .ignoringRequestMatchers("/api/public/**")
            )
            .headers(headers -> headers
                .contentSecurityPolicy("default-src 'self'; script-src 'self'")
                .and()
                .httpStrictTransportSecurity(hsts -> hsts
                    .maxAgeInSeconds(31536000)
                    .includeSubdomains(true)
                )
            )
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .maximumSessions(1)
                .maxSessionsPreventsLogin(true)
            )
            .build();
    }
    
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        
        // Restrict to trusted origins instead of allowing all
        configuration.setAllowedOriginPatterns(Arrays.asList(
            "https://*.company.com",
            "https://trusted-partner.com"
        ));
        
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/api/**", configuration);
        
        return source;
    }
}

Implications and Best Practices

When Risks Are Controllable

The security risks of unrestricted CORS become manageable when you implement:

  1. CSRF Protection: SameSite cookies, CSRF tokens, or origin validation
  2. XSS Prevention: Input sanitization, output encoding, and CSP headers
  3. Secure Token Management: JWT via Authorization headers instead of cookies
  4. Rate Limiting: Prevent API abuse and DoS attacks
  5. Comprehensive Logging: Monitor for suspicious cross-origin activity

Risk Assessment Matrix

Security Control CSRF Risk XSS Risk Token Exposure Overall Risk
No CORS restrictions + Cookie auth High Medium Medium High
No CORS restrictions + JWT (localStorage) Low High Medium Medium
Restricted CORS + Cookie auth Low Low Low Low
Restricted CORS + JWT + CSP Very Low Very Low Very Low Very Low

AWS and Cloud-Level Protections

While application-level security is primary, cloud platforms can provide additional layers:

# AWS WAF Rule Example
Rules:
  - Name: RestrictOriginRule
    Priority: 1
    Statement:
      ByteMatchStatement:
        SearchString: "malicious-domain.com"
        FieldToMatch:
          SingleHeader:
            Name: "origin"
        TextTransformations:
          - Priority: 0
            Type: LOWERCASE
    Action:
      Block: {}

AWS CloudFront Security Headers:

// Lambda@Edge function for security headers
exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;
    const headers = response.headers;
    
    headers['content-security-policy'] = [{
        key: 'Content-Security-Policy',
        value: "default-src 'self'; script-src 'self' 'unsafe-inline'"
    }];
    
    headers['x-frame-options'] = [{
        key: 'X-Frame-Options',
        value: 'DENY'
    }];
    
    callback(null, response);
};

Conclusion

While unrestricted CORS policies in APIs pose legitimate security risks, these risks become highly manageable with proper implementation of layered security controls. The key is not to rely on CORS restrictions alone, but to implement a comprehensive security strategy that includes:

  • Authentication-appropriate CSRF protection
  • Multi-layered XSS prevention (both frontend and backend)
  • Secure credential management practices
  • Comprehensive monitoring and logging

For modern applications using JWT authentication with Authorization headers, the CSRF risk is naturally mitigated, but XSS and token exposure concerns remain. The most secure approach combines restricted CORS policies with robust application-level security controls, creating defense in depth that protects against both current and emerging attack vectors

Makuhari Development Corporation
法人番号: 6040001134259
サイトマップ
ご利用にあたって
個人情報保護方針
個人情報取扱に関する同意事項
お問い合わせ
Copyright© Makuhari Development Corporation. All Rights Reserved.