You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

427 lines
18 KiB

<?php
/**
* Audit Log Tamper Report.
*
* @package OpenEMR
* @link http://www.open-emr.org
* @author Anil N <aniln@ensoftek.com>
* @author Brady Miller <brady.g.miller@gmail.com>
* @copyright Copyright (c) 2014 Ensoftek
* @copyright Copyright (c) 2017-2018 Brady Miller <brady.g.miller@gmail.com>
* @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
*/
require_once("../globals.php");
use OpenEMR\Common\Acl\AclMain;
use OpenEMR\Common\Crypto\CryptoGen;
use OpenEMR\Common\Csrf\CsrfUtils;
use OpenEMR\Common\Logging\EventAuditLogger;
use OpenEMR\Common\Twig\TwigContainer;
use OpenEMR\Core\Header;
// Control access
if (!AclMain::aclCheckCore('admin', 'super')) {
echo (new TwigContainer(null, $GLOBALS['kernel']))->getTwig()->render('core/unauthorized.html.twig', ['pageTitle' => xl("Audit Log Tamper Report")]);
exit;
}
if (!empty($_GET)) {
if (!CsrfUtils::verifyCsrfToken($_GET["csrf_token_form"])) {
CsrfUtils::csrfNotVerified();
}
}
?>
<html>
<head>
<title><?php echo xlt("Audit Log Tamper Report"); ?></title>
<?php Header::setupHeader('datetime-picker'); ?>
<style>
#logview {
width: 100%;
}
#logview table {
width:100%;
border-collapse: collapse;
}
#logview th {
background-color: #cccccc;
cursor: pointer; cursor: hand;
padding: 5px 5px;
align: left;
text-align: left;
}
#logview td {
background-color: var(--white);
border-bottom: 1px solid #808080;
cursor: default;
padding: 5px 5px;
vertical-align: top;
}
.highlight {
background-color: #336699;
color: #336699;
}
.tamperColor{
color:red;
}
</style>
<script>
//function to disable the event type field if the event name is disclosure
function eventTypeChange(eventname)
{
if (eventname == "disclosure") {
document.theform.type_event.disabled = true;
}
else {
document.theform.type_event.disabled = false;
}
}
// VicarePlus :: This invokes the find-patient popup.
function sel_patient() {
dlgopen('../main/calendar/find_patient_popup.php?pflag=0', '_blank', 500, 400);
}
// VicarePlus :: This is for callback by the find-patient popup.
function setpatient(pid, lname, fname, dob) {
var f = document.theform;
f.form_patient.value = lname + ', ' + fname;
f.form_pid.value = pid;
}
</script>
</head>
<body class="body_top">
<font class="title"><?php echo xlt('Audit Log Tamper Report'); ?></font>
<br />
<?php
$err_message = 0;
$start_date = (!empty($_GET["start_date"])) ? DateTimeToYYYYMMDDHHMMSS($_GET["start_date"]) : date("Y-m-d") . " 00:00:00";
$end_date = (!empty($_GET["end_date"])) ? DateTimeToYYYYMMDDHHMMSS($_GET["end_date"]) : date("Y-m-d") . " 23:59:59";
/*
* Start date should not be greater than end date - Date Validation
*/
if ($start_date > $end_date) {
echo "<table><tr class='alert'><td colspan=7>";
echo xlt('Start Date should not be greater than End Date');
echo "</td></tr></table>";
$err_message = 1;
}
if (!empty($_GET["form_patient"])) {
$form_patient = $_GET['form_patient'];
}
?>
<?php
$form_user = $_GET['form_user'] ?? null;
$form_pid = $_GET['form_pid'] ?? null;
if (empty($form_patient)) {
$form_pid = null;
}
?>
<br />
<FORM METHOD="GET" name="theform" id="theform" onSubmit='top.restoreSession()'>
<input type="hidden" name="csrf_token_form" value="<?php echo attr(CsrfUtils::collectCsrfToken()); ?>" />
<?php
$sortby = $_GET['sortby'] ?? null;
?>
<input type="hidden" name="sortby" id="sortby" value="<?php echo attr($sortby); ?>">
<input type=hidden name=csum value="">
<table>
<tr><td>
<span class="text"><?php echo xlt('Start Date'); ?>: </span>
</td><td>
<input type="text" size="18" class="datetimepicker" name="start_date" id="start_date" value="<?php echo attr(oeFormatDateTime($start_date, 0, true)); ?>" title="<?php echo xla('Start date'); ?>" />
</td>
<td>
<span class="text"><?php echo xlt('End Date'); ?>: </span>
</td><td>
<input type="text" size="18" class="datetimepicker" name="end_date" id="end_date" value="<?php echo attr(oeFormatDateTime($end_date, 0, true)); ?>" title="<?php echo xla('End date'); ?>" />
</td>
<td>
&nbsp;&nbsp;<span class='text'><?php echo xlt('Patient'); ?>: </span>
</td>
<td>
<input type='text' size='20' name='form_patient' style='width:100%;cursor:pointer;cursor:hand' value='<?php echo (!empty($form_patient)) ? attr($form_patient) : '' ?>' placeholder= '<?php echo xla('Click To Select'); ?>' onclick='sel_patient()' title='<?php echo xla('Click to select patient'); ?>' />
<input type='hidden' name='form_pid' value='<?php echo attr($form_pid); ?>' />
</td>
</tr>
<tr><td>
<span class='text'><?php echo xlt('Include Checksum'); ?>: </span>
</td><td>
<?php
$check_sum = isset($_GET['check_sum']);
?>
<input type="checkbox" name="check_sum" <?php echo ($check_sum) ? "checked" : ""; ?>>
</td>
<td>
<input type=hidden name="event" value="<?php echo attr($event ?? '') ; ?>">
<a href="javascript:document.theform.submit();" class='link_submit'>[<?php echo xlt('Refresh'); ?>]</a>
</td>
</tr>
</table>
</FORM>
<?php if ($start_date && $end_date && $err_message != 1) { ?>
<div id="logview">
<span class="text" id="display_tamper" style="display:none;"><?php echo xlt('Following rows in the audit log have been tampered'); ?></span>
<table>
<tr>
<th id="sortby_date" class="text" title="<?php echo xla('Sort by date/time'); ?>"><?php echo xlt('Log Types'); ?></th>
<th id="sortby_date" class="text" title="<?php echo xla('Sort by date/time'); ?>"><?php echo xlt('ID'); ?></th>
<th id="sortby_date" class="text" title="<?php echo xla('Sort by date/time'); ?>"><?php echo xlt('Date'); ?></th>
<th id="sortby_user" class="text" title="<?php echo xla('Sort by User'); ?>"><?php echo xlt('User'); ?></th>
<th id="sortby_pid" class="text" title="<?php echo xla('Sort by PatientID'); ?>"><?php echo xlt('PatientID'); ?></th>
<th id="sortby_comments" class="text" title="<?php echo xla('Sort by Comments'); ?>"><?php echo xlt('Comments'); ?></th>
<?php if ($check_sum) {?>
<th id="sortby_newchecksum" class="text" title="<?php xla('Sort by New Checksum'); ?>"><?php echo xlt('Tampered Checksum'); ?></th>
<th id="sortby_oldchecksum" class="text" title="<?php xla('Sort by Old Checksum'); ?>"><?php echo xlt('Original Checksum'); ?></th>
<?php } ?>
</tr>
<?php
$eventname = $_GET['eventname'] ?? null;
$type_event = $_GET['type_event'] ?? null;
?>
<input type="hidden" name="event" value="<?php echo attr($eventname) . "-" . attr($type_event) ?>">
<?php
$type_event = "";
$tevent = "";
$gev = "";
if ($eventname != "" && $type_event != "") {
$getevent = $eventname . "-" . $type_event;
}
if (($eventname == "") && ($type_event != "")) {
$tevent = $type_event;
} elseif ($type_event == "" && $eventname != "") {
$gev = $eventname;
} elseif ($eventname == "") {
$gev = "";
} else {
$gev = $getevent;
}
$dispArr = array();
$icnt = 1;
if ($ret = EventAuditLogger::instance()->getEvents(array('sdate' => $start_date,'edate' => $end_date, 'user' => $form_user, 'patient' => $form_pid, 'sortby' => ($_GET['sortby'] ?? null), 'levent' => $gev, 'tevent' => $tevent))) {
// Set up crypto object (object will increase performance since caches used keys)
$cryptoGen = new CryptoGen();
while ($iter = sqlFetchArray($ret)) {
if (empty($iter["id"])) {
// Log item is missing; it has been deleted.
echo "<tr><td colspan='6' class='text tamperColor''>" . xlt("The log entry with following id has been deleted") . ": " . $iter['log_id_hash'] . "</td></tr>";
continue;
}
//translate comments
$patterns = array ('/^success/','/^failure/','/ encounter/');
$replace = array ( xl('success'), xl('failure'), xl('encounter', '', ' '));
$checkSumOld = $iter['checksum'];
if (empty($checkSumOld)) {
// no checksum, so skip
continue;
} elseif (strlen($checkSumOld) < 50) {
// for backward compatibility (for log checksums created in the sha1 days)
$checkSumNew = sha1($iter['date'] . $iter['event'] . $iter['user'] . $iter['groupname'] . $iter['comments'] . $iter['patient_id'] . $iter['success'] . $iter['checksum'] . $iter['crt_user']);
} else {
$checkSumNew = hash('sha3-512', $iter['date'] . $iter['event'] . $iter['category'] . $iter['user'] . $iter['groupname'] . $iter['comments'] . $iter['user_notes'] . $iter['patient_id'] . $iter['success'] . $iter['crt_user'] . $iter['log_from'] . $iter['menu_item_id'] . $iter['ccda_doc_id']);
}
$checkSumOldApi = $iter['checksum_api'];
if (!empty($checkSumOldApi)) {
$checkSumNewApi = hash('sha3-512', $iter['log_id_api'] . $iter['user_id'] . $iter['patient_id_api'] . $iter['ip_address'] . $iter['method'] . $iter['request'] . $iter['request_url'] . $iter['request_body'] . $iter['response'] . $iter['created_time']);
}
$dispCheck = false;
$mainFail = false;
$apiFail = false;
if ($checkSumOld != $checkSumNew) {
$dispCheck = true;
$mainFail = true;
}
if (!empty($checkSumOldApi) && ($checkSumOldApi != $checkSumNewApi)) {
$dispCheck = true;
$apiFail = true;
}
if (!$dispCheck) {
continue;
}
$logType = '';
if (!empty($mainFail) && !empty($apiFail)) {
$logType = xl('Main and API');
} elseif (!empty($mainFail)) {
$logType = xl('Main');
} else { // !empty($apiFail)
$logType = xl('API');
}
if (!empty($iter['encrypt'])) {
$commentEncrStatus = $iter['encrypt'];
} else {
$commentEncrStatus = "No";
}
if (!empty($iter['version'])) {
$encryptVersion = $iter['version'];
} else {
$encryptVersion = 0;
}
if ($commentEncrStatus == "Yes") {
if ($encryptVersion >= 3) {
// Use new openssl method
if (extension_loaded('openssl')) {
$trans_comments = $cryptoGen->decryptStandard($iter["comments"]);
if ($trans_comments !== false) {
$trans_comments = preg_replace($patterns, $replace, trim($trans_comments));
} else {
$trans_comments = xl("Unable to decrypt these comments since decryption failed.");
}
} else {
$trans_comments = xl("Unable to decrypt these comments since the PHP openssl module is not installed.");
}
} elseif ($encryptVersion == 2) {
// Use new openssl method
if (extension_loaded('openssl')) {
$trans_comments = $cryptoGen->aes256DecryptTwo($iter["comments"]);
if ($trans_comments !== false) {
$trans_comments = preg_replace($patterns, $replace, trim($trans_comments));
} else {
$trans_comments = xl("Unable to decrypt these comments since decryption failed.");
}
} else {
$trans_comments = xl("Unable to decrypt these comments since the PHP openssl module is not installed.");
}
} elseif ($encryptVersion == 1) {
// Use new openssl method
if (extension_loaded('openssl')) {
$trans_comments = preg_replace($patterns, $replace, trim($cryptoGen->aes256DecryptOne($iter["comments"])));
} else {
$trans_comments = xl("Unable to decrypt these comments since the PHP openssl module is not installed.");
}
} else { //$encryptVersion == 0
// Use old mcrypt method
if (extension_loaded('mcrypt')) {
$trans_comments = preg_replace($patterns, $replace, trim($cryptoGen->aes256Decrypt_mycrypt($iter["comments"])));
} else {
$trans_comments = xl("Unable to decrypt these comments since the PHP mycrypt module is not installed.");
}
}
} else {
// base64 decode if applicable (note the $encryptVersion is a misnomer here, we have added in base64 encoding
// of comments in OpenEMR 6.0.0 and greater when the comments are not encrypted since they hold binary (uuid) elements)
if ($encryptVersion >= 4) {
$iter["comments"] = base64_decode($iter["comments"]);
}
$trans_comments = preg_replace($patterns, $replace, trim($iter["comments"]));
}
//Alter Checksum value records only display here
if ($dispCheck) {
$dispArr[] = $icnt++;
?>
<TR class="oneresult">
<TD class="text tamperColor"><?php echo text($logType); ?></TD>
<TD class="text tamperColor"><?php echo text($iter["id"]); ?></TD>
<TD class="text tamperColor"><?php echo text(oeFormatDateTime($iter["date"], "global", true)); ?></TD>
<TD class="text tamperColor"><?php echo text($iter["user"]); ?></TD>
<TD class="text tamperColor"><?php echo text($iter["patient_id"]);?></TD>
<?php // Using mb_convert_encoding to change binary stuff (uuid) to just be '?' characters ?>
<TD class="text tamperColor"><?php echo text(mb_convert_encoding($trans_comments, 'UTF-8', 'UTF-8'));?></TD>
<?php
if ($check_sum) {
if (!empty($mainFail) && !empty($apiFail)) {
echo '<TD class="text tamperColor">' . text($checkSumNew) . '<br>' . text($checkSumNewApi) . '</TD>';
echo '<TD class="text tamperColor">' . text($checkSumOld) . '<br>' . text($checkSumOldApi) . '</TD>';
} elseif (!empty($mainFail)) {
echo '<TD class="text tamperColor">' . text($checkSumNew) . '</TD>';
echo '<TD class="text tamperColor">' . text($checkSumOld) . '</TD>';
} else { // !empty($apiFail)
echo '<TD class="text tamperColor">' . text($checkSumNewApi) . '</TD>';
echo '<TD class="text tamperColor">' . text($checkSumOldApi) . '</TD>';
}
}
?>
</TR>
<?php
}
}
}
if (count($dispArr) == 0) {?>
<TR class="oneresult">
<?php
$colspan = 4;
if ($check_sum) {
$colspan = 6;
}
?>
<TD class="text" colspan="<?php echo attr($colspan);?>" align="center"><?php echo xlt('No audit log tampering detected in the selected date range.'); ?></TD>
</TR>
<?php
} else {?>
<script>$('#display_tamper').css('display', 'block');</script>
<?php
}
?>
</table>
</div>
<?php } ?>
</body>
<script>
// jQuery stuff to make the page a little easier to use
$(function () {
// funny thing here... good learning experience
// the TR has TD children which have their own background and text color
// toggling the TR color doesn't change the TD color
// so we need to change all the TR's children (the TD's) just as we did the TR
// thus we have two calls to toggleClass:
// 1 - for the parent (the TR)
// 2 - for each of the children (the TDs)
$(".oneresult").mouseover(function() { $(this).toggleClass("highlight"); $(this).children().toggleClass("highlight"); });
$(".oneresult").mouseout(function() { $(this).toggleClass("highlight"); $(this).children().toggleClass("highlight"); });
// click-able column headers to sort the list
$("#sortby_date").click(function() { $("#sortby").val("date"); $("#theform").submit(); });
$("#sortby_event").click(function() { $("#sortby").val("event"); $("#theform").submit(); });
$("#sortby_user").click(function() { $("#sortby").val("user"); $("#theform").submit(); });
$("#sortby_cuser").click(function() { $("#sortby").val("user"); $("#theform").submit(); });
$("#sortby_group").click(function() { $("#sortby").val("groupname"); $("#theform").submit(); });
$("#sortby_pid").click(function() { $("#sortby").val("patient_id"); $("#theform").submit(); });
$("#sortby_success").click(function() { $("#sortby").val("success"); $("#theform").submit(); });
$("#sortby_comments").click(function() { $("#sortby").val("comments"); $("#theform").submit(); });
$("#sortby_oldchecksum").click(function() { $("#sortby").val("checksum"); $("#theform").submit(); });
$("#sortby_newchecksum").click(function() { $("#sortby").val("checksum"); $("#theform").submit(); });
$('.datetimepicker').datetimepicker({
<?php $datetimepicker_timepicker = true; ?>
<?php $datetimepicker_showseconds = true; ?>
<?php $datetimepicker_formatInput = true; ?>
<?php require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?>
<?php // can add any additional javascript settings to datetimepicker here; need to prepend first setting with a comma ?>
});
});
</script>
</html>