Better signature validation
rodzic
864315d5f7
commit
3045406f76
107
index.php
107
index.php
|
@ -1485,13 +1485,46 @@ HTML;
|
||||||
$headers = array_change_key_case( $headers, CASE_LOWER );
|
$headers = array_change_key_case( $headers, CASE_LOWER );
|
||||||
|
|
||||||
// Validate the timestamp is within ±30 seconds
|
// Validate the timestamp is within ±30 seconds
|
||||||
if ( !isset( $headers["date"] ) ) { return null; } // No date set
|
// 7.2.4 of https://datatracker.ietf.org/doc/rfc9421/ says compare the Date header and the published date of the message
|
||||||
|
if ( !isset( $headers["date"] ) ) {
|
||||||
|
// No date set
|
||||||
|
// Filename for the log
|
||||||
|
$filename = "{$timestamp}.{$type}.Signature.Date_Failure.txt";
|
||||||
|
|
||||||
|
// Save headers and request data to the timestamped file in the logs directory
|
||||||
|
file_put_contents( $directories["logs"] . "/{$filename}",
|
||||||
|
"Original Body:\n" . print_r( $body, true ) . "\n\n" .
|
||||||
|
"Original Headers:\n" . print_r( $headers, true ) . "\n\n"
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
$dateHeader = $headers["date"];
|
$dateHeader = $headers["date"];
|
||||||
$headerDatetime = DateTime::createFromFormat('D, d M Y H:i:s T', $dateHeader);
|
$headerDatetime = DateTime::createFromFormat('D, d M Y H:i:s T', $dateHeader);
|
||||||
$currentDatetime = new DateTime();
|
$currentDatetime = new DateTime();
|
||||||
|
|
||||||
|
// First, check if the message was sent more than 30 seconds ago
|
||||||
// Calculate the time difference in seconds
|
// Calculate the time difference in seconds
|
||||||
$timeDifference = abs( $currentDatetime->getTimestamp() - $headerDatetime->getTimestamp() );
|
$timeDifference = abs( $currentDatetime->getTimestamp() - $headerDatetime->getTimestamp() );
|
||||||
|
if ( $timeDifference > 30 ) {
|
||||||
|
// Write a log detailing the error
|
||||||
|
// Filename for the log
|
||||||
|
$filename = "{$timestamp}.{$type}.Signature.Delay_Failure.txt";
|
||||||
|
|
||||||
|
// Save headers and request data to the timestamped file in the logs directory
|
||||||
|
file_put_contents( $directories["logs"] . "/{$filename}",
|
||||||
|
"Header Date:\n" . print_r( $dateHeader, true ) . "\n" .
|
||||||
|
"Server Date:\n" . print_r( $currentDatetime->format('D, d M Y H:i:s T'), true ) ."\n" .
|
||||||
|
"Original Body:\n" . print_r( $body, true ) . "\n\n" .
|
||||||
|
"Original Headers:\n" . print_r( $headers, true ) . "\n\n"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is there a significant difference between the Date header and the published timestamp?
|
||||||
|
$published = $body["published"];
|
||||||
|
$publishedDatetime = new DateTime($published);
|
||||||
|
// Calculate the time difference in seconds
|
||||||
|
$timeDifference = abs( $publishedDatetime->getTimestamp() - $headerDatetime->getTimestamp() );
|
||||||
if ( $timeDifference > 30 ) {
|
if ( $timeDifference > 30 ) {
|
||||||
// Write a log detailing the error
|
// Write a log detailing the error
|
||||||
// Filename for the log
|
// Filename for the log
|
||||||
|
@ -1499,8 +1532,10 @@ HTML;
|
||||||
|
|
||||||
// Save headers and request data to the timestamped file in the logs directory
|
// Save headers and request data to the timestamped file in the logs directory
|
||||||
file_put_contents( $directories["logs"] . "/{$filename}",
|
file_put_contents( $directories["logs"] . "/{$filename}",
|
||||||
"Original Date:\n" . print_r( $dateHeader, true ) . "\n" .
|
"Header Date:\n" . print_r( $dateHeader, true ) . "\n" .
|
||||||
"Local Date:\n" . print_r( $currentDatetime->format('D, d M Y H:i:s T'), true ) . "\n"
|
"Published Date:\n" . print_r( $publishedDatetime->format('D, d M Y H:i:s T'), true ) ."\n" .
|
||||||
|
"Original Body:\n" . print_r( $body, true ) . "\n\n" .
|
||||||
|
"Original Headers:\n" . print_r( $headers, true ) . "\n\n"
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1588,38 +1623,50 @@ HTML;
|
||||||
$actorPublicKey = $actorData["publicKey"]["publicKeyPem"];
|
$actorPublicKey = $actorData["publicKey"]["publicKeyPem"];
|
||||||
|
|
||||||
if ( $publicKey != $actorPublicKey ) {
|
if ( $publicKey != $actorPublicKey ) {
|
||||||
$verified = false;
|
// Filename for the log
|
||||||
} else {
|
$filename = "{$timestamp}.{$type}.Signature.Mismatch_Failure.txt";
|
||||||
// Get the remaining parts
|
|
||||||
$signature = base64_decode( $signatureParts["signature"] );
|
|
||||||
$algorithm = $signatureParts["algorithm"];
|
|
||||||
|
|
||||||
// There might be many different signing algorithms
|
// Save headers and request data to the timestamped file in the logs directory
|
||||||
// TODO: Find a way to transform these automatically
|
file_put_contents( $directories["logs"] . "/{$filename}",
|
||||||
// See https://github.com/superseriousbusiness/gotosocial/issues/1186#issuecomment-1976166659 and https://github.com/snarfed/bridgy-fed/issues/430 for hs2019
|
"Original Body:\n" . print_r( $body, true ) . "\n\n" .
|
||||||
if ( "hs2019" == $algorithm ) {
|
"Original Headers:\n" . print_r( $headers, true ) . "\n\n" .
|
||||||
$algorithm = "sha256";
|
"Signature Headers:\n" . print_r( $signatureHeaders, true ) . "\n\n" .
|
||||||
}
|
"publicKeyURL:\n" . print_r( $publicKeyURL, true ) . "\n\n" .
|
||||||
|
"publicKey:\n" . print_r( $publicKey, true ) . "\n\n" .
|
||||||
// Finally! Calculate whether the signature is valid
|
"actorPublicKey:\n" . print_r( $actorPublicKey, true ) . "\n"
|
||||||
// Returns 1 if verified, 0 if not, false or -1 if an error occurred
|
|
||||||
$verified = openssl_verify(
|
|
||||||
$signatureString,
|
|
||||||
$signature,
|
|
||||||
$publicKey,
|
|
||||||
$algorithm
|
|
||||||
);
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the remaining parts
|
||||||
|
$signature = base64_decode( $signatureParts["signature"] );
|
||||||
|
$algorithm = $signatureParts["algorithm"];
|
||||||
|
|
||||||
// Convert to boolean
|
// There might be many different signing algorithms
|
||||||
if ( $verified === 1 ) {
|
// TODO: Find a way to transform these automatically
|
||||||
$verified = true;
|
// See https://github.com/superseriousbusiness/gotosocial/issues/1186#issuecomment-1976166659 and https://github.com/snarfed/bridgy-fed/issues/430 for hs2019
|
||||||
} elseif ( $verified === 0 ) {
|
if ( "hs2019" == $algorithm ) {
|
||||||
$verified = false;
|
$algorithm = "sha256";
|
||||||
} else {
|
|
||||||
$verified = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finally! Calculate whether the signature is valid
|
||||||
|
// Returns 1 if verified, 0 if not, false or -1 if an error occurred
|
||||||
|
$verified = openssl_verify(
|
||||||
|
$signatureString,
|
||||||
|
$signature,
|
||||||
|
$publicKey,
|
||||||
|
$algorithm
|
||||||
|
);
|
||||||
|
|
||||||
|
// Convert to boolean
|
||||||
|
if ( $verified === 1 ) {
|
||||||
|
$verified = true;
|
||||||
|
} elseif ( $verified === 0 ) {
|
||||||
|
$verified = false;
|
||||||
|
} else {
|
||||||
|
$verified = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Filename for the log
|
// Filename for the log
|
||||||
$filename = "{$timestamp}.{$type}.Signature.". json_encode( $verified ) . ".txt";
|
$filename = "{$timestamp}.{$type}.Signature.". json_encode( $verified ) . ".txt";
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue