Přidat otázku mezi oblíbenéZasílat nové odpovědi e-mailem 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..

Jsou zobrazeny jen nové odpovědi. Zobrazit všechny
Předmět Autor Datum
Já bych to udělal asi tak, že bych si nechal vygenerovat několik klíčů (ideálně liché číslo větší ne…
gilhad 04.01.2022 20:20
gilhad
Díky moc. Vypadá to zajímavě, jakou to má licenci?
KUBAA 04.01.2022 20:31
KUBAA
Nějak nechápu proč se ti líbí toto a nelíbí se ti to, co jsem ti tu napsal prvně?
kacikac 04.01.2022 21:56
kacikac
Možná proto, že to je česky, má tam napsané co má dělat a trochu i proč a dá se celkem snadno předst…
gilhad 04.01.2022 23:48
gilhad
Já mu dal odkaz na to, jak se správně a běžně v PHP hashují hesla (pomocí password_hash), tak necháp…
kacikac 05.01.2022 03:09
kacikac
Pokud jsem četl správně (já to jen tak prolítnul), tak password_hash https://www.php.net/manual/en/f…
gilhad 05.01.2022 05:05
gilhad
1) password_hash Vývojáři PHP nejsou žádní blbci. Kdyby nebylo použitelné a vhodné to, co je v passw…
kacikac 05.01.2022 17:22
kacikac
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ů… poslední
gilhad 06.01.2022 00:29
gilhad

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)

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

PASSWORD_DEFAULT - Use the bcrypt algorithm (default as of PHP 5.5.0). Note that this constant is designed to change over time as new and stronger algorithms are added to PHP. For that reason, the length of the result from using this identifier can change over time. Therefore, it is recommended to store the result in a database column that can expand beyond 60 characters (255 characters would be a good choice).
PASSWORD_BCRYPT - Use the CRYPT_BLOWFISH algorithm to create the hash. This will produce a standard crypt() compatible hash using the "$2y$" identifier. The result will always be a 60 character string, or false on failure.

vrací 60 znaků, navíc ořezává to heslo

password

The user's password.
Caution

Using the PASSWORD_BCRYPT as the algorithm, will result in the password parameter being truncated to a maximum length of 72 characters.

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 :-)

Zpět do poradny Odpovědět na původní otázku Nahoru