- GET и POST аутентификация на сервере Arduino

GET и POST аутентификация на web-сервере Arduino

Практика для студентов. Мясищев А.А.

С появлением технологий удаленного управления оборудованием через сеть Интернет актуальной стала задача авторизации на управляющих серверах. Часто такими серверами являются web-сервера, построенные на основе микроконтроллеров среди которых наиболее распространены в составе контроллеров Arduino. Контроллеры Arduino наиболее доступны, дешевые, имеют большое количество плат расширения (шилдов), бесплатную программную поддержку с большим количеством библиотек. Однако в отличие от современных компьютеров Arduino имеют скромные вычислительные ресурсы в связи с чем не в состоянии поддерживать полноценные сетевые протоколы, не говоря уже о программном обеспечении обеспечивающих шифрование данных в пакетах. Поэтому команды по управлению оборудованием посылаются с браузера клиента на web-сервер Arduino без пароля идентификации. Таким образом удаленно может управлять устройствами любой пользователь, которому известен адрес сервера. Это может нанести вред правильному функционированию систем, которые могут располагаться в офисе, предприятии, учреждении, жилом доме.

Для надежной авторизации на серверах с популярными сетевыми операционными системами (Linux, FreeBSD, OpenBSD, Windows Server, ...) используется протокол https, данные которого «упаковываются» в криптографический протокол SSL или TLS, обеспечивая защиту этих данных. Работа этих протоколов выполняется с помощью специального программного обеспечения, которое не может быть установлено на сервера Arduino вследствие, в частности, малой памяти микроконтроллеров. Также и стек коммуникационных протоколов Ethernet Shield Arduino использует сокращенные, урезанные версии протоколов TCP/IP. Несмотря на указанные проблемы рассмотрим возможность упрощенной удаленной авторизации на сервере Arduino. Для этого воспользуемся технологией GET запросов POST.

Рассмотрим web-сервер на Arduino, который управляет удаленно тремя светодиодами и получает данные с температурного датчика. На рисунке 1 показан сервер на Arduino mega с контроллером сети на ENC28J60. Подключение этих двух контроллеров для библиотеки UIPEthernet показано здесь.

Рис.1. Макет исследуемого web-сервера на Arduino mega

Сервер на браузере клиента формирует форму, которая не должна отображать вводимые символы. Включение и выключение светодиодов выполняется вводом следующих кодов:

Включить красный - 11361

Выключить красный - 11360

Включить зеленый - 11381

Выключить зеленый - 11380

Включить синий - 11401

Выключить синий - 11400

Задача заключается в том, чтобы не удалось подсмотреть другому пользователю вводимые в форму коды управления. Шифрование данных по сети не рассматривается.

Рассмотрим GET запрос.

Заголовок, посылаемый от браузера клиента серверу имеет вид:

GET / HTTP/1.1

Accept: text/html, application/xhtml+xml, image/jxr, */*

Accept-Language: ru,en-US;q=0.8,en;q=0.5,uk;q=0.3

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; MALNJS; rv:11.0) like Gecko

Accept-Encoding: gzip, deflate

Host: 192.168.1.18:81

Connection: Keep-Alive

В конце каждой строки заголовка следуют символы \r\n. В случае передачи параметров для включения красного и синего светодиодов заголовок будет следующим:

GET /?ron=11361&roff=&gon=&goff=&bon=11401&boff= HTTP/1.1

Accept: text/html, application/xhtml+xml, image/jxr, */*

Referer: http://192.168.1.18:81/

Accept-Language: ru,en-US;q=0.8,en;q=0.5,uk;q=0.3

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; MALNJS; rv:11.0) like Gecko

Accept-Encoding: gzip, deflate

Host: 192.168.1.18:81

Connection: Keep-Alive

Таким образом при GET запросе передача параметров на управление передается в первой строке заголовка браузера. На рисунке 2 представлен интерфейс управления. Здесь видно, что в адресной строке в открытом виде передаются все кодовые параметры на управление устройствами. Поэтому коды на управление можно легко подсмотреть с адресной строки. Программа сервера представлена ниже.

Рис.2. Интерфейс управления для GET запроса

Программа сервера Arduino для GET аутентификации

#include <UIPEthernet.h>

#include <OneWire.h>

#include <stdio.h>

#include <DallasTemperature.h>

#define ONE_WIRE_BUS 2

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

// Enter a MAC address and IP address for your controller below.

// The IP address will be dependent on your local network:

byte mac[] = {

0xDE, 0xAD, 0xBE, 0xEF, 0x11, 0xE1

};

IPAddress ip(192, 168, 1, 18);

char buf[30];

// Initialize the Ethernet server library

// with the IP address and port you want to use

// (port 80 is default for HTTP):

EthernetServer server(81);

void setup() {

sensors.begin();

// Open serial communications and wait for port to open:

Serial.begin(9600);

while (!Serial) {

; // wait for serial port to connect. Needed for native USB port only

}

pinMode(3, OUTPUT); // 3-й вывод определен как выход для светодиода

pinMode(5, OUTPUT); // 5-й вывод определен как выход

pinMode(7, OUTPUT); // 7-й вывод определен как выход

// start the Ethernet connection and the server:

Ethernet.begin(mac, ip);

server.begin();

Serial.print("server is at ");

Serial.println(Ethernet.localIP());

}

void loop() {

// listen for incoming clients

EthernetClient client = server.available();

if (client) {

Serial.println("----- \n new client");

// an http request ends with a blank line

boolean currentLineIsBlank = true;

byte k=0;

String bufget = String("");

while (client.connected()) {

if (client.available()) {

char c = client.read();

if(k==0) bufget += c;

Serial.write(c);

// if you've gotten to the end of the line (received a newline

// character) and the line is blank, the http request has ended,

// so you can send a reply

if (c == '\n' && currentLineIsBlank) { // Действия сервера после обнаружения пустой строки

if(bufget.indexOf("ron=11361") >= 0) {digitalWrite(3, HIGH);}

if(bufget.indexOf("roff=11360") >= 0) {digitalWrite(3, LOW);}

if(bufget.indexOf("gon=11381") >= 0) {digitalWrite(5, HIGH);}

if(bufget.indexOf("goff=11380") >= 0) {digitalWrite(5, LOW);}

if(bufget.indexOf("bon=11401") >= 0) {digitalWrite(7, HIGH);}

if(bufget.indexOf("boff=11400") >= 0) {digitalWrite(7, LOW);}

// send a standard http response header

client.println("HTTP/1.1 200 OK");

client.println("Content-Type: text/html");

client.print("Connection: close\r\n\r\n"); // the connection will be closed after completion of the response

client.println("<!DOCTYPE HTML>");

client.println("<html lang=\"ru\">");

client.println("<head>");

client.println("<meta charset='Windows-1251'>");

client.println("<title>Arduino mega</title>");

client.println("</head>");

client.println("<body>");

client.println("<font size=5 color=blue> Web-server на Arduino MEGA 1280 <br>и контроллере enc28j60 <br>с аутентификацией без защиты</font>");

client.println("<br>");

client.println("<hr>");

client.println("<FORM action=\"\" method=\"GET\">");

client.println("<label for='pro'><b>Включить красный светодиод:</b></label><br>");

client.println("<input type='password' name='ron' id='pro'><br>");

client.println("<label for='prf'><b>Выключить красный светодиод:</b></label><br>");

client.println("<input type='password' name='roff' id='prf'><br><br>");

client.println("<label for='pgo'><b>Включить зеленый светодиод:</b></label><br>");

client.println("<input type='password' name='gon' id='pgo'><br>");

client.println("<label for='pgf'><b>Выключить зеленый светодиод:</b></label><br>");

client.println("<input type='password' name='goff' id='pgf'><br><br>");

client.println("<label for='pbo'><b>Включить синий светодиод:</b></label><br>");

client.println("<input type='password' name='bon' id='pbo'><br>");

client.println("<label for='pbf'><b>Выключить синий светодиод:</b></label><br>");

client.println("<input type='password' name='boff' id='pbf'><br><br>");

client.println("<INPUT type=\"submit\" value=\"Ввести\">");

client.println("</FORM>");

client.println("<br>");

sensors.requestTemperatures();

dtostrf(sensors.getTempCByIndex(0),7,2,buf);

client.print("<font size=4 color=broun>Температура ");

client.print(buf);

client.println(" градусов C</font><br><br>");

if (digitalRead(3)){ client.println("<font size=4 color=red>Красный светодиод ВКЛЮЧЕН</font><br>"); }

else{ client.println("<font size=4 color=red>Красный светодиод ВЫКЛЮЧЕН</font><br>"); }

if (digitalRead(5)){ client.println("<font size=4 color=green>Зеленый светодиод ВКЛЮЧЕН</font><br>");}

else{ client.println("<font size=4 color=green>Зеленый светодиод ВЫКЛЮЧЕН</font><br>"); }

if (digitalRead(7)){client.println("<font size=4 color=blue>Синий светодиод ВКЛЮЧЕН</font><br>"); }

else{ client.println("<font size=4 color=blue>Синий светодиод ВЫКЛЮЧЕН</font><br>"); }

client.println("<hr>");

client.print("Свободно памяти: ");

client.println(freeRam());

Serial.println("bufget:");

Serial.println(bufget); // Распечатка GET запроса

client.println("</body>");

client.println("</html>");

break;

}

if (c == '\n') {

// you're starting a new line

currentLineIsBlank = true;

if(bufget.indexOf("GET /")>=0) k=1; //Если получена 1-я строка заголовка,

// то дальше в буфер символы не записываем

} else if (c != '\r') {

// you've gotten a character on the current line

currentLineIsBlank = false;

}

}

}

// give the web browser time to receive the data

delay(1);

// close the connection:

client.stop();

Serial.println("client disconnected");

}

}

int freeRam () {

extern int __heap_start, *__brkval; int v;

return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); }

Рассмотрим POST запрос. В этом случае параметры управления(коды) передаются не через адресную строку. Заголовок, который посылает браузер при POST запросе, имеет следующий вид:

POST / HTTP/1.1

Accept: text/html, application/xhtml+xml, image/jxr, */*

Referer: http://192.168.1.18:81/

Accept-Language: ru,en-US;q=0.8,en;q=0.5,uk;q=0.3

Content-Type: application/x-www-form-urlencoded

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; MALNJS; rv:11.0) like Gecko

Accept-Encoding: gzip, deflate

Host: 192.168.1.18:81

Content-Length: 42

Connection: Keep-Alive

Cache-Control: no-cache

<---здесь пустая строка--->

ron=11361&roff=&gon=&goff=&bon=11401&boff=

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

На рисунке 3 представлен интерфейс управления для POST запроса. Видно, что в адресной строке не передаются кодовые параметры на управление устройствами, например, при включении красного и синего светодиодов.

Рис.3. Интерфейс управления для POST запроса

Программа сервера для POST запроса представлена здесь. Для считывания кодового параметра используются операторы

while(client.available()) { //Обработка запроса POST

post = client.read();

if (buffer.length() <= bufferMax) { buffer += post;

}

}

которые считывают часть заголовка, находящегося после пустой строки основной части заголовка. В массив buffer записываются управляющие параметры (ron=11361&roff=&gon=&goff=&bon=11401&boff=). В зависимости от их значения операторами

if(buffer.indexOf("ron=11361") >= 0) { digitalWrite(3, HIGH); }

if(buffer.indexOf("roff=11360") >= 0) { digitalWrite(3, LOW); }

if(buffer.indexOf("gon=11381") >= 0) { digitalWrite(5, HIGH); }

if(buffer.indexOf("goff=11380") >= 0) { digitalWrite(5, LOW); }

if(buffer.indexOf("bon=11401") >= 0) { digitalWrite(7, HIGH); }

if(buffer.indexOf("boff=11400") >= 0) { digitalWrite(7, LOW); }

выполняются соответствующие действия(включение или выключение).

Из изложенного выше видно, что при POST запросе при вводе в форму кодов управления их значения подсмотреть невозможно(как и при вводе пароля при идентификации в системе через консоль). Однако данные передаются по сети в открытой форме, без кодирования. Поэтому используя программы - sniffer можно всегда перехватить переданные данные и выявить управляющие коды.

Выводы

1. Авторизация на сервере Arduino с использованием метода запроса GET является нецелесообразной, так как при использовании форм ввода с полями для пароля коды открыто высвечиваются в адресной строке. Поэтому рядом находящийся пользователь их легко может подсмотреть.

2. Авторизация с использованием метода POST скрывает передаваемые коды, однако коды передаются по сети в открытой, в не кодированной форме, поэтому их можно перехватить с помощью программ снифферов.

22.02.2018 г.