> ## Content Index
> Fetch the complete content index at: https://lsdev.pl/llms.txt
> Use this file to discover other available public pages before exploring further.

# SOP (Same-Origin Policy), CORS (Cross-Origin Resource Sharing) i przykładowa konfiguracja w Springu
- URL: https://lsdev.pl/posts/czym-jest-cors-sop-i-przykladowa-konfiguracja-w-springu/
- Published: 2022-01-09T23:11:44.000Z
- Updated: 2022-01-11T20:12:40.000Z
- Author: Łukasz Sakowicz
- Tags: Spring, Security, Java, Programming, #Import 2026-08-18 22:07

**SOP** (*Same-Origin Policy*) jest mechanizmem bezpieczeństwa przeglądarek internetowych  
polegający na odizolowaniu od siebie zasobów różnego pochodzenia. Każdy zasób webowy ma źródło, z którego pochodzi tzw. *origin*. Jeden unikalny *origin* definiują jego 3 główne składowe tj. `protokół`, `domena` i `port`.  
  
Przykładowo   
  
`http://api.example.com/app.js  
http://api.example.com:8090/app.js  
http://example.com/app.js`   
`https://example.com/app.js`  
  
są różnymi originami mimo, że mogą wskazywać na ten sam zasób. Z drugiej strony  
  
`http://example.com/example1.html`   
`http://example.com/example2.html`   
  
są różnymi zasobami jednak pochodzą z tego samego *origin'a*.

W praktyce pod jednym *origin* zwykle mamy aplikację webową  
składającą się z wielu różnych zasobów aby jej funkcjonowanie było możliwe.

