2013-03-21 Developers Forum



How to Join


Click here for information about joining the meeting in person or remotely.


By Browser

By telephone

  • US telephone number: +1 201.479.2627


  • Quickly review previous meeting minutes (5 min)
  • OpenMRS Security – reviewing feedback from January 2013 FLOSSHacks event
  • After-action review & next week's agenda (5 min)

In Attendance

  • You


Developers Forum 2013-03-21
  • OpenMRS Security – reviewing feedback from FLOSSHacks event
  • Michael Downey
  • Burke Mamlin
  • Nyoman Ribeka
  • Daniel Kayiwa
  • Saptarshi Purkayastha
  • Steve Githens
  • Wyclif Luyima
  • Andrea Patterson
  • Bryan Adams ♬
  • Darius Jazayeri
  • Paul Biondich
  • Lauren Stanisic
  • Ada Yeung
* Patient Display
* Patient Name
* Dimension Name
* Concept Name
* Form Fields name/description
* Locations
* JSESSIONIDs exposed in URL
* JSESSIONID assigned before login, used as session cookie after login.
* Lack of HTTPOnly flag on JSESSIONID cookies
Runtime Properties
* Created as world readable
* Unsafe location (/usr/share/tomcat7/.OpenMRS/ is bad, /var/lib/tomcat7/webapps/openrmrs/ would be better)
* Forgotten Password Form leaks valid usernames
* Weak login brute force mitigation + bug in parsing security.loginAttemptsAllowedPerIP
SQL Injection
* ConceptValidatorChangeSet.isNameUniqueInLocale
* UpdateFileParser.java
=== Issue #1: Reflected XSS in Patient Display ===
Credit: Kevin Jacobs
=== Issue #2: Multiple Stored XSS via Patient Name === (Steve)
Credit: Kevin Jacobs
The create patient flow is allows stored XSS when using the following
name (script is executed when loading mdrtbEditPatient.form, and
potentially other pages that display the patient name).
Other places where patient/user name injects script:
module/reporting/reports/reportHistory.form (xss in automatically populated into the "Requested By" dropdown)
admin/users/user.form (Which Person? textbox autocomplete fires it)
admin/encounters/encounter.form (same dropdown as above. Just fix this control)
patientDashboard.form (multiple places where the name is displayed. Main page, demographics tab.
Every page when logged in as the xss username ("Currently logged in as <script>..") (meh..)
Privilege escalation scenario:
Setup: Admin creates "Person" record and links it to a User account.
1. User can change his/her name to include an XSS string (steal session cookie)
2. User gets admin to visit their "Person" profile, i.e.
admin/person/person.form?personId=93483. Script could steal admin
cookie and send it to a malicious url.
=== Issue #3: Multiple Stored XSS via Dimension Name ===
  • Darius
Credit: Kevin Jacobs
module/reporting/indicators/manageDimensions.form (executes previously injected Dimension Name scripts)
reporting/indicators/editCohortDefinitionDimension: name and description parameters. XSS
=== Issue #4: Multiple Stored XSS via Concept Name ===
  • Andrea
  • TRUNK-3940
Credit: Kevin Jacobs
patientDashboard.form  (Graphs tab can display XSS'd Concept Name (search Find Concepts))
dictionary/concept.form (viewing XSS'd Concept Name)
=== Issue #5: JSESSIONIDs exposed in URL ===
  • Paul
Credit: Timothy D. Morgan
Upon first hitting the app:
=== Issue #6: Session Fixation ===
  • Wyclif
Credit: Timothy D. Morgan
JSESSIONID assigned before login, used as session cookie after login.
=== Issue #7: Locally Exposed Database Credentials ===
  • Daniel
  • TRUNK-3936
Credit: Timothy D. Morgan
This file contains application database credentials and is created world readable:
=== Issue #8: Stored XSS via Custom Form Fields ===
  • Burke
credit: Lauren
Partial POST:
POST /openmrs/admin/forms/formEdit.form HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:18.0)
Gecko/20100101 Firefox/18.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: JSESSIONID=5ADE5893C3F2E6B1FF89040BA35740AD;
Connection: keep-alive
Content-Type: multipart/form-data;
Content-Length: 1019
Content-Disposition: form-data; name="name"
Fields name and description verified to allow stored injection.
=== Issue #9: Forgotten Password Form Leaks Valid Usernames ===
  • Michael
credit: Timothy D. Morgan
Submitting invalid username returns "Invalid user or the secret
question has not been set. Please contact an administrator for help
resetting your password." while a valid username (but no secret
question) yields "Invalid user or the secret question has not been
set. Please contact an administrator for help resetting your
(Issue #10 done: TRUNK-3930)
=== Issue #10: Weak Login Brute Force Mitigation ===
credit: Kevin Jacobs
Login lock outs (LoginServelet.java, lines 82+):
Lock out after 100 attempts. Timer is reset after 5 minutes.
Recommend lockout after 5, (email to admin?).. require admin intervention to unlock.
Note: Default is defined in WebConstants.java can override the static 100 default:
 public static String GP_ALLOWED_LOGIN_ATTEMPTS_PER_IP = "security.loginAttemptsAllowedPerIP";
But the code expects an int, parsing fails, and sets to 100 attempts by default.
=== Issue #11: Stored XSS in Location Pages ===
  • Steve
credit: Kevin Jacobs
admin/locations/location.form - Create new location (<script>alert(1)</script> as name). Load this form, script is ran from Parent Location dropdown box.
admin/locations/locationTag.list - name, desc parameters
admin/locations/hierarchy.list - previously stored location name parameter.
Other Observations
Issue #12
=== Unsafe Location for Runtime Properties Under Linux ===
  • Paul
credit: Timothy D. Morgan
Under Debian Linux using the .war file, the application expects to be
able to write runtime properties and other information to:
This directory is not writable by the tomcat7 user, and for good
reason.  This could be viewed as a simple bug, fixable by a
permissions change, but in fact this leads to a security risk.  The
tomcat user should not have access to write to things under /usr.  If
it did, a single malicious/compromised web application may be able to
escalate privileges.  Certainly the .OpenMRS directory is somewhat
isolated, and a smart user would provide tomcat with permissions only
to this directory.  However, it isn't so far fetched to imagine a user
  chown -R tomcat7 /usr/share/tomcat7/
Ultimately, the runtime properties should be written to a sane place,
such as /var/lib/tomcat7/webapps/openmrs/.  Under normal conditions,
tomcat7 would have access to this.  I have attempted to override the
default behavior based on the environment variable mentioned here:
But this did not work.  It seems to ignore the environment variable.
Consider using Java System properties to determine the path to the
webapps dir, or the deployed application's directory and use that instead.
Issue #13
=== Potential Second Order SQLi in ConceptValidatorChangeSet.isNameUniqueInLocale ===
  • Burke
credit: Timothy D. Morgan
The escapeSqlWildcards method likely only escapes wildcard characters, not ' and the like.
        private boolean isNameUniqueInLocale(JdbcConnection connection, ConceptName conceptName, int conceptId) {
                int duplicates = getInt(connection,
                    "SELECT count(*) FROM concept_name cn, concept c WHERE cn.concept_id = c.concept_id  AND (cn.concept_name_type = '"
                            + ConceptNameType.FULLY_SPECIFIED
                            + "' OR cn.locale_preferred = '1') AND cn.voided = '0' AND cn.name = '"
                            + HibernateUtil.escapeSqlWildcards(conceptName.getName(), connection.getUnderlyingConnection())
                            + "' AND cn.locale = '"
                            + HibernateUtil.escapeSqlWildcards(conceptName.getLocale().toString(), connection
                                    .getUnderlyingConnection()) + "' AND c.retired = '0' AND c.concept_id != " + conceptId);
                return duplicates == 0;
Issue #14
=== Potential XXE via update.rdf Download ===
  • Wyclif
credit: Timothy D. Morgan
Update RDF files seem to be downloaded via HTTPS, which authenticates
them.  However, if these are ever distributed from an insecure source
or over an insecure channel, then they would likely be parsed with XML
external entity support enabled.  Relevant code in
         * Parse the contents of the update.rdf file.
         * @throws ModuleException
        public void parse() throws ModuleException {
                StringReader stringReader = null;
                try {
                        Document updateDoc = null;
                        try {
                                stringReader = new StringReader(content);
                                InputSource inputSource = new InputSource(stringReader);
                                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                                DocumentBuilder db = dbf.newDocumentBuilder();
                                updateDoc = db.parse(inputSource);
                        catch (Exception e) {
                                log.warn("Unable to parse content");
                                throw new ModuleException("Error parsing update.rdf file: " + content, e);
Issue #15
=== Lack of HTTPOnly Flag ===
  • Burke
Set the HTTPOnly flag on JSESSIONID cookies.  This will marginally
help to mitigate XSS exploits.



  • Backchannel IRC transcript
  • Audio recording of the call: Listen online or download - available after the meeting