security-notes
security-notes
jim stafford
We won’t get into the details of Information Security analysis and making specific trade-offs, but we
will cover how we can address the potential mitigations through the use of a framework and how
that is performed within Spring Security and Spring Boot.
1.1. Goals
You will learn:
1.2. Objectives
At the conclusion of this lecture and related exercises, you will be able to:
1. define identity, authentication, and authorization and how they can help protect our software
system
2. identify the purpose for and differences between encoding, encryption, and cryptographic
hashes
7. identify and demonstrate the security features of the default Spring Security configuration
1
Chapter 2. Access Control
Access Control is one of the key mitigation factors within a security solution.
Identity
We need to know who the caller is and/or who is the request being made for. When you make a
request in everyday life (e.g., make a pizza order) — you commonly have to supply your identity
so that your request can be associated with you. There can be many layers of systems/software
between the human and the action performed, so identity can be more complex than just a
single value — but I will keep the examples to a simple username.
Authentication
We need verification of the requester’s identity. This is commonly something known — e.g., a
password, PIN, or generated token. Additional or alternate types of authentication like
something someone has (e.g., access to a specific mobile phone number or email account, or
assigned token generator) are also becoming more common today and are adding a needed
additional level of security to more sensitive information.
Authorization
Once we know and can confirm the identity of the requester, we then need to know what actions
they are allowed to perform and information they are allowed to access or manipulate. This can
be based on assigned roles (e.g., administrator, user), relative role (e.g., creator, owner,
member), or releasability (e.g., access markings).
These access control decisions are largely independent of the business logic and can be delegated to
the framework. That makes it much easier to develop and test business logic outside the security
protections and to be able to develop and leverage mature and potentially certified access control
solutions.
2
Chapter 3. Privacy
Privacy is a general term applied to keeping certain information or interactions secret from others.
We use various encoding, encryption, and hash functions in order to achieve these goals.
3.1. Encoding
Encoding converts source information into an alternate form that is safe for communication and/or
[1]
storage. Two primary examples are URL and Base64 encoding of special characters or entire
values. Encoding may obfuscate the data, but by itself is not encryption. Anyone knowing the
encoding scheme can decode an encoded value and that is its intended purpose.
① echo -n echos the supplied string without new line character added - which would pollute the
value
3.2. Encryption
Encryption is a technique of encoding "plaintext" information into an enciphered form
("ciphertext") with the intention that only authorized parties — in possession of the
[2]
encryption/decryption keys — can convert back to plaintext. Others not in possession of the keys
would be forced to try to break the code through (hopefully) a significant amount of computation.
There are two primary types of keys — symmetric and asymmetric. For encryption with symmetric
keys, the encryptor and decryptor must be in possession of the same/shared key. For encryption
with asymmetric keys — there are two keys: public and private. Plaintext encrypted with the
shared, public key can only be decrypted with the private key. SSH is an example of using
asymmetric encryption.
3
$ cat /tmp/ciphertext
U2FsdGVkX18mM2yNc337MS5r/iRJKI+roqkSym0zgMc=
② encrypting file of plaintext with a symmetric/shared key ("password"). Result is base64 encoded.
④ "-pass" parameter supplies password and "pass:" prefix identifies password is inline in the
command
⑤ decrypting file of ciphertext with valid symmetric/shared key after being base64 decoded
Unlike encryption there is no way to mathematically obtain the original plaintext from the
resulting hash. That makes it a great alternative to storing plaintext or encrypted passwords.
However, there are still some unwanted vulnerabilities by having the calculated value be the same
each time.
By adding some non-private variants to each invocation (called "Salt"), the resulting values can be
technically different — making it difficult to use brute force dictionary attacks. The following
example uses the Apache htpasswd command to generate a Cryptographic Hash with a Salt value
4
that will be different each time. The first example uses the MD5 algorithm again and the second
example uses the Bcrypt algorithm — which is more secure and widely accepted for creating
Cryptographic Hashes for passwords.
• -C: set computing time between 4 and 17; high is slower and more secure
[1] "Code/Encoding",Wikipedia
[2] "Encryption", Wikipedia
5
Chapter 4. Spring Web
Spring Framework operates on a series of core
abstractions and a means to leverage them from
different callchains. Most of the components are
manually assembled through builders and
components/beans are often integrated together
through the Spring application context.
6
Chapter 5. No Security
We know by now that we can exercise the Spring Application Filter Chain by implementing and
calling a controller class. I have implemented a simple example class that I will be using throughout
this lecture. At this point in time — no security has been enabled.
Example GET
@RequestMapping(path="/api/anonymous/hello",
method= RequestMethod.GET)
public String getHello(@RequestParam(name = "name", defaultValue = "you") String name)
{
return "hello, " + name;
}
We can call the endpoint using the following curl or equivalent browser call.
Example POST
@RequestMapping(path="/api/anonymous/hello",
method = RequestMethod.POST,
consumes = MediaType.TEXT_PLAIN_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
public String postHello(@RequestBody String name) {
return "hello, " + name;
7
}
src/main/resources/
`-- static
`-- content
`-- hello_static.txt
Anything placed below src/main/resources will be made available in the classpath
within the JAR via target/classes.
target/classes/
`-- static <== classpath:/static at runtime
`-- content <== /content URI at runtime
`-- hello_static.txt
This would be a common thing to do for css files, images, and other supporting web content. The
following is a text file in our sample application.
src/main/resources/static/content/hello_static.txt
8
The following is an example GET of that static resource file.
9
Chapter 6. Spring Security
The Spring Security
framework is
integrated into the web
callchain using filters
that form an internal
Security Filter Chain.
10
Figure 3. Spring Security Core Authentication Framework
Authentication
provides both an authentication request and result abstraction. All the key properties (principal,
credentials, details) are defined as java.lang.Object to allow just about any identity and
authentication abstraction be represented. For example, an Authentication request has a
principal set to the username String and an Authentication response has the principal set to
UserDetails containing the username and other account information.
AuthenticationManager
provides a front door to authentication requests that may be satisfied using one or more
AuthenticationProvider
AuthenticationProvider
a specific authenticator with access to UserDetails to complete the authentication.
Authentication requests are of a specific type. If this provider supports the type and can verify
the identity claim of the caller — an Authentication result with additional user details is
returned.
UserDetailsService
a lookup strategy used by AuthenticationProvider to obtain UserDetails by username. There are a
few configurable implementations provided by Spring (e.g., JDBC), but we are encouraged to
create our own implementations if we have a credentials repository that was not addressed.
UserDetails
an interface that represents the minimal needed information for a user. This will be made part
11
of the Authentication response in the principal property.
6.2. SecurityContext
The authentication is maintained inside a SecurityContext that can be manipulated over time. The
current state of authentication is located through static methods of the SecurityContextHolder class.
Although there are multiple strategies for maintaining the current SecurityContext with
Authentication — the most common is ThreadLocal.
12
Chapter 7. Spring Boot Security
AutoConfiguration
As with most Spring Boot libraries — we have to do very little to get started. Most of what you were
shown above is instantiated with a single additional dependency on the spring-boot-starter-
security artifact.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
• For Spring Boot < 2.7, the autoconfiguration classes will be named in META-INF/spring.factories:
# org.springframework-boot:spring-boot-autoconfigure/META-INF/spring.factories
...
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
...
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfigur
ation,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguratio
n,\
• For Spring Boot >= 2.7, the autoconfiguration classes will be named in META-
INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
# org.springframework-boot:spring-boot-autoconfigure/META-
INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
...
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfigur
ation
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguratio
n
The details of this may not be that important except to understand how the default behavior was
13
assembled and how future customizations override this behavior.
7.2. SecurityAutoConfiguration
The SecurityAutoConfiguration imports two @Configuration classes that conditionally wire up the
security framework discussed with default implementations.
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import static org.springframework.security.config.Customizer.withDefaults;
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER) //very low priority
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws
Exception {
http.authorizeHttpRequests((requests) -> requests.anyRequest().
authenticated());
http.formLogin(withDefaults());
http.httpBasic(withDefaults());
return http.build();
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN) ②
@ConditionalOnClass(EnableWebSecurity.class) ①
@EnableWebSecurity
static class WebSecurityEnablerConfiguration {
}
14
7.3. WebSecurityConfiguration
WebSecurityConfiguration is imported by @EnableWebSecurity. It gathers all the SecurityFilterChain
beans for follow-on initialization into runtime FilterChains.
7.4. UserDetailsServiceAutoConfiguration
The UserDetailsServiceAutoConfiguration simply defines an in-memory UserDetailsService if one is
not yet present. This is one of the provided implementations mentioned earlier — but still just a
demonstration toy. The UserDetailsService is populated with one user:
spring.security.user.name: user
spring.security.user.password: password
7.5. SecurityFilterAutoConfiguration
The SecurityFilterAutoConfiguration establishes the springSecurityFilterChain filter chain,
implemented as a DelegatingFilterProxy. The delegate of this proxy is supplied by the details of the
SecurityAutoConfiguration.
15
Chapter 8. Default FilterChain
When we activated Spring security we added a level of filters to the Application Filter Chain. The
first was a DelegatingFilterProxy that lazily instantiated the filter using a delegate obtained from
the Spring application context. This delegate ends up being a FilterChainProxy which has a
prioritized list of SecurityFilterChain (implemented using DefaultSecurityFilterChain). Each
SecurityFilterChain has a requestMatcher and a set of zero or more Filters. Zero filters essentially
bypasses security for a particular URI pattern.
16
Chapter 9. Default Secured Application
With all that said — and all we really did was add an artifact dependency to the project — the
following shows where the AutoConfiguration left our application.
1. We entered http://localhost:8080/api/anonymous/hello?name=jim
2. Application saw there was no authentication for the session and redirected to /login page
If we call the endpoint from curl, without indicating we can visit an HTML page, we get flatly
rejected with a 401/UNAUTHORIZED. The response does inform us that BASIC Authentication is
available.
$ curl -v http://localhost:8080/api/authn/hello?name=jim
> GET /authn/hello?name=jim HTTP/1.1
< HTTP/1.1 401
< Set-Cookie: JSESSIONID=D124368C884557286BF59F70888C0D39; Path=/; HttpOnly
< WWW-Authenticate: Basic realm="Realm" ①
{"timestamp":"2020-07-01T23:32:39.909+00:00","status":401,
"error":"Unauthorized","message":"Unauthorized","path":"/authn/hello"}
If we add an Accept header to the curl request with text/html, we get a 302/REDIRECT to the login
page the browser automatically took us to.
17
Example 302/Redirect to FORM Login Page
$ curl -v http://localhost:8080/api/authn/hello?name=jim \
-H "Accept: text/plain,text/html" ①
> GET /authn/hello?name=jim HTTP/1.1
> Accept: text/plain, text/html
< HTTP/1.1 302
< Set-Cookie: JSESSIONID=E132523FE23FA8D18B94E3D55820DF13; Path=/; HttpOnly
< Location: http://localhost:8080/login
< Content-Length: 0
The login (URI /login) and logout (URI /logout) forms are supplied as defaults. If we use the
returned JSESSIONID when accessing and successfully completing the login form — we will
continue on to our originally requested URL.
Since we are targeting APIs — we will be disabling that very soon and relying on more stateless
authentication mechanisms.
① request with successful BASIC authentication gives us the results of intended URL
18
You can avoid the manual step of base64 encoding the username:password and
manually supplying the Authorization header with curl by using the plaintext -u
username:password option.
src/main/resources/application.properties
spring.security.user.name: user
spring.security.user.password: password
19
Example Authentication with Supplied Username/Password
20
< X-XSS-Protection: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: text/html;charset=UTF-8
< Content-Length: 10
< Date: Fri, 30 Aug 2024 22:16:48 GMT
<
hello, jim
Vary
indicates that content will be influenced by these headers. Intermediate/browser caches should
especially use the Origin value when making a cache key. This prevents a different Origin from
receiving a cached copy to the same resource and triggering a CORS error.
X-Content-Type-Options
informs the browser that supplied Content-Type header responses have been deliberately
[1]
assigned and to avoid Mime Sniffing (trying to determine for itself) — a problem caused by
servers serving uploaded content meant to masquerade as alternate MIME types.
X-XSS-Protection
a header that informs the browser what to do in the event of a Cross-Site Scripting attack is
[2]
detected. There seems to be a lot of skepticism of its value for certain browsers
Cache-Control
[3]
a header that informs the client how the data may be cached. This value can be set by the
controller response but is set to a non-cache state by default here.
Pragma
[4]
an HTTP/1.0 header that has been replaced by Cache-Control in HTTP 1.1.
Expires
a header that contains the date/time when the data should be considered stale and should be re-
[5]
validated with the server.
X-Frame-Options
[5]
informs the browser whether the contents of the page can be displayed in a frame. This helps
prevent site content from being hijacked in an unauthorized manner. This will not be pertinent
to our API responses.
21
Chapter 10. Default FilterChainProxy Bean
The above behavior was put in place by the default Security AutoConfiguration — which is
[1]
primarily placed within an instance of the FilterChainProxy class . This makes the
FilterChainProxy class a convenient place for a breakpoint when debugging security flows.
The FilterChainProxy is configured with a set of firewall rules that address such things as bad URI
expressions that have been known to hurt web applications and zero or more SecurityFilterChains
arranged in priority order (first match wins).
The default configuration has a single SecurityFilterChain that matches all URIs, requires
authentication, and also adds the other aspects we have seen so far.
Below is a list of filters put in place by the default configuration. This — by far — is not all the
available filters. I wanted to at least provide a description of the default ones before we start
looking to selectively configure the chain.
It is a pretty dry topic to just list them off. It would be best if you had the svc/svc-security/noauthn-
security-example example loaded in an IDE with:
22
Starter Activates Default Security Policies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
23
• a browser open with network logging active and ready to navigate to http://localhost:8080/api/
authn/hello?name=jim
Whenever we make a request in the default state - we will most likely visit the following filters.
DisableEncodedUrlFilter
An exit filter that cleanses URLs provided in the response header. It specifically looks for and
removes session IDs, which are not technically part of the URL and can be leaked into HTTP
access logs.
WebAsyncManagerIntegrationFilter
Establishes an association between the SecurityContext (where the current caller’s credentials
are held) and potential async responses making use of the Callable feature. Caller identity is
normally unique to a thread and obtained through a ThreadLocal. Anything completing in an
alternate thread must have a strategy to resolve the identity of this user by some other means.
SecurityContextHolderFilter
Manages SecurityContext in between calls. If appropriate — stores the SecurityContext and
clears it from the call on exit. If present — restores the SecurityContext on following calls.
HeaderWriterFilter
Issues standard headers (shown earlier) that can normally be set to a fixed value and optionally
overridden by controller responses.
CorsFilter
Implements Cross-Origin Resource Sharing restrictions that allow or disable exchange of
information with Web clients originating from a different DNS origin (schema, domain, port).
This is the source of Vary and Access-Control-Allow-Origin headers.
24
CsrfFilter
Checks all non-safe (POST, PUT, and DELETE) calls for a special Cross-Site Request Forgery (CSRF)
token either in the payload or header that matches what is expected for the session. This
attempts to make sure that anything that is modified on this site — came from this site and not a
malicious source. This does nothing for all safe (GET, HEAD, OPTIONS, and TRACE) methods.
LogoutFilter
Looks for calls to log out URI. If matches, it ends the login for all types of sessions, and
terminates the chain.
UsernamePasswordAuthenticationFilter
This instance of this filter is put in place to obtain the username and password submitted by the
login page. Therefore, anything that is not POST /login is ignored. The actual POST /login
requests have their username and password extracted, authenticated
DefaultLoginPageGeneratingFilter
Handles requests for the login URI (POST /login). This produces the login page, terminates the
chain, and returns to caller.
DefaultLogoutPageGeneratingFilter
Handles requests for the logout URI (GET /logout). This produces the logout page, terminates the
chain, and returns to the caller.
BasicAuthenticationFilter
Looks for BASIC Authentication header credentials, performs authentication, and continues the
flow if successful or if no credentials where present. If credentials were not successful it calls an
authentication entry point that handles a proper response for BASIC Authentication and ends
the flow.
Authentication Issues?
RequestCacheAwareFilter
This retrieves an original request that was redirected to a login page and continues it on that
path.
SecurityContextHolderAwareRequestFilter
Wraps the HttpServletRequest so that the security-related calls (isAuthenticated(), authenticate(),
login(), logout()) are resolved using the Spring security context.
AnonymousAuthenticationFilter
Populates the Spring security context with anonymous principal if no user is identified
ExceptionTranslationFilter
Attempts to map any thrown AccessDeniedException and AuthenticationException to an HTTP
Response. It does not add any extra value if those exceptions are not thrown. This will save the
25
current request (for access by RequestCacheAwareFilter) and commence an authentication for
AccessDeniedExceptions if the current user is anonymous. The saved current request will allow
the subsequent login to complete with a resumption of the original target. If FORM
Authentication is active — the commencement will result in a 302/REDIRECT to the /login URI.
AuthorizationFilter
Restricts access based upon the response from the assigned AuthorizationManager.
Authorization Issues?
Successfully reaching the end of the Security Filter Chain, this is where the SecurityFilter chain
hands control back to the original ApplicationFilterChain, where the endpoint will get invoked.
26
Chapter 11. Summary
In this module, we learned:
2. the purpose for and differences between encoding, encryption, and cryptographic hashes
8. to step through a series of calls through the Security filter chain for the ability to debug future
access problems
27