Spouštění externích souborů

Školení, která pořádám

Při práci na Admineru jsem přemýšlel, jak by se ještě dala zmenšit velikost stahovaného souboru, a pohrával jsem si s myšlenkou, že by se některé části kódu mohly stahovat z Internetu, pokud by si to uživatel přál. Používá se to už u zvýrazňování syntaxe SQL příkazů, chystám to u alternativních stylů. Přemýšlel jsem i o JavaScriptovém frameworku, ale kompletní nefunkčnost JavaScriptu při nedostupném Internetovém připojení by byla příliš omezující.

Dal by se stahovat i PHP kód? Hned několika způsoby:

include "http://";
Tento způsob je ve výchozím nastavení zakázán direktivou allow_url_include a není příliš rozumné po uživatelích chtít, aby si ho povolili.
eval(file_get_contents("http://"))
Tento způsob bývá obvykle možný, ale dá se zakázat direktivou allow_url_fopen.
Knihovna cURL
Někde se používá pro stahování externích souborů tato knihovna, protože má bohaté možnosti konfigurace. Já pro konfiguraci raději používám kontexty.
fsockopen
Pro stažení externího souboru se dá použít i nízkoúrovňová funkce fsockopen, někdy bývá ale také zakázaná.
<?php
$url = parse_url("http://");
$fp = fsockopen($url["host"], ($url["port"] ? $url["port"] : 80));
fwrite($fp, "GET " . (isset($url["path"]) ? $url["path"] : "/") . (isset($url["query"]) ? "?$url[query]" : "") . " HTTP/1.1\r\n");
fwrite($fp, "Host: $url[host]\r\n");
fwrite($fp, "Connection: close\r\n");
fwrite($fp, "\r\n");
fpassthru($fp);
fclose($fp);
?>
Při použití protokolu HTTP/1.1 je potřeba dát pozor na kódování chunked, s verzí 1.0 zase teoreticky nejde poslat hlavička Host.

Získání dat metodou POST

Všechny způsoby jdou zakázat. Co zakázat prakticky nejde, je získání dat metodou POST.

<?php
$source = str_replace("\r", "", $_POST["source"]);
if (!$source) {
    echo '<iframe src="http://adminer.sf.net/source.php?action=' . urlencode("http://$_SERVER[SERVER_NAME]$_SERVER[REQUEST_URI]") . '"></iframe>';
} elseif (md5($source) == "84ab81e6605ca8141bb331c550093072") {
    eval("?>$source");
} else {
    echo "Invalid source.\n";
}
?>

Z externí adresy se načte zdrojový kód, který se spustí v případě, že sedí jeho otisk.

Skript source.php načte do formuláře zdrojový kód a pokud je zapnutý JavaScript, tak ho automaticky odešle.

<form action="<?php echo (preg_match('~^https?://~', $_GET["action"]) ? htmlspecialchars($_GET["action"]) : "/"); ?>" method="post" target="_parent">
<p><input type="hidden" name="source" value="<?php echo strtr(htmlspecialchars(file_get_contents("adminer.php")), array("\t" => "&#9;", "\n" => "&#10;")); ?>" /></p>
<p><input type="submit" value="Run" /></p>
</form>
<script type="text/javascript">document.getElementsByTagName('form')[0].submit();</script>

Po prvotním stažení kódu by ho bylo možné uložit do session proměnné a spouštět nadále z ní.

Přestože by bylo lákavé získat celý Adminer ve 200 bajtech, tak jde jen o Proof of Concept, který v praxi realizovat nebudu.

Jakub Vrána, Řešení problému, 24.6.2009, diskuse: 12 (nové: 0)

Diskuse

Dundee:

Ta poslední metoda by mě tedy opravdu nenapadla. Velmi zajímavé řešení.

ikona Martin Malý:

Přemýšlels nad tím, jak vyřešit předání nového otisku v případě aktualizace souboru? Aktualizace jedné stahované knihovny by znamenala nutnost aktualizací u všech klientů (minimálně "seznamu hashů") - to by sice v případě "ultramalého" klienta až tak nevadilo, ale stejně...
Připadá mi to podobné jako různé "lite instalátory", kdy si stáhneš pár set kilo programu, vybereš si co chceš nainstalovat, a on si stáhne a nainstaluje potřebné soubory. Problematika online distribuce SW (nejen aktualizací) u desktopových aplikací už tedy nějak řešena je, u webových jsem to zatím moc neviděl. Máš tušení, proč to tak je?

