Proposal title
Date | 2013/02/24 |
Contact(s) | Jose García, Heikki Doeleman |
Last edited | |
Status | in progress |
Assigned to release | To be determined |
Resources | https://github.com/josegar74/core-geonetwork/tree/csrf |
Ticket # |
Overview
Improve the security in GeoNetwork, adding support to CSRF tokens to prevent Cross-Site Request Forgery. Also fixes have been done in user request parameters accepted in main page to prevent XSS attacks.
Proposal Type
- Type: Core Change
- App: GeoNetwork
- Module: Jeeves, Services
Links
- Documents:
- Email discussions:
- Other wiki discussions:
Voting History
- Vote proposed by X on Y, result was +/-n (m non-voting members).
Motivations
Improve the security in GeoNetwork to prevent CSRF (Cross-site request forgery) attacks. This type of attacks force an end user to execute unwanted actions on a web application in which he/she is currently authenticated. See additional information in https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
Additionally, improvements in main page have been done to prevent XSS attacks in user requests parameters.
Proposal
An effective way to prevent CSRF attacks it is to generate a random string (CSRF token) that is included in application forms. When the form is submitted, the CSRF token is send along the other form data, the server validates it before. approving the request for processing.
This way, prevents a malicious website from post a request even if have access to a valid session in a browser.
To implement this mechanism in GeoNetwork:
1) A new base service class for services that require CSRF (Cross Site Request Forgery) tokens: BaseSecureService
This class validates the CSRF token before processing the service:
- If it's not valid and exception
ServiceNotAllowedEx
is thrown. - If it's valid, the service processing continues.
package jeeves.services; public abstract class BaseSecureService implements Service { public final Element exec(Element params, ServiceContext context) throws Exception { if (!CSRFUtil.isValidToken(params, context)) { throw new ServiceNotAllowedEx("Service not allowed. CSRF Token is not valid"); } return doExec(params, context); } /** Services that require CSRF tokens must implement doExec instead of exec **/ protected abstract Element doExec(Element params, ServiceContext context) throws Exception; }
Services that require CSRF tokens must extend this class and implement the logic in doExec
method instead of exec
method.
package org.fao.geonet.services.user; public class Update extends BaseSecureService { public Element doExec(Element params, ServiceContext context) throws Exception { // Service logic } }
Example service requiring CSRF token: https://github.com/josegar74/core-geonetwork/blob/csrf/web/src/main/java/org/fao/geonet/services/user/Update.java
2) A new service to create/retrieve a CSRF token: secure.token
. This service is used to provide the secure token in services that create forms (see next point) and can be used from scripts that use actual GeoNetwork services that have been changed to require CSRF tokens. For example, a script that call metadata.category
to update the categories of metadata, will require to call first secure.token
to get the token and use it in metadata.category
calls.
CSRF tokens are created using SecureRandom
java class.
Code: https://github.com/josegar74/core-geonetwork/blob/csrf/jeeves/src/main/java/jeeves/services/GetSecureToken.java, https://github.com/josegar74/core-geonetwork/blob/csrf/jeeves/src/main/java/jeeves/utils/CSRFUtil.java
3) Services that create forms to submit data to services that require CSRF tokens validation require to add this token in the form to submit. This requires 2 changes:
- Update service definition to provide the CSRF token:
<service name="user.edit"> <class name=".services.user.Get" /> <output sheet="user-update.xsl"> <call name="groups" class=".guiservices.groups.GetMine" /> <call name="groupsAndProfiles" class=".guiservices.groups.GetMineWithProfiles" /> <call name="profiles" class="jeeves.guiservices.profiles.Get" /> <call name="_tk" class="jeeves.services.GetSecureToken" /> </output> </service>
- Add the token value as a hidden field in the form:
<form name="userupdateform" accept-charset="UTF-8" action="{/root/gui/locService}/user.update?operation=editinfo" method="post"> <input type="hidden" name="_tk" value="{/root/gui/_tk}"/>
Also as part of this work security improvements in main page user request parameters have been done to prevent XSS attacks: https://github.com/josegar74/core-geonetwork/commit/50f722f12c36e403ffa80724004e937db0ef2821
Useful resources:
- https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
- http://ricardozuasti.com/2012/preventing-csrf-in-java-web-apps
Backwards Compatibility Issues
User scripts using GeoNetwork services to update data will require to be updated to use secure.token
to retrieve the CSRF token and use it in the services. See Proposal description (point 2) for more details. These changes don't affect interfaces like CSW.
UI WIdgets requires to be changed to use the CSRF token.
New libraries added
Risks
Participants
- Jose García
- Heikki Doeleman