Jak dodać certyfikat SSL dla aplikacji Spring Boota?

Jak dodać certyfikat SSL dla aplikacji Spring Boota?

Certyfikat SSL to niezbędny mechanizm który powinien być wdrożony dla każdej aplikacji internetowej! Brak certyfikatu SSL może doprowadzić do przejęcia wrażliwych danych co może być w konsekwencji tragiczne w skutkach. Aplikacje typu Wireshark które przeznaczone są do nasłuchu sieci pozwalają w łatwy sposób podejrzeć dane przesyłane po nie szyfrowanym protokole HTTP. W tym artykule pokaże Ci zagrożenia wynikające z niestosowania SSL oraz pokaże Ci również w jaki sposób zabezpieczyć aplikację napisaną z użyciem Spring Boota bezpłatnym certyfikatem SSL. Pobieramy oprogramowanie Wiresharkhttps://www.wireshark.org które posłuży do nasłuchu sieci i weryfikacji przesyłanych danych pomiędzy klientem a serwerem.

Tworzymy aplikację z użyciem Spring Boota która będzie reprezentować podstawowy mechanizm uwierzytelnienia – prosty formularz logowania:

Plik pom.xml – niezbędne zależności:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
 
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
 
<dependency>
	<groupId>org.thymeleaf.extras</groupId>
	<artifactId>thymeleaf-extras-springsecurity5</artifactId>
	<version>3.0.4.RELEASE</version>
</dependency>
 
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

Bardzo ważne jest aby zależność do Spring Security oraz do thymeleaf-extras-springsecurity5 były w tej samej wersji! Zależność thymeleaf-extras-springsecurity5 pozwala nam rozszerzyć nieco możliwości Thymeleafa o dodatkowe tagi np:

<span sec:authentication="name"></span>

pozwala wyświetlić login aktualnie zalogowanego użytkownika.

Konfiguracja Spring Security:

@Configuration
@EnableWebSecurity
public class EmployeeSecurityConfiguration extends WebSecurityConfigurerAdapter {
 
	@Override
	protected void configure(HttpSecurity http) throws Exception {
 
		http.authorizeRequests().antMatchers("/")
 
				.permitAll()
 
				.antMatchers("/security").hasAnyRole("USER")
 
				.anyRequest().authenticated()
 
				.and().
					formLogin().loginPage("/login").defaultSuccessUrl("/success", true).permitAll()
 
				.failureUrl("/login-error")
 
				.and()
					.logout()
					.logoutUrl("/logout")
					.logoutSuccessUrl("/login").permitAll();
 
		http.csrf().disable();
	}
 
	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder authenticationMgr) throws Exception {
		authenticationMgr.inMemoryAuthentication().withUser("javaleader").password("{noop}pljavaleader").authorities("ROLE_USER");
	}
}

Powyżej prosta konfiguracja Spring Security:

  • definicja strony logowania oraz adresu wylogowania,
  • definicja loginu i hasła dla użytkownika (zakładamy, że tylko jeden użytkownik może się do systemu zalogować o loginie javaleader i haśle pljavaleader),
  • definicja adresu strony po prawidłowym zalogowaniu.

Strona logowania – ./templates/loginPage.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Login page</title>
</head>
<body>
<h1>Login page</h1>
    <p th:if="${loginError}">Wrong user or password</p>
    <form th:action="@{/login}" method="post">
        <label for="username">Username</label>:
        <input type="text" id="username" name="username" autofocus="autofocus" />
        <br /><br />
        <label for="password">Password</label>:
        <input type="password" id="password" name="password" /> <br />
        <input type="submit" value="Log in" />
    </form>
</body>
</html>

Strona po poprawnym zalogowaniu – ./templates/securityPage.html:

<!DOCTYPE html>
<html lang="en" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
      xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Security content</h1>
<span sec:authorize="isAuthenticated()">
                Logged user: <span sec:authentication="name"></span> |
                Roles: <span sec:authentication="principal.authorities"></span> |
                <a th:href="@{/logout}">Sign Out</a>
            </span>
</body>
</html>

Znaczniki prefiksowane – sec dostępne są ponieważ w pom.xml dodaliśmy zależność do:

<dependency>
	<groupId>org.thymeleaf.extras</groupId>
	<artifactId>thymeleaf-extras-springsecurity5</artifactId>
	<version>3.0.4.RELEASE</version>
</dependency>

Kontroler – AppController:

@Controller
public class AppController {
 
  @RequestMapping("/login")
  public String loginPage() {
    return "loginPage";
  }
 
  @RequestMapping("/login-error")
  public String loginErrorPage(Model model) {
    model.addAttribute("loginError", true);
    return "loginPage";
  }
 
  @RequestMapping("/success")
  public String loginSuccessPage() {
    return "securityPage";
  }
 
}

Testy aplikacji:

Strona logowania:

http://localhost:8080/login

podanie prawidłowych danych:

  • login : javaleader
  • haslo : pljavaleader

skutkuje przekierowaniem na adres:

http://localhost:8080/success

wynik:

Security content
Logged user: javaleader | Roles: [ROLE_USER] | Sign Out

podanie nieprawidłowych danych skutkuje ponownym wyświetleniem formularza i komunikatem o błędzie:

Wrong user or password

Uruchamiamy aplikację Wireshark!

Aplikacja Wireshark posłuży nam do podejrzenia loginu i hasła z racji tego, że URL pod którym dostępna jest aplikacja nie jest zabezpieczony certyfikatem SSL!

Przeglądanie logów dla localhost wymaga doinstalowania aplikacji: npcap – https://nmap.org/npcap/

W miejscu gdzie pobraliśmy narzędzie wykonujemy polecenie:

rawcap

w wyniku otrzymujemy:

Interfaces:
 0.     169.254.16.211  Ethernet        Ethernet
 1.     169.254.219.207 Połączenie lokalne* 2   Wireless80211
 2.     10.10.1.62      Wi-Fi   Wireless80211
 3.     169.254.128.56  Połączenie sieciowe Bluetooth   Ethernet
 4.     127.0.0.1       Loopback Pseudo-Interface 1     Loopback
Select interface to sniff [default '0']: 4
Output path or filename [default 'dumpfile.pcap']:
Sniffing IP : 127.0.0.1
Output File : C:\Users\javaleader\Downloads\dumpfile.pcap
 --- Press [Ctrl]+C to stop ---
Packets     : 0

w aplikacji wpisujemy poprawne dane w taki sposób aby zostać przekierowanym do storny po poprawnym zalogowaniu się. Po tej operacji wyłączamy npcap wpisując w terminalu ctrl + c. Zostanie wygenerowany plik z logami dumpfile.pcap który w łatwy sposób będzie można podejrzeć w WireSharku!

po zdekodowaniu pakietu dokładnie widać jakie dane zostały przesłane co zdecydowanie jest solidnym naruszeniem zasad bezpieczeństwa!

Certyfikat SSL z użyciem narzędzia Keytool:

Narzędzie keytool znajduje się w standardowym pakiecie JDK (katalog .\bin gdzie zainstalowana jest Java) i służy do utworzenia pary kluczy (publicznego i prywatnego) oraz certyfikatu. Narzędzie keytool przechowuje klucze i certyfikaty w tzw. – „magazynie kluczy”, który w domyślnym wdrożeniu jest plikiem – „keystore” znajdującym się w katalogu głównym użytkownika. Klucze prywatne są chronione hasłami.

Generujemy certyfikat!

keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -validity 360 -keysize 2048

wynik:

Enter keystore password:
Re-enter new password:
What is your first and last name?
  [Unknown]:  Marcin Warycha
What is the name of your organizational unit?
  [Unknown]:  JavaLeader.pl
What is the name of your organization?
  [Unknown]:  JavaLeader.pl
What is the name of your City or Locality?
  [Unknown]:  Lodz
What is the name of your State or Province?
  [Unknown]:  Lodz
What is the two-letter country code for this unit?
  [Unknown]:  PL
Is CN=Marcin Warycha, OU=JavaLeader.pl, O=JavaLeader.pl, L=Lodz, ST=Lodz, C=PL correct?
  [no]:  yes
 
Enter key password for <selfsigned>
        (RETURN if same as keystore password):
 
Warning:
The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool 
-importkeystore -srckeystore keystore.jks -destkeystore keystore.jks -deststoretype pkcs12".

Jako, że otrzymaliśmy informację o migracji do PKCS12 to wykonujemy dodatkowo komendę:

keytool -importkeystore -srckeystore keystore.jks -destkeystore keystore.jks -deststoretype pkcs12

wynik:

Enter source keystore password:
Entry for alias selfsigned successfully imported.
Import command completed:  1 entries successfully imported, 0 entries failed or cancelled
 
Warning:
Migrated "keystore.jks" to Non JKS/JCEKS. The JKS keystore is backed up as "keystore.jks.old".

wyświetlenie szczegółowych informacji o certyfikacie:

keytool -list -v -keystore keystore.jks

wynik:

Enter keystore password:
Keystore type: JKS
Keystore provider: SUN
 
Your keystore contains 1 entry
 
Alias name: selfsigned
Creation date: 2020-02-19
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Marcin Warycha, OU=JavaLeader.pl, O=JavaLeader.pl, L=Lodz, ST=Lodz, C=PL
Issuer: CN=Marcin Warycha, OU=JavaLeader.pl, O=JavaLeader.pl, L=Lodz, ST=Lodz, C=PL
Serial number: 34a3840b
Valid from: Wed Feb 19 10:24:37 CET 2020 until: Sat Feb 13 10:24:37 CET 2021
Certificate fingerprints:
         MD5:  13:26:57:F9:6E:06:2E:EA:24:76:EF:EA:C8:FB:F1:5B
         SHA1: 01:7D:A8:CE:0C:53:39:88:31:39:30:22:7B:1A:BF:70:50:54:10:B8
         SHA256: 24:FB:34:21:07:7E:45:7A:26:54:E6:89:58:8C:15:52:63:B6:C6:E2:9E:DF:34:DD:CA:B4:46:31:32:71:4C:A3
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
 
Extensions:
 
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 33 46 C4 0E 65 89 19 CD   32 6E 38 FF D8 07 FC C2  3F..e...2n8.....
0010: 85 69 41

W pliku application.properties dodajemy następujące wpisy:

server.port                    = 443
server.ssl.key-password        = sslkeystore
server.ssl.key-store-password  = sslkeystore
server.ssl.key-store           = src/main/resources/keystore.jks

plik keystore.jks powinien być zamieszczony w lokalizacji:

src/main/resources/keystore.jks

aplikacje testujemy pod adresem:

https://localhost:443/login

przeglądarka wyświetla teraz informację o tym, że organizacja która dostarcza certyfikat jest niezaufana!

wyświetlam certyfikat:

Z poziomu aplikacji Wireshark podejrzenie hasła jest już w zasadzie niemożliwe ze względu na wdrożenie obsługi TLS (z ang. Transport Layer Security). Jeśli zależy nam na zaufanym certyfikacie SSL proponuję skorzystać z bezpłatnej opcji https://letsencrypt.org/ (ale dopiero po wdrożeniu aplikacji na domenę ponieważ dla localhost jest to niemożliwe).

Zobacz kod na GitHubie i zapisz się na bezpłatny newsletter!

.

 

Leave a comment

Your email address will not be published.


*