Jak v PHP vygenerovat string
Zdravím,
mám proměnou "A" která obsahuje všechny běžné znaky jako je abeceda, čísla, @#$~^&* atd..
mám proměnou "B" která obsahuje tajné heslo
mám proměnou "C" která obsahuje počet znaků
teď otázka, jak můžu vygenerovat string (ze znaků uložených v proměnné A) dlouhý 500 znaků (C=500) když tajné heslo bude mít třeba jen 4 znaky (B=4)
Díky..
Nějak v tom nevidím, jak s tím souvisí to heslo.
Podle toho hesla se vygenerují ty znaky, tedy po zadání stejné heslo musím dostat vždy stejný výsledný řetězec.
No tak primitivní způsob je to heslo opakovat tolikrát, aby to dalo požadovanou délku. Ale to předpokládám nechceš. Takže jakým způsobem to chceš vygenerovat?
Ne, tak to nechci. Nevím, proto se ptám zde.
No dobře, ale určitě máš nějakou představu, jak by to mělo vypadat, ne? Bez toho se dost dobře poradit nedá.
Četl jsem to asi 3x a taky nepochopil...
Nejdřív píšeš, že B obsahuje tajné heslo a pak že B=4. Takže co teda?
Omlouvám se, správně to je
...když tajné heslo bude mít třeba jen 4 znaky (B="D3hž")
V tomhle ten problém není. Souvislost mezi A a C je jasná, ale jak se tím souvisí to B?
No to má být klíč pro vygenerování toho řetězce, se kterým budu dál pracovat.
"D3hž" - pokud tam máš povolenou češtinu, tak by ses měl starat i kódování a spíš volit UTF-8 než nějaké jednobytové, které bude s jinými jazyký/prostředími zdrojem nejrůznějších neustálých nekompatibilit.
Zřejmě chce šifrovat, ale prd o tom ví ...
Třeba pomocí hashů
I z jednoho písmena se dá udělat hash a pokud nebude stačit jeden hash, tak ze znaku hashe se dá udělat hash.
A pak přiřazovat ke znakům hashe znaky z toho tvého stringu. Nebo dva znaky...
Prostě si to sesmolíš jak ti to bude vyhovovat
A nechápu proč to řešíš, když php má:
https://www.php.net/manual/en/faq.passwords.php
https://www.php.net/manual/en/ref.password.php
Ještě se zeptám jinak, šlo by udělat tohle? (jen nástřel)
var A="obsahuje všechny znaky"
var B="A5@š"
var C=100
var D="fbLsDrUQk5oChBAbLuwDazstAPM+1Ouwj1ixBqmpNY2VdPLtLqMAwHythnvf/psRpyxuo+qGCen689IDHDpAFc2aqaJFvN2LEXXi"
pokud zadám jako heslo/klíč "A5@š" tak aby byl výsledek shodný s obsahem v proměnné "D"?
Prostě můžu vygenerovat řetězec nějakým kódem tak, aby vygeneroval požadovaný string ale kdyby se někdo ke kódu dostal, požadovaný řetězec to nevygeneruje?
Dyť jsem ti na to odpověděl!!!
Tak ještě jednou.
Jde to!
Ne tak docela, ten můj sgring je totiž již pevně daný, tak že hledám něco jiného než jen vygenerování náhodných znaků. Hledám jak vygenerovat přesně dané znaky.
A proč bys měl generovat nějaké znaky, když ten výsledek znáš?
A pokud jde o nalezení algoritmu, tak ten přece ti tu nikdo nenalezne podle jednoho hesla a navíc pokud to je udělané nějak normálně tak ten algoritmus nalézt nepude. A pokud by to nalézt šlo, tak ti to tu těžko někdo bude zjišťovat a ty na to přijdeš sotva, tak tvá otázka je zbytečná.
Nemohl bys prostě napsat oč ti jde?
Prvně to vypadalo, že si chceš udělat svůj algoritmus na hashe hesel.
Teď to zas vypadá, že ses dostal k nějaké databázi hashů hesel a ty je chceš prolomit
dsa [185.243.173.xxx], 04.01.2022 17:28
Co si o tom něco přečíst ???
Já bych to udělal asi tak, že bych si nechal vygenerovat několik klíčů (ideálně liché číslo větší než 1) a uložil je pro potřeby tohoto programu někam do souboru (nebo i do zdrojáku)
Pokud je B prázdné, vyfakovat uživatele ať ho zadá a skončit - nelze-li to dosadit třeba B="MeZeRa"
Při spuštění kódu bych je načetl do pole (KLICE) a někam si uložil první index v tom poli (asi I=0).
Následně bych ve smyčce opakoval takovýto postup
B = cislo_pruchodu_jako_řetězec + B // takže v prvním průchodu tvého příkladu to bude "1D3hž"
X = B zašifrované pomocí KLICE[I]; I=I+1; pokud I přeteče počet klíčů, nastaví se na začátek
Y = B zašifrované pomocí KLICE[I]; I=I+1; pokud I přeteče počet klíčů, nastaví se na začátek
vyprázdním B a dám do něj střídavě znaky z X a Y (B=""; for J < len(X) do B=B+X[J]+Y[J];) // V tuto chvíli je B minimálně 2x delší
pokud je B dost dlouhé, končím, jinak opakuju smyčku
(B je dost dlouhé, pokud je 2x delší delší než C - pro původní B jednoznakové a C=500 je to asi 10 průchodů. Smyčku v každém případě projdu aspoň 1x - takže test je až na konci)
Teď máme B dost dlouhé a chaotické, zbývá ho převést na sled povolených znaků. Ideálně tak, že ho vyjádříme jako číslo o základu (délka A) v číselné soustavě A - Dělit velká čísla jsme se učili už na základce, takže směle do toho
A bereme jako pole začínající indexem 0 o délce Ad, takže poslední znak má index Ad-1 (je to naše číselná soustava - číslu i odpovídá znak A[i])
B bereme jako číslo v šestnáctkové soustavě, takže co znak, to dvě (hexadecimální) číslice (B[0],B[1])
VYSLEDEK nechť je zápis B v soustavě o základu A a cifrách A[0] .. A[Ad-1]
VYSLEDEK=""
ZBYTEK=0
MEZIDELENEC =0
Postupně bereme půlznaky z B (střídavě horní a dolní), převádíme je na čísla (imho ASCII(B[0]) nebo tak nějak) a MEZIDELENEC vynásobíme 16 a přičteme k němu tu hodnotu půlznaku, dokud není MEZIDELENEC >= Ad
Vydělíme MEZIDELENEC hodnotou Ad, vyjde nám nějaké číslo x a zbytek y a tak na konec VYSLEDEK přidáme znak A[x] a do MEZIDELENEC dáme ten zbytek y, vynásobíme 16 a přičteme k němu další hodnotu půlznaku
Jakmile je délka VYSLEDEK rovna požadované hodnotě C, výpočet končí a máš svůj STRING.
Zbylé mezivýsledky prostě zahodíme.
Takhle je VYSLEDEK tvořen znaky z A, je založen výhradně na tajném hesle B (a konstantních klíčích a algoritmu), pokaždé pro dané B dá stejný výsledek (takže jde zkontrolovat, zda bylo zadáno správné heslo, ale nejde to heslo zpětně vypočíst) a IMHO nehrozí (díky číslům průchodu) kolize (pokud C bude dost velké - tedy větší než délka nejdelšího hesla)
Díky moc. Vypadá to zajímavě, jakou to má licenci?
LOL. Jsem teď vymyslel vlastní hlavou.
Pořebuješ nějaké knihovny pro to šifrování a něco pro generování klíčů. Co já používám je na linuxu a pod GPL / LGPL ale asi jsou i jiné, nevím co máš ty?
Takže pokud po tom toužíš, máš mé autorské svolení tento algoritmus použít pod nějakou příčtnou licencí kompatibilní s použitými knihovnami, ideálně (L)GPL, nebo CC-???? nebo jinou otevřenou. Pokud tam dáš komentář "Inspirováno Gilhadem" nebo tak nějak, tak mě to potěší :)
Nějak nechápu proč se ti líbí toto a nelíbí se ti to, co jsem ti tu napsal prvně?
Možná proto, že to je česky, má tam napsané co má dělat a trochu i proč a dá se celkem snadno představit, jak to funguje a že by se to lámalo blbě a navíc to i dává výsledek, které potřebuje (čili namapuje to do té abecedy v A, navíc dost netriviálním způsobem, takže zpětný krok by se dělal dost blbě, když stejný znak z A je použitý pokaždé pro něco zcela jiného.)
U mě mu stačí pořešit jednoduché problémy mechanického rázu (jak se udělá cyklus, jak se načte znak z pole), tys mu dal jen odkaz na funkce, co dělají něco dost jiného (vrací 60 znaků v jiné abecedě) a všechny složitosti, jak to poskládat jsi mu nechal za domácí úkol - přitom kvůli nim sem šel.
Jako jo, mě a tobě stačí říct to zadaní, manuál k PHP si už najdeme sami a vše potřebné se tam dočteme (jako syntaxi, základní i pokročilé funkce a knihovny) a nějaký algoritmus vymyslíme, ale on asi ještě takhle daleko není, a proto se ptá kudy na to.
Navíc postupné iterování přez prokládání šifer s různými klíči (a modifikací v každé iteraci - to číslo navíc tam taky udělá dost) dokud to není dost dlouhé - místo jednoho hashe a případného rozhešování písmen - zase přidá trochu problémů při zpětném chodu.
(Pravda, ještě bych tam mohl ZBYTEK toho B místo zahození přičíst k začátku (nebo udělat rozklad na to číslo kompletní a pak ho wrapnout kolem délky C a posčítávat to) a mít tak podchycené všechny znaky - ale to by dělalo problémy jen pro dost krátká počáteční hesla a dost dlouhé hlavičky zašifrovaných dat)
---
BTW:
Ideálně by Ad nemělo být mocninou 2 a ani mít nějaký hodně jednoduchý binární rozvoj, ale to při čitelných písmenech stejně nehrozí
Možná je zbytečné to generovat, dokud to nepřeteče délkou C a stačilo by to o něco kratší, ale to už vede na počítání vysokých mocnin a logaritmů. V praxi by stačilo to dělení několikrát spustit a zjistit, při kolika znacích je (ZBYTEK +nedekódovaná část B) dost krátké a tahat to jen do této délky
A krytoanalytik by tam možná našel ještě nějaké boční kanály (jako že stačí pár desítek miliónů opakování se stejným heslem a znáš počet průchodů v tom dělení a že tam můžou nastat nějaké kolize dost daleko od sebe), ale nepředpokládám, že by se tím šifrovalo něco jako státní banka, to by to dělal odborník, který by se tady určitě na to neptal. Pro normální Eshop by to mělo být bezpečné až až.
Já mu dal odkaz na to, jak se správně a běžně v PHP hashují hesla (pomocí password_hash), tak nechápu proč v php řeší hashovací algoritmus.
Pokud by přesto chtěl to co chce, tak bych potřebné hex znaky získal, jak jsem psal, pomocí hashů, kupodivu je to funkce hash, např.: hash("sha3-512",$heslo), což by na nějakých 100 nebo 500 znaků nestačilo, a psal jsem, že by se ze znaků hashe dal udělat hash, takže by se vzal třeba první znak hashe hesla + hash hesla a z toho by se udělal hash. A takto (dál by se použil druhý znak, pak třetí..., případně by se použil třeba i poslední hash) by se to generovalo dokud by nebyl potřebně dlouhý řetězec k převodu na jeho znaky.
A pak by bylo potřeba takový řetězec převést na ty znaky, stejně jak to potřebuješ převést ty.
Jenže se mi zdá, že ten tvůj způsob převodu hex řetězce na jeho znaky by nefungoval, nebo mi něco uniká? A to bych tedy dělal taky jinak :
Z hex na dec:
https://stackoverflow.com/questions/1273484/large-hex-values-with-php-hexdec
Z dec na xyz po úravě toto:
https://stackoverflow.com/questions/39198398/php-convert-decimal-to-hexadecimal
Je možné, že to pude zjednodušit.
Pořád je to málo podrobné? To tu mám hodit hotový php kód?
Ještě poznámka, jde použít i na prázdné heslo
Pokud jsem četl správně (já to jen tak prolítnul), tak password_hash https://www.php.net/manual/en/function.password-hash.php
vrací 60 znaků, navíc ořezává to heslo
takže místo 500 znaků získáš pouze 60, nebo, rekurzivně použito, 3.600 znaků - navíc část z nich nenáhodných a navíc, pokud to aplikuješ mechanicky, tak těch nenáhodných, ba stejných bude víc než prvních 500
---
převod z jedné abecedy do druhé se dá udělat asi tak miliónem způsobů
Já si vybral ten, kde první abecedu beru jako normální písmena, či lépe jim odpovídající pole bytů nějaké délky (jelikož je to všechno uloženo v paměti, tak toto zcela jistě nějak udělat jde)
Toto pole bytů procházím po nibblech https://cs.wikipedia.org/wiki/Nibble, čímž jsem získal "dvakrát tak dlouhé pole s hodnotami 0..F" které je zápisem toho původního řetězce (jako když si uděláš hexa-dump té paměti) a představil si to jako hooodně dlouhé číslo
Načež toto číslo začnu dělit počtem písmen v druhé abecedě Ad (a získávat tak postupně její číslice, prezentované čísly 0..Ad-1)
Ty číslice zapisuju za sebe, až jich je dost, tak mám správně dlouhý řetězec z písmen té druhé abecedy.
---
Jak teď na to koukám, tak jsem se v tom trochu zamotal
https://www.matweb.cz/prevod/
Měl bych na to jít obráceně, vzít celé to strašně dlooouhé číslo a vydělit ho Ad, čímž bych získal jako zbytek poslední znak řetězce, a o něco kratší dlooouhé číslo, které bych zase dělil Ad a získal jako zbytek předposlední znak řetězce, a tak pořád dál a dál. Matematicky jednoduché, výpočetně poněkud náročnější (jako třeba počítat Ad^C a víc)
Takže to mám fakt blbě.
No, dalo by se z toho vybruslit tím, že bych to vstupní číslo rozdělil na nějaké menší shluky (třeba po 7 bytech, protože Ad bude asi tak 127-33=94 klasických ascii znaků, čili něco pod 7 bitů a ze sedmi osmibitových znaků snadno nadělám 8 sedmibitových a něco zbude ) a poctivě celočíselně vydělil ten shluk, celou část bych zapsal a zbytek "přenesl o patro níž, jako dodatečnou nejvyšší číslici" (takže prostě vzít ten zbytek a přidat ho jako nejvyšší byte nad těch dalších 7 a v dalším kroku dělit tu osmici bytů, pokud mi vyjde celočíselná dělitelnost, tak to doplnit "nulama zleva" na 8 cifer a začít dalším shlukem)
Nebylo by to přesné dělení, ale překódovávalo by se to docela rychle a většinou by se využil celý rozsah abecedy A (asi tak každý 8.-9. znak by byl z trochu menší abecedy) - no chtělo by to ještě promyslet za světla a udělat si na to příklad, ale ono by se asi dalo vyhnout i těm slabším znakům, konec konců nepotřebujeme to mít "matematicky správně vydělené" ale "jen nějak rozumně a opakovatelně rozhozené" a aby dva různé podobné vstupy ideálně vedly ke dvěma různým výsledkům.
---
jenom koukám, že než jsem tohle dopsal, tak jsi svoji odpověď doplnil - nevím, jaké jsou limity těch použitých funkcí v těch odkazech, ale pok dost velké a je to dost rychlé, tak by to mělo taky fungovat (respektive jít upravit na tu jeho abecedu)
---
A vlastně jak na to koukám, tak pokud nemá nějaký extra důvod, proč použít tu výstupní abecedu celou a proč mít zrovna přesně C a nikoli jiné blízké číslo, tak klidně na ten výsledek hodit https://cs.wikipedia.org/wiki/Base64 a vystaráno - dostane velká a malá písmena, číslovky a "+ / =" možná by to stačilo taky a jsou na to knihovný všude
1) password_hash
Vývojáři PHP nejsou žádní blbci. Kdyby nebylo použitelné a vhodné to, co je v password_hash, tak by tam bylo něco jiného.
Teď je tam to co je a podle mě je to použitelné a vhodné.
Je ten algoritmus bezpečný a je ta délka řetězce dostatečná vůči prolomení tímto způsobem? Obojí ano.
Další věc je prolomení hrubou silou, což je vpodstatě jediný způsob jak hash prolomit. Záleží tedy i na rychlosti ověřování. V manuálu k té funkci je zmíněn čas 50 ms. Asi je ten čas +- vhodný. Čím je ten čas větší, tím je to bezpečnější, ale zase to může vadit z hlediska funkcionality webu. Defaultní cost 10 na mém hardwaru dělá asi 47 ms. Cost 12 asi 184 ms (12 se běžně reálně používá pokud to chce mít člověk bezpečnější). Cost 16 asi 2940 ms a to už by uživatelsky asi moc příjemné nebylo. Hodnoty jde nastavit 04 - 31. A i ta defaultní 10 by měla zajistit relativní bezpečnost, proto tam je jako default.
2) "Jak v PHP vygenerovat string" ... "když tajné heslo bude mít třeba jen 4 znaky"
Někdo, kdo se ptá takto, podle mě neví co dělá a je pro něj nejvhodnější password_hash.
Jasně, vygenerovat string... 100, 500 znaků... Jestli to má být vůči prolomení, proč nestačí délka, která se bežně používá? Podle mě stačí a je to úplně stejně bezpečné. Daleko důležitější je vhodnost algoritmu, respektive aby ověřování probíhalo v optimálním čase. Řeší to? Neřeší. Podle mě tvůj i můj způsob je hodně rychlý a k tomu prolomení čtyřznakového hesla (počítám 118 možných znaků) by došlo během několika sekund hrubou silou. Ale to je jedno, on to přece neřeší, otázka zněla "Jak v PHP vygenerovat string".
3) V těch odkazech ten převod z hex na dec a dec na xyz je brutálně rychlý, pod 1 ms. Testováno na číslech z sha3-512 hashe, např. 1E+154 (jako jednička a 154 nul).
4) Pokud nemá nějaký extra důvod nepoužívat password_hash, tak ať použije password_hash
No, mě ten požadavek na C=500 (a hlavně následné zpracování) přišel takový ... zajímavý. Co s tím může chtít dělat?
A kdysi jsem řešil problém, který tomuto nebyl až tak nepodobný - jen trochu obecnější - pro takovou hru, kde jsi zadal jméno hrdiny a bloudil jsi bludištěm a bojoval s potvorama a hromadil poklady. A to bludiště bylo pro každého hrdinu jiné, ale pro jednoho a toho samého vždy stejné - prostě se celé vygenerovalo ze zadaného jména postupem, který byl deterministický, ale těžko uhodnutelný. Takže si nagenerovat z toho jména nějaký dost dlouhý řetězec, který to bludiště popisoval a přihodit tam něco, co ty místnosti propojovalo, aby byla cesta k cíli (a zase deterministicky, ale nepředvidatelně)
takže třeba ...xyz... (x) znamenalo že v místnosti je kobold a (y) znamenalo kolik má zlaťáků, jaký má popis (zuřivý zelený, žlutý začtený do svitku, fialový zbabělý a tak podobně) a (z) znamenalo, jaký má poklad (léčivý lektvar 3 životy, dýka +1, erotický obrázek (bezcený), pár kostí, ...) a zbytek už si hráč domyslel vlastní fantazií. Když ho to zabilo, mohl to zkusit znovu od začátku a využít, že už to měl částečně zmapované, nebo zadat jiné jméno a mít zela jiné bludiště.
Takže dokud se autor nevymáčkne, o co mu vlastně jde ideově (mít heslo, mít bludiště, mít ...), nikoli jen technicky (mít 500 znaků této abecedy), tak těžko říct, co je nejlepší přístup
Algoritmus bezva. Jen drobná připomínka:
Zvolil bych některé prvočíslo, je-li to něčím dělitelné, lze dojít k více různým výsledkům při prolamování.
Což je v našem případě pouze 9, protože 15 přichází v úvahu při jednopísmenném hesle, šifrování bez hlaviček a C větším než asi 45 tisíc :)
Teda ono i těch 9 povede k opakování páru klíčů jen u kratičkých hesel a dlouhých C (jednopísmenné heslo a C větší než 300, 2písmenné heslo a C přez 700 a tak)
Jinak jsou příčetná lichá čísla zároveň prvocísly. (Vyjímky: 1, 9, 15, 21, 25, ... a protože jde exponent u 2, tak ta velká jsou problém až u DOST nepříčetně velkých C)
A kdyby to měl být problém, tak se snadno k C přidá jeden znak, který bude indikovat, jaká sada klíčů se má použít
Teda v nastíněném problému s C=500 přidáme jeden znak za, a pokud uložený klíč má 501 znaků, tak poslední znamená sadu klíčů pro generování těch prvních 500 znaků - a každá sada může být jinak dlouhá a používat jinou sůl (tato sada používá číslo cyklu v každém cyklu, jiné mohou přidávat cokoli jiného) a klidně i jiný algoritmus, kdyby na to došlo. (teda on by se ten znak dal přidat na libovolnou pozici, nejen před, nebo za, protože už délka řetězce nese informaci o přístupu k šifrování)
To prokládání šifer + sůl (číslo průchodu) tu mj. způsobí, že se nám ta šifrování neskládají přímo, ale na posunuté a navíc silně deformované řetězce. Takže pokud nejde zrovna o Cěsarovu šifru, ale něco o fous složitějšího, tak se to spíš nerozsype, ale ještě více zacuchá