ikona Jakub Vrána OpenID:

Když jsem to psal, tak jsem si říkal, že by loader předával informaci o verzi, kterou chce stáhnout.

Jak to udělat pro nejnovější verzi, mě zatím nenapadlo.

U webových aplikací se lite instalace neřeší obvykle proto, že je nepotřebuješ instalovat :-). A když už potřebuješ (všelijaké ty WordPressy a MediaWiki), tak by musel mít loader právo zápisu zdrojových souborů (protože kód nestačí držet v paměti, jak by si to mohl dovolit Adminer), což bývá u webových aplikací problém. Takže je jednodušší si to nakopírovat ručně.

Peter:

Nové verzie sa čiastočne dajú ošetriť tak, že spolu s dotazom na stiahnutie sa odošle informácia o verzii, ktorá má byť stiahnutá. Takže vždy najnovšiu verziu by to síce nestiahlo, ale aspoň by fungovali tie staršie. No a potom stačí presvedčiť klientov, aby vždy aktualizovali.

ikona Jakub Vrána OpenID:

Ano, přesně to samé jsem psal…

Aktuální verze by se dala vyřešit asymetrickou kryptografií. Kód ze serveru by byl podepsaný soukromým klíčem a loader by obsahoval veřejný klíč, který by podpis ověřil. To už by ale potřebovalo přítomnost knihovny OpenSSL, samotné hešování by na to nestačilo.

Martin Straka:

Rozhodne zajimave.

A jako bonus to vytvori XSS zranitelnost na serveru, kde je source.php:)

.../source.php?action=javascript:alert(123)

ikona Jakub Vrána OpenID:

Díky za upozornění, opravil jsem to.

phx:

Vzhledem k tomu, ze na serveru je vetsinou rychle pripojeni slo by udelat nejaky downloader, ktery by pri prvnim spusteni stahnul zdrojaky do temp souboru an disk. Takze by uzivatel musel na server nahrat jen par bajtu.

ikona david@grudl.com:

Obsese velikostí není u mužů neobvyklá, ale cílem bývá zvětšování, ty holt musíš mít zase něco extra :-))

Zkoušel jsi něco jako eval(gzuncompress(file_get_contents(__FILE__, NULL, NULL, __COMPILER_HALT_OFFSET__))) ?

ikona Jakub Vrána OpenID:

Ano, o tomhle jsem uvažoval dlouho. Problém je, že v PHP není žádná vestavěná dekompresní funkce. Uvažoval jsem dokonce, že bych si napsal vlastní, ale tímto směrem jsem se nevydal. Když budu citovat svůj vlastní článek pro php|arch (dosud nevydaný):

Everybody knows that eval is evil (one reason is that it is not compatible with PHP accelerators). Therefore, the code is not compressed but only minified.

Problem:

Ahoj, narazil jsem na problém, že mi nejde obejít přechod mezi HTTP a HTTPS. Potřebuji zavolat ze své stránky (HTTP) script na serveru (HTTPS) ale bez zásahu uživatele. Zkoušel jsem echnout Framy zkoušel jsem FSockOpen, FOpen a nic z toho mě nepustí.

Prostě když napíšu do prohlížeče framu nebo odkazu url:

https://www.s.cz/data.php?x=10

tak to funguje ale až po osouhlašení cetrifikátu.

ale když chci aby mi to zpracoval sám script který vypočítá proměnnou x nevím jak ji předat serveru s bez toho debilniho odklikávání cetifikátu.

Potřebuju to metodou bez výstupu... tedy bez echo nejlépe FsockOpen ale jak... https nefunguje ani na portu 80 ani na portu 443. Prostě vrací false;

ikona Jakub Vrána OpenID:

Použij <?php fsockopen("ssl://$host", 443); ?>.

Vložit komentář

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2018 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.