[FOLIO-2564] investigate HTTP Response Header injection Created: 16/Apr/20  Updated: 13/Jul/21  Resolved: 05/May/20

Status: Closed
Project: FOLIO
Components: None
Affects versions: None
Fix versions: None

Type: Task Priority: P2
Reporter: Jakub Skoczen Assignee: Craig McNally
Resolution: Done Votes: 0
Labels: platform-backlog, security
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original estimate: Not Specified

Issue links:
Blocks
blocks FOLIO-2524 Security Audit raised issues Open
is blocked by MODNOTIFY-63 Q1 2020 release Closed
Relates
relates to RMB-478 RMB echoes all headers Closed
relates to OKAPI-763 Prevent X-Okapi-Token being returned ... Closed
relates to MODLOGIN-119 change login API to return tokens in ... Closed
Sprint: CP: sprint 87, CP: sprint 88
Story Points: 3
Development Team: Core: Platform

 Description   

Problem

The application reflects the value of any HTTP headers into the response headers. This may allow an attacker to insert arbitrary code into the response, including additional HTTP head- ers and potentially body content. An attacker can leverage HTTP response header injection to perform cross-site scripting, cross-user defacement, cache poisoning attacks and more. However, due to cross-origin setting of HTTP headers being blocked by the CORS policy, the severity of this finding is set to Medium.

Steps to verify

Perform a GET request with with a valid JWT and a set-cookie header and observe the set-cookie header in HTTP response, e.g:

GET /users?query=%28id%3D%3D%22f5f46a28-d34f-4c8d-9e7f-d88206141d12%22%20or%20id% 3D%3D%22a058f28f-80ac-4994-add6-e4d02fc238fe%22%20or%20id%3D%3D%22e19ae972-63 41-4451-9ca1-1f4aabfc986e%22%29
query urldecoded: (id=="f5f46a28-d34f-4c8d-9e7f-d88206141d12" or id=="a058f28f-80ac-4994-add6-e4d02fc238fe" or id=="e19ae972-6341-4451-9ca1-1f4aabfc986e")

Acceptance criteria
This problem has been addressed in RMB-478 Closed (commit 3ae1e2c, v27.1.2) and OKAPI () but the problem has been reported again during the NCC audit.

Ensure that:

  • that header reflection via GET no longer works
  • verify that all FOLIO backend modules have been upgraded to RMB with the problem solved (platform-complete) and create tickets in Jira for the modules with issues
  • verify that non-RMB FOLIO backend modules in platform-complete do not allow for header injection – test w X-Okapi header (token, tenant) and a standard header like Set-Cookie
  • review the fix implemented for RMB and Okapi, propose improvements (if any)

List of Q1 modules: https://docs.google.com/spreadsheets/d/1NvvCq1wTfDeCnd7zHDIzLI7RBfuSr_Ty0tbzYzgEaI8/edit#gid=0



 Comments   
Comment by Craig McNally [ 22/Apr/20 ]

I was able to reproduce this against an edelweiss deployment for a few modules:

  • mod-circulation-storage
  • mod-fees-fines
  • mod-notify
  • mod-login-saml

Example:

..elided...
> Set-Cookie: token=foobarbaz; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT
> 
< HTTP/1.1 200 OK
< Date: Wed, 22 Apr 2020 14:15:00 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< Server: nginx/1.10.3
< X-Okapi-Trace: GET mod-authtoken-2.4.0 http://<masked>:8051/mod-authtoken/loan-storage/loans.. : 202 8572us
< x-forwarded-for: <masked>
< x-forwarded-proto: http
< x-forwarded-port: 8051
< x-amzn-trace-id: Self=1-5ea05164-a3e7a75c3d2325a8ad153628;Root=1-5ea05164-45990650726085cc22435244
< x-nginx-proxy: true
< user-agent: curl/7.47.0
< accept: */*
< set-cookie: token=foobarbaz; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT
< x-okapi-request-id: 793543/loan-storage
< x-okapi-tenant: fs00000002
< x-okapi-url: http://<masked>:9130
< x-okapi-request-ip: <masked>
< x-okapi-request-timestamp: 1587564900099
< x-okapi-request-method: GET
< x-okapi-permissions: ["circulation-storage.loans.collection.get"]
< x-okapi-user-id: f9e1f652-efed-49c9-a3e4-34c3374047bc
< x-okapi-match-path-pattern: /loan-storage/loans
< X-Okapi-Trace: GET mod-circulation-storage-10.0.1 http://<masked>:8051/mod-circulation-storage/loan-storage/loans.. : 200 7253us
< 
{
  "loans" : [ ],
  "totalRecords" : 192
}

I did not try every module, but focused on those that had an older version of RMB (or weren't using RMB). The handful of modules I tested that were running RMB 29.X did not echo the request headers.

When retesting these modules against fameflower, I was able to verify that the problem has been addressed in most cases. However, there's at least one case where the problem still exists.

mod-circulation-storage

...elided...
> Set-Cookie: token=foobarbaz; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT
> X-Okapi-Tenant: diku
> X-Okapi-Foo: bar
> 
< HTTP/1.1 200 OK
< Date: Wed, 22 Apr 2020 14:30:10 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Okapi-Trace: GET mod-authtoken-2.4.0 http://10.36.1.37:9175/loan-storage/loans.. : 202 6586us
< X-Okapi-Trace: GET mod-circulation-storage-11.0.0 http://10.36.1.37:9176/loan-storage/loans.. : 200 2496us
< 
{
  "loans" : [ ],
  "totalRecords" : 5
* Connection #0 to host folio-fameflower-okapi.dev.folio.org left intact
}

mod-fees-fines

...elided...
> Set-Cookie: token=foobarbaz; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT
> X-Okapi-Tenant: diku
> X-Okapi-Foo: bar
> 
< HTTP/1.1 200 OK
< Date: Wed, 22 Apr 2020 14:31:23 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Okapi-Trace: GET mod-authtoken-2.4.0 http://10.36.1.37:9175/accounts.. : 202 32237us
< X-Okapi-Trace: GET mod-feesfines-15.7.2 http://10.36.1.37:9137/accounts.. : 200 19008us
< 
{
  "accounts" : [ ],
  "totalRecords" : 0,
  "resultInfo" : {
    "totalRecords" : 0,
    "facets" : [ ],
    "diagnostics" : [ ]
  }
* Connection #0 to host folio-fameflower-okapi.dev.folio.org left intact
}

mod-notify:

...elided...
> Set-Cookie: token=foobarbaz; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT
> X-Okapi-Tenant: diku
> X-Okapi-Foo: bar
> 
< HTTP/1.1 200 OK
< Date: Wed, 22 Apr 2020 14:32:37 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Okapi-Trace: GET mod-authtoken-2.4.0 http://10.36.1.37:9175/notify.. : 202 31585us
< x-forwarded-for: 24.63.116.35
< x-forwarded-proto: https
< x-forwarded-port: 443
< host: folio-fameflower-okapi.dev.folio.org
< x-amzn-trace-id: Root=1-5ea05585-9371801b241a16cb03fd3c7a
< user-agent: curl/7.47.0
< accept: */*
< set-cookie: token=foobarbaz; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT
< x-okapi-tenant: diku
< x-okapi-foo: bar
< x-okapi-request-id: 606342/notify
< x-okapi-url: http://10.36.1.37:9130
< x-okapi-request-ip: 10.36.10.74
< x-okapi-request-timestamp: 1587565957613
< x-okapi-request-method: GET
< x-okapi-permissions: ["notify.collection.get"]
< x-okapi-user-id: ff8a07c0-9985-50c4-82be-d71aca738738
< x-okapi-match-path-pattern: /notify
< X-Okapi-Trace: GET mod-notify-2.5.0 http://10.36.1.37:9162/notify.. : 200 206911us
< 
{
  "notifications" : [ ],
  "totalRecords" : 0
* Connection #0 to host folio-fameflower-okapi.dev.folio.org left intact
}

mod-licenses

