HTTP/2

Почти всички от най-разпространените браузъри го поддържат от краят на 2015 г.

As of July 2023, 36% (after topping out at just over 50%) of the top 10 million websites support HTTP/2.

Its successor is HTTP/3, a major revision that builds on the concepts established by HTTP/2.

  • Притежава т.н. negotiation mechanism, позволяващ използването на по-стария HTTP 1.1. Create a negotiation mechanism that allows clients and servers to elect to use HTTP/1.1, 2.0, or potentially other non-HTTP protocols.
  • Напълно съвместим е с HTTP 1.1 откъм методи (GET, POST…), статус кодове, хедъри… Maintain high-level compatibility with HTTP/1.1 (for example with methodsstatus codesURIs, and most header fields).
  • По-оптимални зареждане на URL ресурсите чрез: header compression, server push (deprecated), TCP connection multiplexing, prioritization of requests.
  • It is fully multiplexed, sending multiple requests in parallel over a single TCP connection.
  • HTTP/2 is binary, instead of textual. This gives the ability for HTTP/2 to be multiplexed, meaning that it can receive multiple requests at a time over just one connection instead of having to have multiple connections to complete requests.

Използването на HTTP/2 при условие, че досега е използван HTTP 1.1, не изисква никакви промени по web приложението.
The proposed changes do not require any changes to how existing web applications work, but new applications can take advantage of new features for increased speed. HTTP/2 leaves all of HTTP/1.1’s high-level semantics, such as methodsstatus codesheader fields, and URIs, the same. What is new is how the data is framed and transported between the client and the server.

Литература:

https://en.wikipedia.org/wiki/HTTP/2

https://dzone.com/articles/understanding-http2

https://tools.keycdn.com/http2-test

https://blog.fiverr.com/post/http2-is-your-site-using-it-heres-how-you-can-check

https://en.wikipedia.org/wiki/HTTP/3

Що е HTTP Authentication и има ли то почва у нас?

Нека изходим от началната точка – искаме HTTP аутентикация. Стандартният начин е чрез cookies. Но самият HTTP протокол предлага и чисто свой начин.

HTTP аутентикацията е процес, минаващ през серия от рекуести и респонси, които най-просто могат да се илюстрират така:

Първо клиентът отправя HTTP заявка.

На която сървът отговаря „не давам без да се аутентикираш кой си“, чрез хедърът WWW-Authenticate и статус кодът 401. Само един от тези (хедър и код) достатъчен ли е? То няма как. Един HTTP респонс от какво се състои? От status line, хедъри и евентуално body. Нещо такова:

HTTP/1.1 200 OK
Content-Type: text/plain

This is the response body bla bla bla

Демек, няма как да отговорим само с код или само хедър, респонсът е нещо цяло, атомарно, което трябва да има и двете, такъв е протоколът.

HTTP дава два начина за аутентикация – Basic и Digest.

Basic е по-простият начин, сървът изпраща към клиента хедърът WWW-Authenticate и статус кодът 401 като с директивата realm (задължително) задава и за кой realm иска юзър и парола. Например:

WWW-Authenticate: realm="Videos"

Клиентът има юзър и парола, които трябва първо да конкатенира с двуеточие, например alfons:123123, след което полученият стринг да бъде Base64 енкоднат и изпратен на сърва с хедъра Authorization. Например:

Authorization: Basic YWxmb25zOjEyMzEyMw==

Digest е подобен но предлага доста повече сигурност като например, че паролата е хеширана, има ограничение на броя опити, повече директиви и т.н…

Що е HTTP realm и има ли то почва у нас?

Какво наистина е т.н. realm? Целият сайт? Даден ресурс (URL)?…

И да, и не.

A realm can be seen as an area (not a particular page, it could be a group of pages) for which the credentials are used; this is also the string that will be shown when the browser pops up the login window

According to the RFC 7235, the realm parameter is reserved for defining protection spaces (set of pages or resources where credentials are required) and it’s used by the authentication schemes to indicate a scope of protection.

Демек, един и ли група от ресурси, които са групирани по защитата им, демек – трябва ти аутентикация + ауторизация за да можеш да ги достъпиш.

Могат да се застъпват, демек като множества, между които даден ресурс може да присъства и в повече от един realm. Просто като един вид групиране на ресурси.

Демек, realm не значи непременно цял сайт или отделна страница, въпреки че може и да значи. По скоро група от страници или подобни ресурси, групирани според това кой (authentication) и как (authorization) може да ги достъпва.

Литература

https://stackoverflow.com/questions/12701085/what-is-the-realm-in-basic-authentication

Host HTTP header

При request от стрна на брауза към даден хост, например www.alabala.com, браузът, като клиент, добавя и request header Host, съдържащ името на желаният домейн, понеже на даден сърв може да има N на брой виртуални хоста. Например:

Host: www.alabala.com

Идеята на този хедър е даденият сърв да знае кой точно домейн да обслужи.

В какъв смисъл?

В смисъл, че DNS системата ще превърне това www.alabala.com в IP адрес за да свърже брауза със сърва. Но сървът ще получи един HTTP рекуест и понеже сървът обикновено хоства много сайтове (виртуални хостове), няма да знае за кой точно от тях е даденият рекуест.

Ето затова е този хедър, за да кажеш на сърва кой точно домейн искаш.

Не е ли само DNS достатъчен?
Не е, работата на DNS е да обърне www.alabala.com в IP адрес за да може клиентът чисто от мрежова гледна точка да намери сърва, да си изпрати TCP пакетите с рекуеста и да получи респонса…

When multiple websites or applications are hosted on one server, this is known as virtual hosting. The server has a single IP address in this scenario, and received requests are routed to the relevant domains.

Given that websites and applications don’t have their own personal IP addresses, the purpose of the host header is to provide the server with information about the proper recipient of the request located downstream.

Да се свържеш с даден домейн с помощта на DNS значи да се свържеш по IP с даденият сърв, но ти се свързваш със сърва чисто на мрежово ниво, не знаеш на този сърв кой точно домейн ти трябва. Все едно да се стовариш с куфара на дадена гара и да не знаеш на кой точно адрес да отидеш.
Не е задължително всеки сайт да има собствен IP адрес.

Литература

https://crashtest-security.com/invalid-host-header/

HTTP/2 Server Push deprecated

До колкото разбрах, този feature е депрекейтнат.

Защо?

Явно са решили, че е безсмислено да се действа на принципа „щом искаш това, на ти и това, и това, и това…., защото ще ти трябват и те“. Нали все пак трябва някакъв алгоритъм, по-който да се реши точно какво допълнително ще ти трябва. Което не е лесно. Алгоритъмът демек.

Демек, реално идеята на HTTP/2 Server Push e супер, но на практика е много трудна и несигурна за реализация – сърва да реши кое допълнително ще ти трябва, отделно – кое вече имаш кеширано и не трябва да ти го праща и т.н…
Отделно, дали няма да флууднеш, да залееш клиента с лавина от респонси, които уж ще му трябват, която лавина той ще е затруднен да обработи…

Отделно, в HTML имаме таг атрибут rel=“preload“ към тагове като <link…> с който задаваме на брауза да започне да тегли даденият ресурс още преди да е ренднал и дисплейнал дадената страница, с което получаваме почти същият ефект.
Така даваш възможност на брауза, бидейки клиент, да прецени дали го има вече този ресурс и съответно дали да го тегли отново.

Литература:

URI vs. URL vs. URN

URI е обединяващ термин, демек нещо служещо да идентифицира даден web ресурс по един или друг начин. Какъв „един или друг“ начин? И двата (URN и URL) служат за да сочат към да даден ресурс в дадена мрежа, уникално но на ниво мрежа, била тя малка и локална, или глобалната Интернет.

И двата са разновидности от т.н. URI – идентификатор на даденият ресурс, по един или друг начин.

Това може да стане или с помощта на даден уникален стринг URN (RFC 2141),
или стринг, сочещ как да достъпим този ресур в дадената мрежа – URL (RFC 3986).

Разликата между URN и URL e, че URN е един вид само текстов идентификатор, демек стринг, който не води никъде в мрежата, а по-скоро се използва в например XML документи, разни парсери и т.н…
Демек, URN не води никъде, неговата идея е да е един стрингов идентификатор и се използва предимно в XML документи например…

Сподед RFC 2141 един примерен URN би бил:
urn:isbn:123326546546
urn:EGN:7804111111
urn:мекици:топли:предпочитани:с малиново сладко

Това което ги обединява е URI, който има малка практическа полза, защото и URN, и URL са напълно достатъчни за уникално идентифициране на даден ресурс в дадена мрежа. Просто по различен начин – едното не води на никъде, другото води, но и двете трябва уникално да идентифицират даден ресурс.

Въпреки, че например можем да използваме стринг, формиран като URL да бъде използван за да идентифицира уникално даден ресурс, но да не води на никъде. Демек, няма да е задължително URL, а ще е на практика URN. Да не забравяме, че и URL, и URN са подмножества на URI, демек негови разновидности или ако щете – практически превъплъщения.

Демек, чуем ли URN или URL, все едно чуваме URI но в различен формат.

Литература:

https://en.wikipedia.org/wiki/Web_resource

CORS (Cross-Origin Resource Sharing)

https://www.keycdn.com/support/cors

Дали един хост да може да използва ресурси от друг хост. Демек, дали например http://hello.com да използва например картинки от http://world.com

CORS e един вид политика от страна на браузъра (т.н. Same Origin Policy). Дали той като види, че даден сайт иска да зареди даден ресурс (css, js, image) от друг сърв, дали този друг сърв иска да му го предостави.

Разсъждавайте за една уебстраница или изобщо уеб ресурс, като нещо комплексно. В смисъл, съставено от много компоненти, като освен самият HTML код, JSON код, така и от сума ти статични ресурси, като JS, CSS, изображения…

Които могат да бъдат зареждани от различни сървъри, но искат ли въпросните сървъри да им използват въпросните ресурси?

Литература:

HTTP requests and response format

HTTP requests, and responses, share similar structure and are composed of:

  1. start-line, (още известен и като request line) describing the requests to be implemented, or its status of whether successful or a failure. This start-line is always a single line.
  2. An optional set of HTTP headers specifying the request, or describing the body included in the message.
  3. A blank line indicating all meta-information for the request has been sent.
  4. An optional body containing data associated with the request (like content of an HTML form), or the document associated with a response. The presence of the body and its size is specified by the start-line and HTTP headers.

Литература:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages

WebSocket

Вместо да имаме т.н. HTTP Polling, тоест клиентът постоянно да пита сървъра дали има промяна, нова информация и т.н…., сървърът сам изпраща рекуест към клиента когато има такава. Тоест имаме full duplex, двуканална и постоянно отворена TCP връзка. „Fully duplex, bi-directional communication channel, that allows the client and the server to talk to each other in real time, without having continuously make requests“.

WebSocket може да се разглежда като един вид надстройка на HTTP.

Клиентът трябва да изпрати съответни HTTP хедъри, с които да каже на сървъра „искам да ъпгрейднем наша връзка до WebSocket“. „To establish a WebSocket connection, the client sends a WebSocket handshake request, for which the server returns a WebSocket handshake response, as shown in the example below.“

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

И примерен отговор от сървъра:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

Интересно е, че клиентът трябва да поиска да се премине на WebSocket, не сървърът. Сървърът също може да го поиска като отговори на даден обикновен рекуест с „426 Upgrade Required“, но след това пак трябва да се мине по същият „WebSocket handshake“ като по-горе.
„A connection upgrade must be requested by the client; if the server wants to enforce an upgrade it may send a 426 Upgrade Required response. The client can then send a new request with the appropriate upgrade headers while keeping the connection open.“

Backend езици като Java, Python, PHP… имат готови библиотеки, с които да се напише приложение, което да действа като webserver, поддържащ WebSocket протокола.

Откъм frontend, JavaScript вече поддържа WebSocket клиент, с който можем да напишем прост WebSocket клиент. Всички модерни браузъри го поддържат. Пример:

// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');

// Connection opened
socket.addEventListener('open', function (event) {
    socket.send('Hello Server!');
});

// Listen for messages
socket.addEventListener('message', function (event) {
    console.log('Message from server ', event.data);
});

GET request max size

По принцип, от страна на HTTP протоколът, ограничение няма.

https://www.w3.org/2001/tag/doc/get7#myths – Myth: URIs cannot be longer than 256 characters

This was a limitation in some server implementations, and while servers continue to have limitations to prevent denial-of-service attacks, they are generally at least 4000 characters, and they evolve as the legitimate uses of application developers evolve.

Има различни ограничения от страна на отделните браузъри (All Browsers allowed up to around 5400 chars. FF and IE9 did up to around 6200 chars)

Също има ограничения от страна на уебсървърите, за да се избегнат различни атаки като DoS…

XMLHttpRequest

https://www.w3schools.com/xml/xml_http.asp

https://javascript.info/xmlhttprequest

All modern browsers have a built-in XMLHttpRequest object to request data from a server.

По-скоро клас, не обект, защото за да го използваш трябва да го инстанцираш.

var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
        // Typical action to be performed when the document is ready
        document.getElementById("demo").innerHTML = xhttp.responseText;
    }
};
xhttp.open("GET", "filename", true);
xhttp.send();

Използва се за HTTP комуникация със сървъра, идва си с методи като send и пропъртита като onreadystatechange и други…

Може да се използва синхронно или асинхронно.

Push vs. Pull vs. Poll in the web

Всичко в света на web е въпрос на предаване на информация между отделните участващи елементи. Затова и съществуват различни стратегии за това предаване, от гледна точка на това кой започва комуникацията.

При PULL клиентът прави заявка към сървъра и съответно използва полученият отговор според случая.

При PUSH е обратното – сървърът отправя заявка към клиента като пак може да изпозва полученият отговор според случая.

Принципната разлика между PUSH и PULL е в това кой инициира заявката, комуникацията.

Какво е POLL? Това е когато клиентът постоянно изпраща заявки към сървъра за да следи за промяна в дадена информация, състояние или събитие. Което е всъщност съвкупност от PULL заявки, но целта на практика е да се получи ефектът на PUSH заявка.

Poll requesting има две разновидности – Poll и Long poll.

При обикновеният Poll имаме периодични рекуести към сървъра, но всяка бива затваряна, за разлика от Long poll, където имаме един рекуест но се чака и не се затваря докато не се получи отговор. Е да, със сигурност пак има някакъв timeout.
„…the client polls the server requesting new information. The server holds the request open until new data is available. Once available, the server responds and sends the new information. When the client receives the new information, it immediately sends another request, and the operation is repeated.“

Webhooks

Най-просто казано – това е когато една програма изпраща HTTP заявка към друга програма, когато настъпи определено събитие.

Например, правим заявка към сървър и като част от параметрите, които му подаваме, подаваме и URL адрес, където сървърът да отправи заявка от своя страна, когато той (сървърът) приключи своята работа.
Това е много удобно например, ако искаме да видим какво получава сървърът като отговор от друг сървър.

„Като отправиш заявка към даденото 3-rd party, и получиш отговора, прати го на този URL, който е даден website като https://webhook.site/ например, където да видя този отговор.“
Тоест, webhooks са подобни на callback функциите в програмирането, изпълняват се автоматично при завършване друга дейност.
Това още се нарича HTTP Callbacks.

Webhooks идеята е подобна на PUSH/PULL начинът на комуникация с разликата, че PUSH бива отправен от сървъра към клиента, а Webhooks – от сървъра към друг сървър, който се явява „трета страна“.

Все едно да чакам пратка по пощата и да ходя всеки ден да питам дали не е пристигнала, а webhook да ми я донесат щом пристигне.

Какво в HTTP Callback?

Същото. По-скоро HTTP Callback е идеята или технологията, а Webhook е практическата реализация.

Литература:

https://bowenli86.github.io/2016/05/18/web%20development/webhook/What-is-Callback-and-Webhook/

https://zapier.com/blog/what-are-webhooks/

HTTP headers case sensitive?

HTTP хедърите НЕ СА кейс сенситив, което ще рече, че ACCEPT и Accept ще се обработи по един и същ начин.

Each header field consists of a name followed by a colon („:“) and the field value. Field names are case-insensitive.

Литература

https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html

REST Full properties

  1. Everything accessed through REST is a resource. By resource we mean the information we can access.
  2. Any resource can be accessed at a certain URL
  3. Use the full set of HTTP methods
  4. All communication is stateless
  5. All request may be cached
  6. Layers may exist between the client and the server
  7. Code on Demand – client can execute code on his side

OAuth 2

Идеята е ти, като логнат потребител на един сървър, да дадеш право на друг сървър, да контакува със сървърът, в който си логнат, от твое име.

Например, ти си логнат в Google, но искаш да използваш този акаунт, за да се логнеш и в други сайтове, примерно Facebook…

Тогава въпросната технология (OAuth 2) повелява, че в конкретният случай Google трябва да има специален сървис (API), който да „гарантира“ пред Facebook, „да, този потребител е мой, логнат е“.

Как става това програмно? Google (където си логнат като потребител), дава определен код, чрез свой отделен сървис.

Workflow-ът e следният:

Tи си логнат например в Google;

Но искаш този акаунт да го използваш и за да се логваш например във Facebook;

Което значи, че Google трябва някак да удостовери пред Facebook, че ти си логнат потребител на Google, демек Google да каже на Facebook „да, този е мой потребител, няма проблем да се логне при вас с акаунтът, който има при мен“.

Което програмно се осъществява с помощта OAuth сървис (даден URL) на Google, който сървис ти връща даден уникален код.

Който код ти подаваш на Facebook.

Който код Facebook подават на Google, който те разпознава по него (кодът), който току що ти е издал.

Който Google съответно връща на Facebook даден токен, с който те вече могат да работят с Google.

Има трима играчи: Потребителят, това си ти, логнат в Сървърът (напр. Google), който от твое име ще работи със Сървисът (напр. Facebook).
Потребителят дава право на Сървъра да работи от негово име със Сървиса.
Демек, логнал си се в Google, но опълномощаваш Google да се представя пред Facebook все едно си ти.

Изходното положение е, че Потребителят е вече логнат в Сървъра (Google напр.). Но как да даде право на Сървисът (Facebook) да работи със Сървърът (Google) от негово име? Да си даде акаунта за да се логва Сървисът (Facebook) с него? Как ли пък не!

Единствено остава Потребителят първо да вземе от Сървъра (Google) специален код, да го даде на Сървиса (Facebook), който пък да го даде на Сървъра (Google) „Ето, бай Иван ми даде този код, който ти си му дал, с което ми разрешава да работя с теб от негово име“. Тогава Сървърът (Google) дава на Сървиса (Facebook) специален токен, с който вече Сървисът (Facebook) се ауторизира пред Сървъра (Google).

Трябва да му подадеш :
1) т.н. scope – за коя точно услуга на Сървъра (Gооgle) искаш да грантнеш Сървиса (Facebook)
2) response type – например „code“
3) callback url – къде обратно да бъде изпратен отговорът с кода
4) client id

Toзи код е временен и е нужен само докато се издаде токен, защото не е сигурно токен да се предава между Потребителя и Сървисът. Между Сървъра и Сървиса, може, но не и между Потребителя и Сървиса. Този код е просто една допълнителна секюрити стъпка за добиване на токен (например JWT токен).

Демек, ти като Потребител, който се е логнал на Сървиса (Google), трябва само да вземеш този код и да го дадеш на Сървъра (Facebook), който да се идентифицира пред Сървиса (Google) от името на теб – бай Иван, за да получи токен. От там нататък вече Сървъра (Facebook) си продължава със Сървиса (Google) с помощта на този токен, кодът беше до тук.

Литература:

JWT – JSON Web Token

JWT е технология (или по-точно open standard – RFC 7519), с която сигурно да се пренасят данни в Интернет, които са форматирани като JSON обект и криптирани по определен начин.

Едно практическо приложение би било, ако например искаме да се Аутентикираме и след това съответно – Ауторизираме, без да използваме стандартният начин (със session cookies), a като при всеки request изпращаме гореспоменатият JSON, който съдържа всичко необходимо за Authorisation.

Нищо не се запазва на сърва.

При успешна Authentication сървът изпраща на клиента този JWT, криптиран с някакъв secret key, с което все едно му казва „Разбрах кой си, но не сте един или два, затова всеки път, при request от теб, пращай ми и този JWT за да преизчисля на ново signature на базата на енкоднатите header и payload, и да ги сравня дали са енакви.

Сървът, като получи Authentication JSON-a с например user и password, проверява ги и ако всичко е ОК, връща на клиента т.н JWT (JSON Web Token)

Важно е да се знае, че самата идея е JWT да се използва само след Authentication. Authentication служи само да се установи дали даденият потребител съществува и да се върне даденият JWT.
JWT се използва за Authorization в последващите рекуести.

Веднъж щом клиентът получи JWT той трябва някак да го запази при себе си, защото ще му трябва.

Голям недостатък е, че ако някой ти открадне JWT-то, може да действа от твое име.

Структура:
header.payload.signature

И трите са JSON-и (header, payload и signature), кодирани с някой от трите алгоритъма (HMAC, SHA256 или RSA) и конкатенирани с „.“

Header
Хедърът се състои от „alg“ и „typ“,
alg е алгоритъмът на подписване, например HMAC, SHA256 или RSA,
typ е „JWT“.
Т.е. хедърът изглежда най-често така:

{
    "alg": "HS256",
    "typ": "JWT"
}

След което бива Base64Url енкоднат.

Payload
„Полезният товар“ се състои от т.н. твърдения (claims), които биват три вида:
registered
public
private

Но може да има и много други полета. По принцип, в пейлодът сърва може да си сложи само някакво ID на потребителя, по което после по-лесно да го установи, когато получи обратно този JWT и го декодне. Може и поле с някакъв таймстамп, да покава до кога ще смята комуникацията за неизтекла и т.н…

Примерен payload би бил:

{
    "sub": "1234567890",
    "name": "John Doe",
    "admin": true
}

Също бива Base64Url енкоднат.

Signature
За да получим signature ни трябват хедъра и пейлоуда.
Тези двете се конкатенират с „.“ и полученият стринг се хешира с HMACSHA256 като се използва даден secret ключ, който е на сърва.
В PHP има функция hash_hmac()

Putting all together
След като имаме вече и трите части, конкатенираме ги с „.“ и това ни е JWT токенът, който можем да върнем на клиента.
При всеки последващ request от страна на клиента, той връща въпросният JWT чрез хедърът Authorization: Bearer <token>
Тогава сървът разбива въпросният JWT токен на трите части, с първите две (header и payload) изчислява отново signature и го сравнява с третата част.
Ако подаденият от клента signature съвпада с преизчисленият, всичко е ОК

Къде JWT може да има практическо приложение?

Имаш примерно акаунт на даден сърв. Логваш се с потр. име и парола (Authentication).
При успешен логин, сърва ти връща JWT токен, който съдържа информация за теб. Нищо не се пази на сървъра както при сесиите.
Клиентът (ти) пазиш този JWT при теб.
Всеки път като изпрати рекуест към сърва, изпращаш и този JWT токен. Сървът го проверява като пак изчислява хеша от хедъра и пейлоуда (използвайки сикрета за ключ) за да е сигурен, че не е променен. Ако е ОК, base64 декодва пейлоуда и вижда кой си.

Естествено, съществено важно е сигурно да пазиш този JWT при теб.

Няма как някой да си създаде JWT с твоите данни и да излъже сърва, че си ти, освен ако няма този сикрет, с който е хешнато signature-то.

Защо се казва, че JWT се използва за ауторизация, а не за аутентикация?
Защото флоуът е следният:
1. потребителят се логва на сърва
2. сърва изпраща към клиента JWT токен с инфомация за потребителя
3. при всяка следваща заявка от клиента, се изпраща и JWT-то за да знае сърва за кой потребител иде реч.
Демек, идеята е JWT да се използва в последствие, а не само първоначално.

Примерен код за генериране на JWT:

define('SECRET_KEY', 'alabalaportokala');

function base64UrlEncode(string $str): string
{
return rtrim(strtr(base64_encode($str), '+/', '-_'), '=');
}

function generateJwt(array $headers, array $payload, string $secret): string
{
$headers_encoded = base64UrlEncode(json_encode($headers, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
$payload_encoded = base64UrlEncode(json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));

$signature = hash_hmac('SHA256',
$headers_encoded . '.' . $payload_encoded,
$secret,
true
);
$signature_encoded = base64UrlEncode($signature);

$jwt = $headers_encoded . '.' . $payload_encoded . '.' . $signature_encoded;

return $jwt;
}

$headers = array('alg' => 'HS256', 'typ' => 'JWT');
$payload = array('sub' => '1234567890', 'name' => 'John Doe', 'iat' => 1516239022);

echo $jwt = generateJwt($headers, $payload, SECRET_KEY);

echo PHP_EOL . PHP_EOL;

function verifyJwtToken(string $jwt): string
{
$header_encoded = explode('.', $jwt)[0];
$payload_encoded = explode('.', $jwt)[1];

$signature_encoded = hash_hmac('SHA256',
$header_encoded . '.' . $payload_encoded,
SECRET_KEY,
true
);

return (base64UrlEncode($signature_encoded) === explode('.', $jwt)[2])
? base64_decode($payload_encoded)
: '';
}

var_dump(verifyJwtToken($jwt));

Литература

https://jwt.io/introduction

https://cryptii.com/

POST vs. PUT

По какво се различават?

Една проста но и съществена разлика между POST и PUT:

POST is not idempotent, тоест колкото и пъти да изпращаме един и същ рекуест, винаги ще създаде нов ресурс. Все едно INSERT в SQL.

PUT is idempotent, колкото и PUT рекуеста да пуснем, резултатът ще е винаги еднакъв. Все едно UPDATE (или INSERT ако няма такъв) в SQL. Просто ако първият път този ресурс не съществува – ще го съзададе, а всеки последващ PUT рекуест – ще го променя.

Тоест, с PUT изцяло променяш ресурса, тоест каквото подадеш като данни, презаписва старото. Ако искаш да промениш само някои полета – тогава PATCH.

PUT променя целият ресурс анблок, и разбира се, трябва да знаем за кой конкретен ресурс става дума. Ако даденият ресур е имал много данни, но с даден PUT рекуест подадем само едно поле, това ще е новият ресурс при сървъра. Това пък е разликата с PATCH метода.

С POST можеш да изпращаш колкото искаш различни рекуести с различни или еднакви данни, към един и същ URI адрес, но сървърът ще създава отделни ресурси за всеки рекуест.

С PUT трябва предварително да знаеш кой конкретно ресурс ще променяш. PUT е за конкретни ресурси.

По какво си приличат?

POST и PUT си приличат по това, че и с двата можеш да създаваш ресурси на сървъра, но когато го правиш с PUT, трябва предварително да знаеш на кой URI адрес ще се намира новият ресурс. Тоест, не само какво, но и къде искаш да бъде създаден.

С POST всички рекуести се подават на едно URI, а сървърът вече евентуално създава уникален URI за всеки нов ресурс.

Authentication vs. Authorization

Authentication е все едно „логването“, тоест, да се легитимираш , най-често използвайки е-mail или потребителско име и парола, за да може сървът да провери дали има такъв регистиран и валиден потребител при него.

Authorization е дали при следващ request (следващ, демек след Authentication) наистина си ти, който си се логнал и ако да, да ти върна ли респонс.

При Authentication се сетва cookie с хедъра Set-Cookie с някакво session ID.

При Authorization клиентът, вече имайки това cookie, го изпраща с хедъра Cookie за да покаже кой е.

Но и не само това. В какъв смисъл?

В смисъл такъв, ОК влязъл си, искаш да првиш това или онова, но дали имаш право на това или онова? Това, че си влязъл не ти дава пълни права за всичко. Едно приложение има различни „области“, и различните потребители може да бъдат ограничени откъм права, според дадената област.

Демек, не само дали наистина си ти, който преди малко си се логнал, но и на ниво access level дали имаш право да правиш това и онова.

Authentication determines who you are, authorization determines what you can do.

Authentication verifies the identity of a user or service, and authorization determines their access rights.

After users are authenticated, authorization is a matter of determining what level of access authenticated users should have. For instance, the system admin of a web application has typically more access than a regular user.

Литература

https://identitymanagementinstitute.org/difference-between-authentication-and-authorization

Форма във форма може ли?

No. HTML explicitly forbids nested forms.

From the HTML 5 draft:

Content model: Flow content, but with no form element descendants.

From the HTML 4.01 Recommendation:

<!ELEMENT FORM - - (%block;|SCRIPT)+ -(FORM) -- interactive form -->

(Note the -(FORM) section).

Добър въпрос е, ако имаш все пак нестнати форми и събмит бутонът в във вътрешната, и при субмит, коя ще ти събмитне?

Външната или вътрешната?

CSRF (Cross site request forgery)

Има случаи, в които сървът трябва да знае от къде идва дадената заявка от съображения за сигурност.

Представи си следната примерна ситуация: имаме форма за онлайн плащане в сайта на дадена банка. Две полета и бутон – да въведеш номер на банкова сметка, на която ще се превеждат парите, и поле за сумата, която ще превеждаме.

Даденото действие (в случаят – събмитване на sensitive information) трябва да стане от въпросната страница и форма НА СЪЩИЯТ сърв, или поне, този сърв който приема рекуеста, трябва да знае ОТ КОГО го приема и кой му го подава. Сървът трябва да знае от къде идва рекуестът.

Кой гарантира, че някой няма да направи страница с фалшива форма, която изглежда по същият начин и събмитва към същият адрес, просто полето за номер на сметка на получателят да е hidden поле с номера на банковата сметка на мошениците. Демек, някаква пеймънт система, което приема както кредитната карта на плащащият, така и на получателят, и следователно – да пренасочим плащането към мошеници.

Идеята на тези атаки е най-просто следната: ти си хакер и искаш да изпратиш например пеймънт информация към даден сърв, чрез дадена форма, хостната на него.

Така, всеки платил през тази фалшива форма ще плаща пак през истинската банка но на сметката на мошениците. Демек, банката ще получава три полета – сумата, от кого и на кого. Просто това „на кого“ ще е фалшиво, на мошениците. Но банката няма как да го знае.

За защита от подобни ситуации се добавя едно hidden поле с някакъв хеш, например session_id, който при събмит бива проверяван от сърва дали е правилен. Така отрязваш възможността всеки да събмитва към сърва от всякъде, демек – с подобни фалшиви страници и форми.

Литература

GET vs. POST

1. GET е за получаване на нещо, POST – за изпращане на нещо.

2. GET могат да бъдат букмаркнати, POST – не, защото данните, които биват изпращани с POST, не са чат от URL-a на даденият рекуест. Демек, не дават уникалност.

3. Не използвай GET за изпращане на sensitive data.

4. GET обикновено има ограничение за дължината – 255 байта, иначе има грешка Request too long (411) но то не е от самият протокол, а на ниво сървър.

5. POST няма ограничение. Има, но то НЕ Е от самия HTTP протокол, а от PHP настройките (post_max_size).

6. При GET може само текстови данни, при POST може и binary.

7. При GET данните се предават вътре в самият URL, а при POST – в бодито, като първо се пращат хедърите, после празен ред, после данните. Демек, при GET данните се пращат в адреса върху плика, при POST – вътре в писмото.

8. GET не трябва да променя информацията на сървъра, а само да я извлича. Явно затова, браузърите не се притесняват да приемат отново и отново GET заявки, за разлика от POST такива, при които съответно, няма как да знаят какво им се изпраща.

Set-Cookie and Cookie

Сървъра сетва куки на клиента с респонс хедърът Set-Cookie

Когато клиента иска да се удостовери с това куки, праща рекуест хедърът Cookie

Все едно си носиш кожуха на химическо чистене. Те не могат да помнят всеки, който им е занесъл нещо за пране, затова ти дават бележка с някакъв номер (Set-Cookie).

Ти като отидеш след два дена например, да си вземеш кожуха, казваш: „Ето, оставих ви един кожух за пране“ (Cookie)

По този номер, те ти намират кожуха.

Литература:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie

https://www.geeksforgeeks.org/http-headers-set-cookie/

REST и RESTfull

REST (REpresentational State Transfer) е философия за свързване и поддържане на тази връзка между сърв и клиент, при условие, че дадената връзка е т.н. „stateless“ (каквито по принцип са HTTP връзките).

Демек, при всеки рекeуст от страна на клиента, той трябва да се удостовери, че е този, за който претендира, че е и иска или да използва даденият ресурс, или да го промени.

И е този, с който сървът е комуникирал до преди малко.

Според тази философия, няма нещо като постоянна, непрекъсваща се връзка. Трябва при всеки рекуест, клиентът да се удостоверява някак за да бъде така да се каже проследен или известен на сърва.

Представи си двама приятели, които си говорят. За футбол, коли, жени… няма значение…
Логично е, щом са приятели, да няма нужда да се представят и запознават всеки път. Тоест, между тях има вече постоянна връзка.
И разговорът им би бил например:
– „Барса спечелила, Ливърпул загубил.“
– „Левски купили едикойси, ЦСКА продали едикойси.“
– „БМВ изкарали нов модел, по-добър от сегашните им най-добри.“
– „Мерцедес също имат нов топмодел от електрическите.“
– „Скивай тая мадама.“

и т.н…

„Stateless“ обаче е все едно:
– „Здравей, Пламен съм„. „Барса спечелила, Ливърпул загубил.“
– „Здравей, Иван съм„. „Левски купили едикойси, ЦСКА продали едикойси“.
– „Здравей, Пламен съм„. „БМВ изкарали нов модел, по-добър от сегашните им най-добри.“
– „Здравей, Иван съм„. „Мерцедес пък имат нов топмодел от електрическите“.
– „Здравей, Пламен съм„. „Скивай тая мадама.“

и т.н…

Забелязваш ли как винаги когато единият иска да каже нещо на другият, и обратното, трябва винаги да се представят, сякаш се виждат за първи път. Или все едно като двама приятели, които се сприятеляват и се забравят всеки път, когато искат да си кажат нещо.

Ето това е т.н. „stateless“. Няма състояние, няма постоянно приятелство, приятелството го има всъщност, но е само за даден рекуест.

И трябва винаги да се поддържа, при всеки рекуест-респонс.
Може би затова се казва „Representational“ – демек „ре-представяне“ или „ре-запознаване“ всеки път.

Всеки REST сървис трябва да отговаря на следните условия:

  1. Всичко е сървис на сърва и се достига през уникален URI.
  2. Правилата за комуникация между сървът и клиентът са общи, демек „да говорят на един език“, на общ протокол като например HTTP.
  3. Няма „стейт“, при всеки рекуест и респонс трябва да има „representation“, или с други думи „повторно запознаване“.
  4. Има пълна независимост (isolation) между отделните рекуст-респонси. Теоретично, и през пет години да си казват по нещо, трябва да е все едно сега си говорят.

А RESTFull е когато за да се постигне горното, могат да се използват всички HTTP методи, които включват не само четене но и качване, изтриване, променяне на данна на сърва.

REST vs. HTTP???

REST е идеята, концепцията, а HTTP e по-скоро реализацията, практиката.

REST идеята е да се използват всички методи на HTTP, но по една или друга причина се използват само GET и POST.
Например, ако трябва да се изтрие даден ресурс от сърва, всеки пуска GET с нещо от рода на /product/?delete_id=22
при условие, че има метод DELETE

Явно RESTfull значи, да се направи приложението така, че да ЗА ВСЕКИ ОТДЕЛЕН РЕСУРС да можеш да приложиш ВСИЧКИ „CRUD“ МЕТОДИ, а не с заместители като по-горният пример. Или „за всеки ресурс да имаш всичките CRUD методи“. Да можеш да създаваш нов ресурс, да извличаш такъв, да променяш както части от него така и изцяло, да изтриваш ресурс.

В този смисъл на думата, HTTP се явява подмножество на REST, или по-скоро реализация на REST, защото ти дава методите, с които да го реализираш, и ако използваш всичките предоставени методи – това е вече RESTfull.

Кееp-alive

Какво е Keep-alive

Отива клиентът във фирма да му свършат нещо и като му го свършат, казва на служителят:
„Я стой, че Пешо и той има една работа за теб, така и така съм дошъл, след това и на Гошо да свършим една работа… че да не идвам пак сто пъти…“

Под „идване сто пъти“ в случая имаме предвид – така и така TCP връзката е отворена, защо не изпратим N на брой рекуести по нея?

Брауза казва на сърва „Connection: keep-alive“, демек, „стой там, не затваряй телефона“, демек TCP връзката.

Сърва му отговаря с: „Connection: Keep-Alive
Keep-Alive: timeout=5, max=5″

Демек, „споко, тука съм, но като станат 5 нещата, които мe караш да правя, няма да чакам повече от 5 секунди за рекуест и затварям, сори“.

timeout: indicating the minimum amount of time an idle connection has to be kept opened (in seconds). Note that timeouts longer than the TCP timeout may be ignored if no keep-alive TCP message is set at the transport level.

max: indicating the maximum number of requests that can be sent on this connection before closing it. Unless 0, this value is ignored for non-pipelined connections as another request will be send in the next response. An HTTP pipeline can use it to limit the pipelining.

Прекратяване на връзката може да се задейства или от уеб браузъра или уеб сървъра, като в заглавните части на последната заявка/отговор се подаде Connection: close

В HTTP/2 този механизъм (Кееp-alive) e безсмислен, защото там изобщо принципът е различен, демек има т.н. „Multiplexing multiple requests over a single TCP connection“.