-
Notifications
You must be signed in to change notification settings - Fork 4
/
proxy.php
243 lines (211 loc) · 10.8 KB
/
proxy.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
<?php
/****************************************************************
PHP-BASED I2B2 PROXY "CELL"
Author: Nick Benik
Contributors: Nich Wattanasin
Mike Mendis
Last Revised: 04-26-23
*****************************************************************
This file acts as a simple i2b2 proxy cell. If no variables have been sent it is assumed that the request is from a
user's Web browser requesting the default page for the current directory. In this case, this file will read the
contents of the default.htm file and return its contents to the browser via the current HTTP connection.
New Feature: 01-27-16 (nw096):
- the $WHITELIST has been reworked to read i2b2_config_data.js and detect the hostname of where i2b2 lives
- the hostname that the web client is running on is also added to the $WHITELIST, in case i2b2 lives there
** If there are other cells/URLs that you connect to that is not where your PM Cell lives, you will need
to add that server's hostname to the $WHITELIST array below.
Update: 05-03-17 (nw096):
- the automatic detection of $WHITELIST URLs from i2b2_config_data.js now supports ports (bug fix)
*/
$pmURL = "http://127.0.0.1:8080/i2b2/rest/PMService/getServices";
$pmCheckAllRequests = true;
$WHITELIST = array(
"http" . (($_SERVER['SERVER_PORT'] == '443') ? 's' : '' ) . "://" . $_SERVER['HTTP_HOST'],
"http://services.i2b2.org",
"http://127.0.0.1:9090",
"http://127.0.0.1:8080",
"http://127.0.0.1",
"http://localhost:8080",
"http://localhost:9090",
"http://localhost"
);
$BLACKLIST = array(
"http://127.0.0.1:9090/test",
"http://localhost:9090/test"
);
// There is nothing to configure below this line
// ===========================================================================
session_start();
$_SESSION["shib-session-id"] = filter_input(INPUT_SERVER, 'AJP_Shib-Session-ID', FILTER_UNSAFE_RAW);
$_SESSION["eppn"] = filter_input(INPUT_SERVER, 'AJP_eduPersonPrincipalName', FILTER_UNSAFE_RAW);
$matches = array();
$config_file = fopen("i2b2_config_domains.json", "r");
if ($config_file) {
while (($line = fgets($config_file)) !== false) {
if(strpos($line, "urlCellPM:") !== false) $matches[] = $line;
}
fclose($config_file);
}
foreach($matches as $match){
$match = preg_replace('/\s+/', '', $match); // remove all whitespace
$match = rtrim($match, ','); // remove trailing comma, if any
$regex = "/(http|https)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,5}(\:[0-9]{2,5})*\/?/";
if (preg_match($regex, $match, $url)) { // match hostname
array_push($WHITELIST, $url[0]);
}
}
$PostBody = file_get_contents("php://input");
if ($PostBody=="") {
// no POST variables sent, assume this is user navigation
// load the inital page "default.htm"
$IndexFile = dirname($_SERVER["SCRIPT_FILENAME"]).'/index.html';
if (!file_exists($IndexFile) || !is_file($IndexFile)) {
die("The initial HTML file does not exist!");
} else {
// read and passthru the file contents to the browser
readfile($IndexFile);
}
} else {
// Process the POST for proxy redirection
// Validate that POST data is XML and extract <proxy> tag
// SECURITY FIX: Prevent processing of any prepended user/pass when extracting the proxy URL
$startPos = strpos($PostBody,"<redirect_url>") + 14;
$endPos = strpos($PostBody,"</redirect_url>", $startPos);
$proxyURL = substr($PostBody, $startPos, ($endPos - $startPos));
$sec_fix = parse_url($proxyURL);
$sec_invalid_url = false;
if ($sec_fix == false) {
// a *very* badly formed URL
$sec_invalid_url = true;
} else {
if ($sec_fix['scheme'] != null) {
$proxyURL = $sec_fix['scheme'];
// we are silently dropping any PHP_URL_USER and/or PHP_URL_PASS
if ($sec_fix['host'] != null) {
$proxyURL = $proxyURL . "://" . $sec_fix['host'];
if ($sec_fix['port'] != null) {
$proxyURL = $proxyURL . ":" . $sec_fix['port'];
}
if ($sec_fix['path'] != null) {
$proxyURL = $proxyURL . $sec_fix['path'];
}
if ($sec_fix['query'] != null) {
$proxyURL = $proxyURL . "?" . $sec_fix['query'];
}
} else {
$sec_invalid_url = true;
}
} else {
$sec_invalid_url = true;
}
}
if ($sec_invalid_url) {
// security has failed - exit here as the proxy URL is malformed
die("The proxy URL is malformed!");
}
// ---------------------------------------------------
// white-list processing on the URL
// ---------------------------------------------------
$isAllowed = false;
$requestedURL = strtoupper($proxyURL);
foreach ($WHITELIST as $entryValue) {
$checkValue = strtoupper(substr($requestedURL, 0, strlen($entryValue)));
if ($checkValue == strtoupper($entryValue)) {
$isAllowed = true;
break;
}
}
if (!$isAllowed) {
// security as failed - exit here and don't allow one more line of execution the opportunity to reverse this
die("The proxy has refused to relay your request.");
}
// ---------------------------------------------------
// black-list processing on the URL
// ---------------------------------------------------
foreach ($BLACKLIST as $entryValue) {
$checkValue = strtoupper(substr($requestedURL, 0, strlen($entryValue)));
if ($checkValue == strtoupper($entryValue)) {
// security as failed - exit here and don't allow one more line of execution the opportunity to reverse this
die("The proxy has refused to relay your request.");
}
}
$newXML = $PostBody;
// Do not allow DOCTYPE declarations
$replace_match = '/^.*(?:!DOCTYPE).*$(?:\r\n|\n)?/m';
if (preg_match($replace_match, $newXML)) {
exit('DOCTYPE not allowed to be proxied');
}
if ($pmCheckAllRequests) {
error_log("Searhing for Security in " . $PostBody);
//Validate that user is valid against known PM
preg_match("/<security(.*)?>(.*)?<\/security>/", $PostBody, $proxySecurity);
error_log("My Security is " . $proxySecurity[1]);
preg_match("/<domain(.*)?>(.*)?<\/domain>/", $proxySecurity[0], $proxyDomain);
preg_match("/<username(.*)?>(.*)?<\/username>/", $proxySecurity[0], $proxyUsername);
preg_match("/<password(.*)?>(.*)?<\/password>/", $proxySecurity[0], $proxyPassword);
$checkPMXML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><i2b2:request xmlns:i2b2=\"http://www.i2b2.org/xsd/hive/msg/1.1/\" xmlns:pm=\"http://www.i2b2.org/xsd/cell/pm/1.1/\"> <message_header> <i2b2_version_compatible>1.1</i2b2_version_compatible> <hl7_version_compatible>2.4</hl7_version_compatible> <sending_application> <application_name>i2b2 Project Management</application_name> <application_version>1.1</application_version> </sending_application> <sending_facility> <facility_name>i2b2 Hive</facility_name> </sending_facility> <receiving_application> <application_name>Project Management Cell</application_name> <application_version>1.1</application_version> </receiving_application> <receiving_facility> <facility_name>i2b2 Hive</facility_name> </receiving_facility> <datetime_of_message>2007-04-09T15:19:18.906-04:00</datetime_of_message> <security> " . $proxyDomain[0] . $proxyUsername[0] . $proxyPassword[0] . " </security> <message_control_id> <message_num>0qazI4rX6SDlQlk46wqQ3</message_num> <instance_num>0</instance_num> </message_control_id> <processing_id> <processing_id>P</processing_id> <processing_mode>I</processing_mode> </processing_id> <accept_acknowledgement_type>AL</accept_acknowledgement_type> <application_acknowledgement_type>AL</application_acknowledgement_type> <country_code>US</country_code> <project_id>undefined</project_id> </message_header> <request_header> <result_waittime_ms>180000</result_waittime_ms> </request_header> <message_body> <pm:get_user_configuration> <project>undefined</project> </pm:get_user_configuration> </message_body></i2b2:request>";
// Process the POST for proxy redirection
error_log($checkPMXML,0 );
error_log("My proxy: " . $proxyURL, 0);
}
if ($pmCheckAllRequests) {
// open the URL and forward the new XML in the POST body
$proxyRequest = curl_init($pmURL);
// these options are set for hyper-vigilance purposes
curl_setopt($proxyRequest, CURLOPT_COOKIESESSION, 0);
curl_setopt($proxyRequest, CURLOPT_FORBID_REUSE, 1);
curl_setopt($proxyRequest, CURLOPT_FRESH_CONNECT, 0);
// Specify NIC to use for outgoing connection, fixes firewall+DMZ headaches
// curl_setopt($proxyRequest, CURLOPT_INTERFACE, "XXX.XXX.XXX.XXX");
// other options
curl_setopt($proxyRequest, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($proxyRequest, CURLOPT_CONNECTTIMEOUT, 900); // wait 15 minutes
// data to proxy thru
curl_setopt($proxyRequest, CURLOPT_POST, 1);
curl_setopt($proxyRequest, CURLOPT_POSTFIELDS, $checkPMXML);
// SEND REQUEST!!!
curl_setopt($proxyRequest, CURLOPT_HTTPHEADER, array('Expect:', 'Content-Type: text/xml'));
$proxyResult = curl_exec($proxyRequest);
// cleanup cURL connection
curl_close($proxyRequest);
error_log("My PM Result " . $proxyResult);
$pattern = "/<status type=\"ERROR\">/i";
//Check if request is valid
if (preg_match($pattern, $proxyResult)) {
error_log("Local PM denied request");
die("Local PM server could not validate the request.");
}
}
// open the URL and forward the new XML in the POST body
$proxyRequest = curl_init($proxyURL);
curl_setopt($proxyRequest, CURLOPT_SSL_VERIFYPEER, FALSE);
// these options are set for hyper-vigilance purposes
curl_setopt($proxyRequest, CURLOPT_COOKIESESSION, 0);
curl_setopt($proxyRequest, CURLOPT_FORBID_REUSE, 1);
curl_setopt($proxyRequest, CURLOPT_FRESH_CONNECT, 0);
// Specify NIC to use for outgoing connection, fixes firewall+DMZ headaches
// curl_setopt($proxyRequest, CURLOPT_INTERFACE, "XXX.XXX.XXX.XXX");
// other options
curl_setopt($proxyRequest, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($proxyRequest, CURLOPT_CONNECTTIMEOUT, 900); // wait 15 minutes
// data to proxy thru
curl_setopt($proxyRequest, CURLOPT_POST, 1);
curl_setopt($proxyRequest, CURLOPT_POSTFIELDS, $newXML);
// Handle headers manually - copy over "AJP_" data
$headers = array('Expect:', 'Content-Type: text/xml');
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 4) === "AJP_") {
$header = str_replace('AJP_', 'X-', $key) . ": " . $value;
array_push($headers, $header);
}
}
curl_setopt($proxyRequest, CURLOPT_HTTPHEADER, $headers);
// SEND THE REQUEST
$proxyResult = curl_exec($proxyRequest);
// cleanup cURL connection
curl_close($proxyRequest);
// perform any analysis or processing on the returned result here
header("Content-Type: text/xml", true);
print($proxyResult);
}
?>