Es ginge mit auto_prepend_file und auto_append_file (siehe hier:
PHP: Description of core php.ini directives - Manual), Output-Buffer und Post-Processing der Ausgaben.
- Entsprechende PHP-Config-Werte setzen (zum Beispiel per .htaccess, falls es die Umgebung zulässt).
.htaccess
Code:
php_value auto_append_file "auto_append.php"
php_value auto_prepend_file "auto_prepend.php"
- Zu Beginn jedes Scripts den Output-Buffer anschmeißen und eventuell verschlüsselte GET-Werte wieder entschlüsseln.
auto_prepend.php
PHP:
<?php
require_once './GetObfuscator.php';
if (isset($_GET['data']) && count($_GET) === 1) {
$obfuscator = new GetObfuscator();
$obfuscator->deobfuscate($_GET['data']);
}
ob_start();
- Zum Ende jedes Scripts die generierte Ausgabe parsen und jedes href-Attribut eines a-Elements, das nicht mit http oder ftp beginnt (also ein interner Link ist), verschlüsseln.
auto_append.php
PHP:
<?php
require_once './GetObfuscator.php';
$content = ob_get_clean();
$doc = new DOMDocument();
$doc->loadHTML($content);
$xpath = new DOMXPath($doc);
$obfuscator = new GetObfuscator();
foreach ($xpath->query('//a[@href]') as $element) {
/* @var $element DOMElement */
$href = $element->getAttribute('href');
if (0 === preg_match('/^http|ftp/', $href)) {
$obfuscatedHref = $obfuscator->obfuscate($href);
$element->setAttribute('href', $obfuscatedHref);
}
}
echo $doc->saveHTML();
- Zuletzt die Logik hinter der Geschichte.
GetObfuscator.php
PHP:
<?php
/**
* @author Marc Ermshaus <http://www.ermshaus.org/>
* @license GPL <http://www.gnu.org/licenses/gpl.html>
*/
class GetObfuscator
{
const SALT = 'This is a salt value. Change this to something else.';
public function obfuscate($href)
{
$queryPart = parse_url($href, PHP_URL_QUERY);
$encodedQueryPart = '';
$saltLength = strlen(self::SALT);
$len = strlen($queryPart);
for ($i = 0; $i < $len; $i++) {
$encodedQueryPart .= chr(ord(substr($queryPart, $i, 1))
^ ord(substr(self::SALT, $i % $saltLength, 1)));
}
$encodedQueryPart = base64_encode($encodedQueryPart);
$newHref = parse_url($href, PHP_URL_PATH) . '?data=' . $encodedQueryPart;
if (parse_url($href, PHP_URL_FRAGMENT) !== null) {
$newHref .= '#' . parse_url($href, PHP_URL_FRAGMENT);
}
return $newHref;
}
public function deobfuscate($data)
{
unset($_GET['data']);
$data = base64_decode($data);
$len = strlen($data);
$deobfuscated = '';
$saltLength = strlen(self::SALT);
for ($i = 0; $i < $len; $i++) {
$deobfuscated .= chr(ord(substr($data, $i, 1))
^ ord(substr(self::SALT, $i % $saltLength, 1)));
}
$queryParts = explode('&', $deobfuscated);
foreach ($queryParts as $part) {
list($key, $value) = explode('=', $part);
$_GET[$key] = $value;
}
}
}
Das habe ich gerade zusammengeschrieben. Es wäre gelogen, zu behaupten, dass der Code robust oder erprobt ist.
- Eine Beispielseite.
index.php
PHP:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Test</title>
</head>
<body>
<?php
if (count($_GET) > 0) {
echo '<p>Content of $_GET</p>';
echo '<pre>';
var_dump($_GET);
echo '</pre>';
}
?>
<p>Some links</p>
<ul>
<li><a href="./?x=abc&y=def&z=ghi#test">test with fragment identifier</a></li>
<li><a href="./?x=abc&y=def&z=ghi">test</a></li>
<li><a href="./?foo=bar&hello=world&id=12">test</a></li>
<li><a href="http://example.org/">don't touch this</a></li>
</ul>
</body>
</html>
Hieraus wird automatisch:
HTML:
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>Test</title></head><body>
<p>Some links</p>
<ul><li><a href="./?data=LFUIEUNPCh0FRRVHFklHHgg=#test">test with fragment identifier</a></li>
<li><a href="./?data=LFUIEUNPCh0FRRVHFklHHgg=">test</a></li>
<li><a href="./?data=MgcGTkIIAQYJRR8NA0lXGRMAEUNHRH5ZUw==">test</a></li>
<li><a href="http://example.org/">don't touch this</a></li>
</ul></body></html>
Bei Klick auf ersten Link:
Code:
Content of $_GET
array
'x' => string 'abc' (length=3)
'y' => string 'def' (length=3)
'z' => string 'ghi' (length=3)
Eventuell musst du für die Angabe der *_append- und *_prepend-Dateien absolute Pfade nutzen, falls die Geschichte auch in Unterverzeichnissen funktionieren soll. Das habe ich nicht getestet.
Noch kurz zum Algorithmus: Der Query-Teil des URL (alles zwischen „?“ und eventuell „#“) wird hergenommen und zeichenweise mit dem angegebenen SALT-Wert ver-xor-t. Das Ergebnis davon wird dann base64-kodiert. Bei der Entschlüsselung wird die Operation rückgängig gemacht.
Das ist auf gar keinen Fall sicher. Wenn es für Besucher an irgendeiner Stelle die Möglichkeit gibt, selbst einen Link zu erstellen, der auf diese Weise bearbeitet wird (also die Eingabe zur Ausgabe zu kennen), kann dir ein halbwegs fähiger Mensch relativ sicher den SALT-Wert knacken. Ich sag's nur. ;)
* * *
Und sonderlich effizient ist es natürlich nicht, alles zu buffern und dann durch einen XML-Parser zu schicken.
Es ist in dem Zusammenhang sowieso zu empfehlen, alle internen Links durch eine Funktion generieren zu lassen. Siehe etwa die url-Funktion hier:
-
http://www.html.de/wiki/MVC#Ausgabe_an_den_Browser
In dieser Funktion könntest du dann auch das Verschlüsseln vornehmen.