Pages: [1]   Go Down
Author Topic: Password protect my Ethernet Web Server - SOLVED  (Read 5461 times)
0 Members and 1 Guest are viewing this topic.
South Africa
Offline Offline
Sr. Member
****
Karma: 2
Posts: 403
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I am posting this in the hope that, after sponging so much info and learning from this great forum over the past few months, I may be able to contribute back something that could be of use to other users.

After completing a small home automation system with controls accessible over the internet, I was concerned about (accidental or deliberate) unwanted access to the web server interface page.
 
The following is a basic sketch to password protect the web server page.
 
Credits : A major credit goes to Zoomkat for his huge input with this project. Without his help and coding, I would never have got this completed. Thanks Zoomkat.
 
This code may not be perfect ( except for Zoomkat's contributed parts ), and it may not be the most efficient way, but it is how I want my system to work and coded with my basic understanding so that I could follow it's operation.
 
The code works as follows :
 
If the code can not find a valid session ID in the received http, it displays the login page which requires a password.

When the user logs in, the current millis() is set as the session ID.

The page of options that is displayed after login contains the session ID in all the hyperlinks.

Click on a hyperlink sends the session ID and the command / action number to the Arduino based server.

The server then validates the session ID and performs the command / action, or if the session ID is invalid, shows the login form again.

There is a variable in the code ( passExpireMil ) that sets the idle time to wait before deleting a session ID. If set to passExpireMil = 60000, then if any link on the served page is not clicked within 60 seconds, the session expires and you need to login again.

Each click on any link on the page restarts the passExpireMil timer.


Code:
#include <SPI.h>
#include <Ethernet.h>
 
//ethernet setup
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
IPAddress ip(192,168,1,101);           // change to ip in lan
IPAddress gateway(192,168,1,254);      // change to internet access via router
IPAddress subnet(255,255,255,0);       // change to subnet mask
IPAddress myserver(41,00,00,00);       // not used in this code, but used if writing data to a server based file
EthernetServer server(82);             // server port : remember to set port forwarding on the DSL router
EthernetClient client;                 // Define a client object
 
const int passCount = 20;
unsigned long passIDstart[passCount];  // array to record start time of user sessions
unsigned long passIDlast[passCount];   // array to record last time of user sessions
unsigned long passIDvalid = 0;         // set default as passID : 0 = missing or invalid
unsigned long passExpireMil = 60000;   // expiry time for automatic expiry of valid passID
 
String readString;                     // used to clear away incoming data that is not required
char myPass[] = "1234";                // Valid password (1234 for testing)
char DoAction[] = "DoAction";          // keyword for DoAction commands
char buffer[256];                      // buffer for debugging
char xx1[20];                          // temp char for the long converted to char  
char JoinedChar[100];                  // char to hold the joined strings
String LastAction;
 
/*----------------------------------------------------------------------------*/
/* incoming data : Get a single client char                                   */
/*----------------------------------------------------------------------------*/
char gchr(void){
 while (!client.available());         /* Await data from client           */
 return client.read();               /* Return input character            */
}                                      /* end: gchr()                        */
 
/*----------------------------------------------------------------------------*/
/* incoming data : Get an entire line from the client                         */
/*----------------------------------------------------------------------------*/
char *glin(char *buf){  
 char c,*p = buf;                    /* Input char, input buffer pointer   */
 while (' ' > (c = gchr()));         /* Discard (leading) control chars    */
 do *p++ = c;                        /* Move input char to line buffer     */
 while (' ' <= (c = gchr()));        /* Until control char encountered     */
 *p = '\0';                          /* Terminate line in buffer           */
 return buf;                         /* Return pointer to input string     */
}                                       /* end: glin()                        */
 
/*----------------------------------------------------------------------------*/
/* Arduino standard setup() function                                          */
/*----------------------------------------------------------------------------*/
void setup(void){
 pinMode(4,OUTPUT);                     /* pin selected to control          */
 Ethernet.begin(mac,ip,subnet,gateway); /* Initialize ethernet device       */
 server.begin();                        /* Listen for connections           */
 Serial.begin(9600);                    /* Connect to serial monitor        */
 
 //initialize the passIDstart array to hold zero values in all elements  
 for (int i=0; i < passCount; i++){
  passIDstart[i] = 0;
 }
 
 Serial.println("Setup Done");
}                                         /* end: setup()                     */
 
/*----------------------------------------------------------------------------*/
/* Arduino standard loop() function                                           */
/*----------------------------------------------------------------------------*/
void loop(void) {
 
 unsigned long currentMillis = millis();
 
 if (client = server.available()) {    // Request client connection
  while (client.connected()) {      // Is there client data available?
   glin(buffer);                 // Get HTTP request line by line
   Serial.print("The current millis is : ");
   Serial.println(millis());
   Serial.print("Received: '");  // Show what we received from
   Serial.print(buffer);         // the current client
   Serial.println("'");
 
   passIDvalid = 0;              // set the passIDvalid to 0
   LastAction = "";
  

Logged

South Africa
Offline Offline
Sr. Member
****
Karma: 2
Posts: 403
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

rest of the code to follow the above code  ( forum limitation on number of characters in a post )

Code:
   if (strstr(buffer,"logform")) {    /* Is logform keyword present?         */
    Serial.println("Found logform");
    if (strstr(buffer,myPass)) {    /* Is password present?                */
     Serial.println("Found myPass");
 
     // clear the rest of the incoming buffer
     char c = client.read();
     if (readString.length() < 100) {         // read char by char HTTP request
       readString += c;                       // store characters to string
     }
     if (c == '\n') {                         // if HTTP request has ended - blank line received
      Serial.println("Header Flushed");
     }
 
     /* Login form processing */
 
     // get the first open passID storage space and create a new session ID.
     for (int i=0; i < passCount; i++){
      if (passIDvalid == 0 && passIDstart[i] == 0){
       passIDstart[i] = currentMillis;  // set the variable to use as the passID
       passIDlast[i] = currentMillis;   // set the last time the passID was used
       passIDvalid = passIDstart[i];
       Serial.print("New alocated passID = ");
       Serial.print(passIDvalid);
       Serial.print(" in array space ");
       Serial.println(i);
       LastAction = "Login Completed";
      }
     }                     // end of loop for get the first open passID storage space
    }                         // end: if password is present
   }                             // end: if logform in line
 
   Serial.println("Processing input");
 
   // clear expired passIDs from array - housekeeping each time a server connection is made
   for (int i=0; i < passCount; i++){
    if (passIDstart[i] != 0){
       if (currentMillis >= passExpireMil){
      if (currentMillis - passIDlast[i] >= passExpireMil){
       passIDstart[i] = 0;
       Serial.print("Cleared passIDstart ");
       Serial.println(i);
      }
     }
    }
   }
 
   unsigned int GotAction = 0;   // shows that no DoAction command received
 
   // look for DoAction commands in the input buffer
   if (strstr(buffer,"DoAction")) {    /* If DoAction keyword is present         */
    Serial.println("Found DoAction keyword");
    Serial.println("Looking for valid passID");
 
    //see if a valid passID exists in the DoAction line
    for (int i=0; i < passCount; i++){
 
     if (GotAction == 0 && passIDstart[i] != 0){
 
      ltoa(passIDstart[i],xx1,10);  // convert the long to char
 
      //check for the passID + DoAction keyword + action code in the buffer
 
      JoinedChar[0] = '\0';               // clear the destination array
      strcat(JoinedChar, xx1);            //add the passID to the array
      strcat(JoinedChar, DoAction);       //add the keyword to the array
      strcat(JoinedChar, "on001");        //add the action code to the array
       if (strstr(buffer,JoinedChar)) GotAction = 1;
 
      JoinedChar[0] = '\0';               // clear the destination array
      strcat(JoinedChar, xx1);            //add the passID to the array
      strcat(JoinedChar, DoAction);       //add the keyword to the array
      strcat(JoinedChar, "on002");        //add the action code to the array
       if (strstr(buffer,JoinedChar)) GotAction = 2;
 
      JoinedChar[0] = '\0';               // clear the destination array
      strcat(JoinedChar, xx1);            //add the passID to the array
      strcat(JoinedChar, DoAction);       //add the keyword to the array
      strcat(JoinedChar, "on003");        //add the action code to the array
       if (strstr(buffer,JoinedChar)) GotAction = 3;
 
      if(GotAction != 0){
       Serial.print("Found DoAction ");
       Serial.print(GotAction);
       Serial.print(" for passID : ");
       Serial.println(passIDstart[i]);
       passIDlast[i] = currentMillis;   //renew the last time that the passID was used
       passIDvalid = passIDstart[i];
      }
     }
    }
 
    if(GotAction != 0){
 
     // clear the rest of the incoming buffer
     char c = client.read();
     if (readString.length() < 100) {         // read char by char HTTP request
       readString += c;                       // store characters to string
     }
     if (c == '\n') {                         // if HTTP request has ended - blank line received
      Serial.println("Header Flushed");
     }
 
     /* DoAction processing */
 
     if(GotAction == 1){
      Serial.println("Received Web Server command on001");
      // do tasks in response to command 1 received
      LastAction = "Command on001 processed";
     }
     if(GotAction == 2){
      Serial.println("Received Web Server command on002");
      // do tasks in response to command 2 received
      LastAction = "Command on002 processed";
     }
     if(GotAction == 3){
      Serial.println("Received Web Server command on003");
      // do tasks in response to command 3 received
      LastAction = "Command on003 processed";
     }
    }
   }
   
 
   //display the web page
   client.println("HTTP/1.1 200 OK"); //send new page
   client.println("Content-Type: text/html");
   client.println();
   client.println("<HTML>");
   client.println("<HEAD>");
   client.println("<STYLE TYPE=\"text/css\"><!--");     //set the style for the page to Courier New
   client.println("BODY   {   font-family:Courier New;   }");
   client.println("--></STYLE>");
   client.println("<TITLE>Home Control</TITLE>");       //browser tab title
   client.println("</HEAD>");
   client.println("<BODY>");                            //start of body section
   client.println("Home Control<br><br>");
   client.print("Current Millis value = ");
   client.print(currentMillis);
   client.println("<br><br>");
   client.print("Last Action : ");
   client.print(LastAction);
   client.println("<br><br>");
 
   if (passIDvalid == 0){  // this is not a valid passID - ask for the password
    client.print ("<form method=get>");
    client.print ("<input type=password name=logform size=10>");
    client.print (" <input type=submit value=Login>");
    client.print ("</form>");
   }
 
   if (passIDvalid != 0){  // this IS a valid passID - display the web page
 
    client.print("System : <a href=\"./?");
    client.print(passIDvalid);
    client.print("DoActionon001");
    client.println("\"\">[Command 1]</a><br><br>");
 
    client.print("System : <a href=\"./?");
    client.print(passIDvalid);
    client.print("DoActionon002");
    client.println("\"\">[Command 2]</a><br><br>");
 
    client.print("System : <a href=\"./?");
    client.print(passIDvalid);
    client.print("DoActionon003");
    client.println("\"\">[Command 3]</a><br><br>");
   }
 
   client.println("</BODY>");
   client.println("</HTML>");
   delay(1);
   
   client.stop();                /* Disconnect from the server          */
  }                                /*  end: while client connected        */
 }                                   /*  end: if client connection          */
}                                      /*  end: loop()                        */
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hi,
i like your project so much but when i tried it , it didn't work smiley-sad
i will tell u what i have done:
first upload code to arduino uno and connect it to ehternet shield and i update my mac address
then write 192.168.1.101:82
it tells me webpage not available . i don't know why ?
i need the part of the password to my project so urgently.
thanks in advance
Logged

South Africa
Offline Offline
Sr. Member
****
Karma: 2
Posts: 403
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi

1. ensure that the IP address you enter, example :
Code:
IPAddress ip(192,168,1,101);
does not conflict with another IP address on your network.

2. did you set port forwarding in your router to forward port 82 to the ethernet shield ?

3. you mentioned : 192.168.1.101:82    In your browser, you should use  "http://"  +  your public IP address  + :82  so look at http://www.whatismyip.com/ for your IP and add the  "http://" to the start, and  ":82"  at the end.

Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hi ,
i tried it in LAN so i didn't do port forwarding , i typed in browser 192.168.1.101:82

i want to add the password part to that code , when i tried that it gives my page to enter password when i entered it correctly it opened the page to control leds but when i press on anything nothing happens to leds and it opens the page of password again.
Code:

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 102 }; // ip in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(84); //server port