...elided...
> Set-Cookie: token=foobarbaz; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT
> X-Okapi-Tenant: diku
> X-Okapi-Foo: bar
> 
< HTTP/1.1 200 OK
< Date: Wed, 22 Apr 2020 14:35:55 GMT
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Okapi-Trace: GET mod-authtoken-2.4.0 http://10.36.1.37:9175/licenses/licenses.. : 202 6735us
< X-Application-Context: application:production
< X-Okapi-Trace: GET mod-licenses-2.1.1 http://10.36.1.37:9164/licenses/licenses.. : 200 96036us
< 
{ [13986 bytes data]
* Connection #0 to host folio-fameflower-okapi.dev.folio.org left intact

mod-agreements

...elided...
> Set-Cookie: token=foobarbaz; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT
> X-Okapi-Tenant: diku
> X-Okapi-Foo: bar
> 
< HTTP/1.1 200 OK
< Date: Wed, 22 Apr 2020 14:38:18 GMT
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Okapi-Trace: GET mod-authtoken-2.4.0 http://10.36.1.37:9175/erm/sas.. : 202 31902us
< X-Application-Context: application:production
< X-Okapi-Trace: GET mod-agreements-2.2.0 http://10.36.1.37:9178/erm/sas.. : 200 28761us
< 
{ [791 bytes data]
* Connection #0 to host folio-fameflower-okapi.dev.folio.org left intact

mod-login-saml:

...elided...
> Set-Cookie: token=foobarbaz; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT
> X-Okapi-Tenant: diku
> X-Okapi-Foo: bar
> 
< HTTP/1.1 200 OK
< Date: Wed, 22 Apr 2020 14:40:01 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Okapi-Trace: GET mod-authtoken-2.4.0 http://10.36.1.37:9175/saml/check : 202 7417us
< X-Okapi-Trace: GET mod-login-saml-1.3.0 http://10.36.1.37:9153/saml/check : 200 20497us
< 
{ [28 bytes data]
* Connection #0 to host folio-fameflower-okapi.dev.folio.org left intact

mod-ncip

...elided...
> Set-Cookie: token=foobarbaz; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT
> X-Okapi-Tenant: diku
> X-Okapi-Foo: bar
> 
< HTTP/1.1 200 OK
< Date: Thu, 23 Apr 2020 15:26:20 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Okapi-Trace: GET mod-authtoken-2.4.0 http://10.36.1.37:9175/ncipconfigcheck : 202 6468us
< X-Okapi-Trace: GET mod-ncip-1.1.1 http://10.36.1.37:9179/ncipconfigcheck : 200 265768us
< 
* Connection #0 to host folio-fameflower-okapi.dev.folio.org left intact
OK

mod-user-import

...elided...
> Set-Cookie: token=foobarbaz; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT
> X-Okapi-Tenant: diku
> X-Okapi-Foo: bar
> Content-Length: 779
> 
* upload completely sent off: 779 out of 779 bytes
< HTTP/1.1 200 OK
< Date: Thu, 23 Apr 2020 15:54:34 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Okapi-Trace: POST mod-authtoken-2.4.0 http://10.36.1.37:9175/user-import : 202 7621us
< X-Okapi-Trace: POST mod-user-import-3.2.0 http://10.36.1.37:9142/user-import : 200 899483us
< 
{
  "message" : "Users were imported successfully.",
  "createdRecords" : 0,
  "updatedRecords" : 0,
  "failedRecords" : 1,
  "failedUsers" : [ {
    "username" : "jhandey",
    "externalSystemId" : "source1_111_112",
    "errorMessage" : "Failed to create new user with externalSystemId: source1_111_112"
  } ],
  "totalRecords" : 1
* Connection #0 to host folio-fameflower-okapi.dev.folio.org left intact
}
Comment by Craig McNally [ 27/Apr/20 ]

The only module I found that still had this issue in the fameflower release (mod-notify) has already been fixed. I verified this on folio-testing:

...elided...
> set-cookie: token=foobarbaz; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT
> X-Okapi-Tenant: diku
> X-Okapi-foo: bar
> 
< HTTP/1.1 200 OK
< Date: Mon, 27 Apr 2020 12:28:15 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Okapi-Trace: GET mod-authtoken-2.5.0-SNAPSHOT.66 http://10.36.1.210:9133/notify : 202 8019us
< X-Okapi-Trace: GET mod-notify-2.6.0-SNAPSHOT.91 http://10.36.1.210:9143/notify : 200 261488us
< 
{
  "notifications" : [ ],
  "totalRecords" : 0
* Connection #0 to host folio-testing-okapi.aws.indexdata.com left intact
}
Comment by Hongwei Ji [ 27/Apr/20 ]

Looks good. Just want to clear, for q2 release, are there any modules that need to upgrade RMB due to this header injection issue?

Comment by Craig McNally [ 28/Apr/20 ]

The latest release of mod-notify is 2.5.0, which uses an older RMB (26.2.2). So yes, a mod-notify release will need to be made that includes a fix for this security issue.

Comment by Jakub Skoczen [ 04/May/20 ]

Craig McNally Oleksii Petrenko The remaining problem in mod-notify has been fixed on the master branch, we need to make sure the module gets released for Goldenrod.

Comment by Oleksii Popov [ 05/May/20 ]

What is left: ping release coordinator about module release.

Generated at Thu Feb 08 23:21:35 UTC 2024 using Jira 1001.0.0-SNAPSHOT#100246-sha1:7a5c50119eb0633d306e14180817ddef5e80c75d.