Przeglądarka implementując **SOP** w podstawowym zakresie zabezpiecza naszą aplikację  
przed różnego rodzaju potencjalnym zagrożeniem ze strony bezpieczeństwa gdzie inna aplikacja np. stworzona przez atakującego mogłaby z wykorzystaniem [AJAX](https://pl.wikipedia.org/wiki/AJAX?ref=lsdev.pl) wywoływać metody w imieniu użytkownika naszej aplikacji np. kupić produkt w sklepie uprzednio zmieniając adres wysyłki na atakującego czyli tzw. atak [CSRF](https://pl.wikipedia.org/wiki/Cross-site%5Frequest%5Fforgery?ref=lsdev.pl) (*Cross-site request forgery*). Warto wspomnieć, że nie należy polegać jedynie na tym mechaniźmie i stosować różnego rodzaju zabezpiczenia po stronie serwera tj. csrf token, walidacja origin, nagłówki access control i inne zabezpieczenia.

W celu przetestowania mechanizmu **SOP** wystarczy wejść na dowolną stronę np. `google.com` otworzyć narzędzia developerskie w zakładce `Console` i wysłać żądanie *Ajaxowe* na inny *origin*:

```js
var req = new XMLHttpRequest();
req.open('GET', 'http://localhost:8081', false);
req.send();

```

Access to XMLHttpRequest at 'http://localhost:8081/' from origin 'https://www.google.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. 

Jak widać przeglądarka zablokowała możliwość odczytania / dostępu do odpowiedzi z uwagi na inny *origin*, z którego pobieramy zasób niż ten, w kontekście którego aktualnie się znajdujemy czyli `google.com`. Warto zaznaczyć, że przeglądarka jedynie zablokowała dostęp do odpowiedzi natomiast samo żądanie zostało wysłane poprawnie. Wynika to z faktu, że przeglądarka dzieli żądania na „proste” czyli np. wykonywane w kontekście `img`, `iframe`, `style`, `form` i „złożone” np. typu `PUT`, `DELETE` (więcej o różnicy [tutaj](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS?ref=lsdev.pl#examples%5Fof%5Faccess%5Fcontrol%5Fscenarios)).

Wiemy już dlaczego nie możemy polegać tylko na mechaniźmie **SOP**. Atakujący w łatwy sposób mógłby spreparować tzw. „proste” żądanie przygotowując np. odpowiedni  
formularz, który automatycznie by został wysłany do serwera zmieniając stan systemu w niekontrolowany sposób.

Z uwagi na to, że **SOP** jest dość restrykcyjny ponieważ pozwala na dostęp do zasobów tylko z tego samego *origin,* **CORS** (*Cross-Origin Resource Sharing*) ma za zadanie zluzować tą sztywną politykę i umożliwić za pomocą odpowiedniej konfiguracji na bezproblemową komunikację pomiędzy różnymi *origin'ami*.

Przykładowo aby odblokować dostęp do odpowiedzi, która wcześniej była przyblokowana przez przeglądarkę możemy po stronie serwera w odpowiedzi ustawić nagłówek w taki sposób:

`Access-Control-Allow-Origin: *`

W ten sposób zapewnimy dostęp dla danego zasobu wszystkim aplikacjom (*origins*), które będą chciały uzyskać do niego dostęp. Zwykle powyższy nagłówek ustawiany jest w taki sposób dla zasobów statycznych dostępnych publicznie np. biblioteki javascriptowe. Jest to typowe dla sieci CDN:

```bash
$ curl -I https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js

HTTP/2 200 
content-type: application/javascript; charset=utf-8
access-control-allow-origin: *
server: cloudflare
...

```

### Przykładowa konfiguracja CORS w Springu

Może się zdarzyć tak, że aplikacja kliencka po stronie przeglądarki (np. Angular) będzie musiała komunikować się z api serwera w innym *origin* niż jest serwowana np. aplikacja napisana w Angular jest dostępna pod adresem `https://example.com` i komunikuje się z backendem poprzez `https://api.example.com`. Wiemy już, że są to dwa różne *origin'y* w związku z tym aby komunikacja przebiegła pomyślnie musimy w taki sposób skonfigurować backend aby zezwolić na taką komunikację.

Rozpoczniemy od zdefiniowania beana odpowiedzialnego za dostarczenie ww. konfiguracji:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.List;

@Configuration
@ConditionalOnProperty(prefix = "cors", name = "allowedOrigins")
public class CorsConfiguration {

    @Bean
    public CorsConfigurationSource corsConfigurationSource(
            @Value("${cors.allowedOrigins}") List allowedOrigins
    ) {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        org.springframework.web.cors.CorsConfiguration corsConfiguration = new org.springframework.web.cors.CorsConfiguration();
        corsConfiguration.setAllowedOrigins(allowedOrigins);
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.setAllowedMethods(List.of(
                HttpMethod.GET.name(),
                HttpMethod.HEAD.name(),
                HttpMethod.POST.name(),
                HttpMethod.PUT.name(),
                HttpMethod.DELETE.name()
        ));
        corsConfiguration.setMaxAge(3600L);
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }
}

 Następnie stworzymy adnotację pomocniczą, która zaimportuje nam ww. konfigurację.  
Zakładam, że konfiguracja będzie w jednej z bibliotek wspólnych w związku z tym pozwoli to nam ją włączać w zależności od potrzeb dla modułów, które wystawiają api restowe. 

```java
import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({CorsConfiguration.class})
public @interface EnableCorsConfiguration {
}

```

Po czym wykorzystamy ww. konfigurację:

```java
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;

@Configuration
@EnableResourceServer
@EnableCorsConfiguration
public class ExampleSecurityConfiguration extends ResourceServerConfigurerAdapter {
    
    @Value("${oauth.resourceId}")
    private String resourceId;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.requestMatchers().antMatchers("/**")
                    .anyRequest().authenticated().and().cors();
    }
    
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId(resourceId);
    }
}

```

Na koniec w pliku `application.yaml` zdefiniujemy na jakie *origin'y* zezwalamy:

```js
cors:
  allowedOrigins: https://example.com

```

Od teraz aplikacja kliencka bez problemu powinna komunikować się z backendem.  
  
Warto wspomnieć, że jeżeli przed naszymi aplikacjami backendowymi w Java stoi jakiś reverse proxy np. Nginx tam również byłaby możliwa konfiguracja poprzez manipulację nagłówkami. 

Na koniec warto zastanowić się czy architektura, w której API mamy na oddzielnej domenie faktycznie ma sens. Przeniesienie go na główną domenę może zredukować liczbę wykonywanych żądań nawet o połowę (z uwagi na *Preflight Requests*) co na pewno odciąży nasz serwer.