When we develop an application security is something to take into account from the design and never forget about it!
If we detect a bug at the beginning of the development it will have a much lower cost than if we detect it later in production.
The idea is to consider security when we are going to develop applications, thinking about what bugs or security needs our application could have from the moment we start to develop it.
First recommendations:
OWASP stands for Open Web Application Security Project. It is an open web application security project, a non-profit organization that supports infrastructure and IT security projects related to web applications.
Among the top ten security risks we have:
We will not go into detail on each one, but we will focus on the most common ones and how some tools help us to prevent them.
Despite being one of the most known vulnerabilities, it is still in the TOP TEN.
Note: A SQL injection is a type of attack in which a piece of SQL code is used to manipulate a database and access valuable information.
Typical example
Suppose we want to get a user’s purchases by their id. To do this we can put together a function like the following:
public List<CompraDTO> obtenerComprasPorUsuarioId(String usuarioId) throws SQLException {
String sql = «select * »
+ «from Compra where usuario_id = ‘»
+ usuarioId
+ «‘»;
Connection c = dataSource.getConnection();
ResultSet rs = c.createStatement().executeQuery(sql);
// …
Now, if we set out to exploit the vulnerability in this code we could do:
curl -X GET \
‘http://localhost:8080/compras?usuarioId=123%27%20or%20%271%27=%271’ \
123 or ‘1’ = ‘1
And this ends up translating to:
select * from Compra where usuarioId = ‘123’ or ‘1’ = ‘1’
While this example is pretty DUMB, there are situations where we may want to concatenate strings to put together a query:
Furthermore we may think that by using JPA we are saved from SQL injection but in reality JPA and ORMs do not prevent us from writing vulnerable code.
We can perfectly do something like what we see in the following example:
public List<PurchaseDTO> getPurchaseByUserId(String userId){
String jql = «Select c from Purchase c where c.userId = ‘» + userId + «‘»;
TypedQuery<Purchase> q = em.createQuery(jql, Purchase.class);
return q.getResultList()
.stream()
.map(this::toCompraDTO)
.collect(Collectors.toList());
We are using an unvalidated data input to create a JPA query so we are still exposed to the same type of vulnerability.
The solution comes from the side of using parameterizable queries.
Simple solution
public List<CompraDTO> getComprasPorUserId(String userId) throws Exception {
String sql = «select * from Purchase »
+ «where user_id = ?»;
Connection c = dataSource.getConnection();
PreparedStatement p = c.prepareStatement(sql);
p.setString(1, customerId);
ResultSet rs = p.executeQuery(sql));
……
Solution with JPA
String jql = «Select c from Compra c where c.usuarioId = :usuarioId»;
TypedQuery<Compra> q = em.createQuery(jql, Compra.class)
.setParameter(«usuarioId», usuarioId);
A good option can be to sanitize data, that is to make it safe.
private static final Set<String> VALID_COLUMNS_FOR_ORDER_BY = Collections.unmodifiableSet(Stream
.of(«importe»,»fecha»)
.collect(Collectors.toCollection(HashSet::new)));
public List<CompraDTO> obtenerComprasPorUsuarioId(String usuarioId, String orderBy) throws Exception {
String sql = «select * from Compra where usuario_id = ? «;
if (VALID_COLUMNS_FOR_ORDER_BY.contains(orderBy)) {
sql = sql + » order by » + orderBy;
} else {
throw new IllegalArgumentException(«Buen intento!»);
}
Connection c = dataSource.getConnection();
PreparedStatement p = c.prepareStatement(sql);
p.setString(1,customerId);
Now let’s move on to Spring…
Another common type of attack comes from our session side. If we use Spring we can control exactly when our session is created and how Spring Security will interact with it. For that we have the values:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
This configuration only controls what Spring Security does, not the whole application.
When a user who is already authenticated tries to authenticate again, the application can deal with that event in one of several ways. It can invalidate the user’s active session and re-authenticate the user with a new session, or allow both sessions to exist concurrently.
The first step in enabling concurrent session control support is:
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
This is very important to ensure that you are notified when the session is destroyed .
To allow multiple concurrent sessions for the same user we do the following:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().maximumSessions(2)
}
After the session times out, if the user submits a request with an expired session id, he/she will be redirected to a previously configured URL
http.sessionManagement()
.expiredUrl(«/sessionExpired.html»)
.invalidSessionUrl(«/invalidSession.html»);
We can set the session timeout value of the embedded server session using properties:
server.servlet.session.timeout=15m
Exposing session information in the URL is a security risk. For that we can choose where to store the JSESSIONID.
servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));
This chooses where to store the JSESSIONID : in the cookie or in a URL parameter.
Session fixation is a very common type of attack in which it is possible for an attacker to create a session by accessing a site and then have another user log in with the same session (by sending them a link containing the session identifier as a parameter, for example).
To prevent this attack we can:
Accept only server-generated session IDs.
Implement a robust logout feature.
http.sessionManagement()
.sessionFixation().migrateSession()
By default, Spring Security has this protection enabled («migrateSession «): on authentication a new HTTP session is created, the old one is invalidated and the attributes of the old one are copied.
If this is not the desired behavior, two other options are available:
Session pinning policy | Description |
none() | Spring Security will disable session fixation protection
|
migrateSession() | When the user has successfully authenticated, a new session will be created and move the values from the previous session to the new session. (Default and applicable for most cases)
|
newSession() | When the user is successfully authenticated, a new session is created and does not copy any attributes from the previous session.
|
changeSessionId() | This is done using the new Servlet containers (Servlet 3.1 and later). It will not create a new session after user authentication, but will change the session ID.
|
The Secure attribute ensures that cookies are sent via HTTPS, while the HttpOnly attribute does not allow scripts to be executed or cookies to be accessed via the DOM.
It is always recommended to implement these attributes to reduce the possibility of attack.
We can use HTTPOnly and secure flags to secure our session cookie:
In the application.properties
server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true
1 2 3 |
server.servlet.session.cookie.http-only=true server.servlet.session.cookie.secure=true |
Incorrect control of these methods can allow an attacker to modify or delete server resources.
If any of them are needed, it is advisable to handle accesses with a whitelist by returning «405 Method not allowed» for disallowed accesses.
For those methods that are not needed, it is recommended to disable them.
A web application firewall (WAF) is a type of firewall that monitors, filters or blocks HTTP traffic to and from a web application.
For the review of HTTP traffic, the WAF applies a set of rules (defined in advance) to carry out the detection of malformed HTTP requests, web attacks.
A firewall protects a network, but if you are hosting web applications, you should definitely consider a WAF. It is important to note that a WAF does not replace a Firewall; they are independent devices or functions that complement each other.
It is important to keep security in mind from the beginning and maintain it throughout the development process! Here are some recommendations for Spring applications that we suggest you follow.