String readString;

//////////////////////

void setup(){

  pinMode(5, OUTPUT); //pin selected to control
  pinMode(6, OUTPUT); //pin selected to control
  pinMode(7, OUTPUT); //pin selected to control
  pinMode(8, OUTPUT); //pin selected to control
  //start Ethernet
  Ethernet.begin(mac, ip, gateway, gateway, subnet);
  server.begin();

  //enable serial data print
  Serial.begin(9600);
  Serial.println("server multi pin button test 1.0"); // so I can keep track of what is loaded
}

void loop(){
  // Create a client connection
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string
          readString += c;
          //Serial.print(c);
        }

        //if HTTP request has ended
        if (c == '\n') {

          ///////////////
          Serial.println(readString); //print to serial monitor for debuging

          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();

          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<TITLE>Arduino GET test page</TITLE>");
          client.println("</HEAD>");
          client.println("<BODY>");

          client.println("<H1>Zoomkat's simple Arduino button</H1>");
         
          // For simple testing, pin 5, 6, 7, and 8 are used in buttons
          // DIY buttons
          client.println("<a href=/?on2 >ON</a>");
          client.println("<a href=/?off3 >OFF</a>");
          client.println("&nbsp;<a href=/?off357 >ALL OFF</a><br><br>");

          // mousedown buttons
          client.println("<input type=button value=ON onmousedown=location.href='/?on4'>");
          client.println("<input type=button value=OFF onmousedown=location.href='/?off5'>");       
          client.println("&nbsp;<input type=button value='ALL OFF' onmousedown=location.href='/?off3579'><br><br>");       
                   
          // mousedown radio buttons
          client.println("<input type=radio onmousedown=location.href='/?on6'>ON</>");
          client.println("<input type=radio onmousedown=location.href='/?off7'>OFF</>");
          client.println("&nbsp;<input type=radio onmousedown=location.href='/?off3579'>ALL OFF</><br><br>");   
   
         
          // custom buttons
          client.print("<input type=submit value=ON style=width:100px;height:45px onClick=location.href='/?on8'>");
          client.print("<input type=submit value=OFF style=width:100px;height:45px onClick=location.href='/?off9'>");
          client.print("&nbsp;<input type=submit value='ALL OFF' style=width:100px;height:45px onClick=location.href='/?off3579'>");

          client.println("</BODY>");
          client.println("</HTML>");
 
          delay(1);
          //stopping client
          client.stop();

          ///////////////////// control arduino pin
          if(readString.indexOf('2') >0)//checks for 2
          {
            digitalWrite(5, HIGH);    // set pin 5 high
            Serial.println("Led 5 On");
          }
          if(readString.indexOf('3') >0)//checks for 3
          {
            digitalWrite(5, LOW);    // set pin 5 low
            Serial.println("Led 5 Off");
          }
         
          if(readString.indexOf('4') >0)//checks for 4
          {
            digitalWrite(6, HIGH);    // set pin 6 high
            Serial.println("Led 6 On");
          }
          if(readString.indexOf('5') >0)//checks for 5
          {
            digitalWrite(6, LOW);    // set pin 6 low
            Serial.println("Led 6 Off");
          }
         
           if(readString.indexOf('6') >0)//checks for 6
          {
            digitalWrite(7, HIGH);    // set pin 7 high
            Serial.println("Led 7 On");
          }
          if(readString.indexOf('7') >0)//checks for 7
          {
            digitalWrite(7, LOW);    // set pin 7 low
            Serial.println("Led 7 Off");
          }     
         
            if(readString.indexOf('8') >0)//checks for 8
          {
            digitalWrite(8, HIGH);    // set pin 8 high
            Serial.println("Led 8 On");
          }
          if(readString.indexOf('9') >0)//checks for 9
          {
            digitalWrite(8, LOW);    // set pin 8 low
            Serial.println("Led 8 Off");
          }         
             
          //clearing string for next read
          readString="";

        }
      }
    }
  }
}


Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 1
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hey man could you help me, with ur arduino ehthernet web server with password, it didnt work, i have my ports open and i tryed to see the code whit mi external ip and the :81 in your code, bud it didnt work, what  am i doing wrong. sorry for my bad English
or u could made a tutorial step by step thanks man
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@mohamed66
Code:
          Serial.println(readString); //print to serial monitor for debuging
