forked from openemr/openemr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
formdata.inc.php
315 lines (291 loc) · 13.1 KB
/
formdata.inc.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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
<?php
/**
* Functions to globally validate and prepare data for sql database insertion.
*
* @package OpenEMR
* @link https://www.open-emr.org
* @author Rod Roark <[email protected]>
* @author Brady Miller <[email protected]>
* @copyright Copyright (c) 2009 Rod Roark <[email protected]>
* @copyright Copyright (c) 2018 Brady Miller <[email protected]>
* @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
*/
/**
* Escape a parameter to prepare for a sql query.
*
* @param string $s Parameter to be escaped.
* @return string Escaped parameter.
*/
function add_escape_custom($s)
{
//prepare for safe mysql insertion
$s = mysqli_real_escape_string($GLOBALS['dbh'], ($s ?? ''));
return $s;
}
/**
* Escape a sql limit variable to prepare for a sql query.
*
* This will escape integers within the LIMIT ?, ? part of a sql query.
* Note that there is a maximum value to these numbers, which is why
* should only use for the LIMIT ? , ? part of the sql query and why
* this is centralized to a function (in case need to upgrade this
* function to support larger numbers in the future).
*
* @param string $s Limit variable to be escaped.
* @return string Escaped limit variable.
*/
function escape_limit($s)
{
//prepare for safe mysql insertion
$s = (int)$s;
return $s;
}
/**
* Escape/sanitize a sql sort order keyword variable to prepare for a sql query.
*
* This will escape/sanitize the sort order keyword. It is done by whitelisting
* only certain keywords(asc,desc). If the keyword is illegal, then will default
* to asc.
*
* @param string $s Sort order keyword variable to be escaped/sanitized.
* @return string Escaped sort order keyword variable.
*/
function escape_sort_order($s)
{
return escape_identifier(strtolower($s), array("asc","desc"));
}
/**
* If parameter string contains comma(,) delimeter
* Splits parameter string into an array, using comma(,) as delimeter
* else it returns original string
*
* @param string $s string to be processed
* @return array $columns an array formed by spliting $s with comma(,) delimeter
*/
function process_cols_escape($s)
{
//returns an array of columns
$columns = explode(",", $s);
if (count($columns) > 1) {
return $columns;
}
return $s;
}
/**
* Escape/sanitize a table sql column name for a sql query..
*
* This will escape/sanitize the sql column name for a sql query. It is done by whitelisting
* all of the current sql column names in the openemr database from a table(s). Note that if
* there is no match, then it will die() and a error message will be sent to the screen and
* the error log. This function should not be used for escaping tables outside the openemr
* database (should use escape_identifier() function below for that scenario)
*
* @param string|array $s sql column name(s) variable to be escaped/sanitized.
* @param array $tables The table(s) that the sql columns is from (in an array).
* @param boolean $long Use long form (ie. table.colname) vs short form (ie. colname).
* @param boolean $throwException Whether to throw a SQL exception instead of dieing
* @return string Escaped table name variable.
*/
function escape_sql_column_name($s, $tables, $long = false, $throwException = false)
{
// If $s is asterisk return asterisk to select all columns
if ($s === "*") {
return "*";
}
// If $s is an array process then use recursion to check each column
if (is_array($s)) {
$multiple_columns = [];
foreach ($s as $column) {
$multiple_columns[] = escape_sql_column_name(trim($column), $tables);
}
return implode(", ", $multiple_columns);
}
// If the $tables is empty, then process them all
if (empty($tables)) {
$res = sqlStatementNoLog("SHOW TABLES");
$tables = array();
while ($row = sqlFetchArray($res)) {
$keys_return = array_keys($row);
$tables[] = $row[$keys_return[0]];
}
}
// First need to escape the $tables
$tables_escaped = array();
foreach ($tables as $table) {
$tables_escaped[] = escape_table_name($table);
}
// Collect all the possible sql columns from the tables
$columns_options = array();
foreach ($tables_escaped as $table_escaped) {
$res = sqlStatementNoLog("SHOW COLUMNS FROM " . $table_escaped);
while ($row = sqlFetchArray($res)) {
if ($long) {
$columns_options[] = $table_escaped . "." . $row['Field'];
} else {
$columns_options[] = $row['Field'];
}
}
}
// Now can escape(via whitelisting) the sql column name
$dieIfNoMatch = !$throwException;
return escape_identifier($s, $columns_options, $dieIfNoMatch, true, $throwException);
}
/**
* Escape/sanitize a table name for a sql query. This function can also can be used to
* process tables that contain any upper case letters.
*
* This will escape/sanitize the table name for a sql query. It is done by whitelisting
* all of the current tables in the openemr database. The matching is not case sensitive,
* although it will attempt a case sensitive match before proceeding to a case insensitive
* match (see below escape_identifier() function for more details on this). Note that if
* there is no match, then it will die() and a error message will be sent to the screen
* and the error log. This function should not be used for escaping tables outside the
* openemr database (should use escape_identifier() function below for that scenario).
* Another use of this function is to deal with casing issues that arise in tables that
* contain upper case letter(s) (these tables can be huge issues when transferring databases
* from Windows to Linux and vice versa); this function can avoid this issues if run the
* table name through this function (To avoid confusion, there is a wrapper function
* entitled mitigateSqlTableUpperCase() that is used when just need to mitigate casing
* for table names that contain any uppercase letters).
* @param string $s sql table name variable to be escaped/sanitized.
* @return string Escaped table name variable.
*/
function escape_table_name($s)
{
$res = sqlStatementNoLog("SHOW TABLES");
$tables_array = array();
while ($row = sqlFetchArray($res)) {
$keys_return = array_keys($row);
$tables_array[] = $row[$keys_return[0]];
}
// Now can escape(via whitelisting) the sql table name
return escape_identifier($s, $tables_array, true, false);
}
/**
* Process tables that contain any upper case letters; this is simple a wrapper function of
* escape_table_name() above when using it for the sole purpose of mitigating sql table names
* that contain upper case letters.
*
* @param string $s sql table name variable to be escaped/sanitized.
* @return string Escaped table name variable.
*/
function mitigateSqlTableUpperCase($s)
{
return escape_table_name($s);
}
/**
* Escape/sanitize a sql identifier variable to prepare for a sql query.
*
* This will escape/sanitize a sql identifier. There are two options provided by this
* function.
* The first option is done by whitelisting ($whitelist_items is array) and in this case
* only certain identifiers (listed in the $whitelist_items array) can be used; if
* there is no match, then it will either default to the first item in the $whitelist_items
* (if $die_if_no_match is FALSE) or it will die() and send an error message to the screen
* and log (if $die_if_no_match is TRUE). Note there is an option to allow case insensitive
* matching; if this option is chosen, it will first attempt a case sensitive match and if this
* fails, then attempt a case insensitive match.
* The second option is done by checking against a regex expression, which would use as a string
* in $whitelist_items (for example, 'a-zA-Z0-9_'). If $die_if_no_match is true, then will die
* if any illegal characters are found. If $die_if_no_match is false, then will remove the illegal
* characters and send back string of only the legal characters.
* The first option is ideal if all the possible identifiers are known, however we realize this
* may not always be the case.
*
* @param string $s Sql identifier variable to be escaped/sanitized.
* @param array/string $whitelist_items Items used in whitelisting method (See function description for details of whitelisting method).
* Standard use is to use a array. If use a string, then should be regex expression of allowed
* characters (for example 'a-zA-Z0-9_').
* @param boolean $die_if_no_match If there is no match in the whitelist, then die and echo an error to screen and log.
* @param boolean $case_sens_match Use case sensitive match (this is default).
* @param boolean $throw_exception_if_no_match If there is no match in the whitelist then throw an exception
* @return string Escaped/sanitized sql identifier variable.
*/
function escape_identifier($s, $whitelist_items, $die_if_no_match = false, $case_sens_match = true, $throw_exception_if_no_match = false)
{
if (is_array($whitelist_items)) {
// Only return an item within the whitelist_items
$ok = $whitelist_items;
// First, search for case sensitive match
$key = array_search($s, $ok);
if ($key === false) {
// No match
if (!$case_sens_match) {
// Attempt a case insensitive match
$ok_UPPER = array_map("strtoupper", $ok);
$key = array_search(strtoupper($s), $ok_UPPER);
}
if ($key === false) {
// Still no match
if ($die_if_no_match) {
// No match and $die_if_no_match is set, so die() and send error messages to screen and log
error_Log("ERROR: OpenEMR SQL Escaping ERROR of the following string: " . errorLogEscape($s), 0);
die("<br /><span style='color:red;font-weight:bold;'>" . xlt("There was an OpenEMR SQL Escaping ERROR of the following string") . " " . text($s) . "</span><br />");
} else if ($throw_exception_if_no_match) {
throw new \OpenEMR\Common\Database\SqlQueryException("", "ERROR: OpenEMR SQL Escaping ERROR of the following string: " . errorLogEscape($s));
} else {
// Return first token since no match
$key = 0;
}
}
}
return $ok[$key];
} else {
if ($die_if_no_match) {
if (preg_match('/[^' . $whitelist_items . ']/', $s)) {
// Contains illegal character and $die_if_no_match is set, so die() and send error messages to screen and log
error_Log("ERROR: OpenEMR SQL Escaping ERROR of the following string: " . errorLogEscape($s), 0);
die("<br /><span style='color:red;font-weight:bold;'>" . xlt("There was an OpenEMR SQL Escaping ERROR of the following string") . " " . text($s) . "</span><br />");
} else if ($throw_exception_if_no_match) {
throw new \OpenEMR\Common\Database\SqlQueryException("", "ERROR: OpenEMR SQL Escaping ERROR of the following string: " . errorLogEscape($s));
} else {
// Contains all legal characters, so return the legal string
return $s;
}
} else {
// Since not using $die_if_no_match, then will remove the illegal characters and send back a legal string
return preg_replace('/[^' . $whitelist_items . ']/', '', $s);
}
}
}
/**
* (Note this function is deprecated for new scripts and is only utilized to support legacy scripts)
* Function to manage POST, GET, and REQUEST variables.
*
* @param string $name name of the variable requested.
* @param string $type 'P', 'G' for post or get data, otherwise uses request.
* @param bool $istrim whether to use trim() on the data.
* @return string variable requested, or empty string
*/
function formData($name, $type = 'P', $isTrim = false)
{
if ($type == 'P') {
$s = isset($_POST[$name]) ? $_POST[$name] : '';
} elseif ($type == 'G') {
$s = isset($_GET[$name]) ? $_GET[$name] : '';
} else {
$s = isset($_REQUEST[$name]) ? $_REQUEST[$name] : '';
}
return formDataCore($s, $isTrim);
}
/**
* (Note this function is deprecated for new scripts and is only utilized to support legacy scripts)
* NEED TO KEEP THIS FUNCTION TO ENSURE LEGACY FORMS ARE SUPPORTED
* Core function that will be called by formData.
* Note it can also be called directly if preparing
* normal variables (not GET,POST, or REQUEST)
*
* @param string $s
* @param bool $istrim whether to use trim() on the data.
* @return string
*/
function formDataCore($s, $isTrim = false)
{
//trim if selected
if ($isTrim) {
$s = trim($s);
}
//add escapes for safe database insertion
$s = add_escape_custom($s);
return $s;
}