What did this print?

Regardless of what GET request the client made, you serve up the same page. That page does not contain any log on information, so how it relates to the topic you hijacked is a mystery.

@iluvatarGats
Quoting is a good thing, so we can see who you are talking to. Who's code are you interested in?
Logged

New River, Arizona
Offline Offline
God Member
*****
Karma: 19
Posts: 928
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for the code that uses a session ID.  Session IDs are cool, but I didn't want to spend a ton of time working the bugs out to implement it.  I have been taking a different tactic.  I sample the incoming IP address and compare it to my local netmask to see if the request is incoming from my local network.  If it is, I allow things to be done, if it isn't, they get denied.  To allow me to change things from way far away, I give it a secret word that turns off the check for a little while allowing me to change the temperature, close a door or whatever.  The security gets turned back on automatically on expiration of a timer so I can't forget.

This has been working fine and I get occasional (not very often) attempts to open my garage doors or something, but never a sophisticated attack.  On a device this dumb, sophisticated attacks just don't work.  The secret word is in the code only and never goes out over the web so it should be good for a long time.  The attempts to do things are most likely people clicking to see what happens, nothing bad, just curiosity.  Curiosity is fine, I don't mind that at all.

I gotta take your example an try it.
Logged

Trying to keep my house under control http://www.desert-home.com/

Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Cool, currently trying to adapt your method to my little project on home automatisation, will post results here.
Thanks for idea!
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 1
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi!

I'm new to arduino and registered just to say:

Thank you very much Sir!  smiley-cool

The code works very well on my platform (arduino Uno + Ethernet Shield with PoE) but a few strings were to be pulled:

The program left only 131 bytes of SRAM left on the processor (witch has 2k ) , the server didn't respond at all and the serial debugging was a mess with a lot of stupi charachters. So, I tried to reduce the SRAM space occupied by the program. And with a simple method I left the Arduino with an astonishing 963 bytes left to play (instead of the 131 bytes)

the method is very simple. In every line of the program with, for example

Code:

    client.print("DoActionon003");
    client.println("\"\">[Command 3]</a><br><br>");
    Serial.print("Setup Done");


you (not the author of the code, you, who are going to use the code) should put the quotes inside the function "F"

Code:

    client.print(F("DoActionon003"));
    client.println(F("\"\">[Command 3]</a><br><br>"));
    Serial.print(F("Setup Done"));


with this, you are reducing the space written in the SRAM and passing it to program memory.

Cheers!
Logged

Pages: [1]   Go Up
Jump to: