When you use embedded web for your IoT devices, you may concern about security issue, so you want nobody except for you can access your webpage. In this article, I show how to implement “Basic Access Authentication” on PHPoC to protect your webpage.



Understanding of Basic Access Authentication

From Wikipedia: “In the context of a HTTP transaction, basic access authentication is a method for a HTTP user agent to provide a user name and password when making a request” (https://en.wikipedia.org/wiki/Basic_...on#cite_note-4 )

The below image shows data flow between client and server using Basic Access Authentication.Click image for larger version  Name:	Basic Access Authentication Flow.png Views:	1 Size:	51.2 KB ID:	532
  1. Server send a request to get a protected resource
  2. When the server receive a request to a protected resource on server from client, it will look up username and password in HTTP request header. If not existed or mismatched with password stored on server, the server will send HTTP Error 401 Unauthorized response to request user to send username/password.
  3. When receiving HTTP Error 401 Unauthorized response, client (web browser) will display the default login popup to prompt user input username/password. The below image show an example of default login popup.
    Click image for larger version  Name:	Basic_Access_Auth_Defaut Popup.png Views:	1 Size:	61.0 KB ID:	535
  4. Server get password from HTTP header, if matched with password on server, the protected content will be responded to client.

To avoid constantly prompting the user for their username and password, the web browser needs to cache username/password for a period of time. Caching policy differs between browsers. Therefore, step 2 and 3 can be ignored for later request since web browser automatically includes the cached username/password in HTTP header of later requests.



What we need to implement
  • Password setting page
  • Basic Access Authentication for every webpage (including password setting page)
Password setting page

Password setting page is a user interface that allow use to set/modify/remove username/password.

Click image for larger version  Name:	Basic_Access_Auth_User_Interface.png Views:	1 Size:	43.5 KB ID:	533

When user set username/password and send it to PHPoC (as a webserver), PHPoC will store this password in flash memory.

Codes (setup_passwd.php and setup_passwd_ok.php)

setup_passwd.php provide user interface to set/modify/remove username/password
PHP Code:
<?php
include_once "setup_auth.php";
include_once 
"config.php";
include_once 
"/lib/sc_envu.php";

$envu envu_read("nm0"NM0_ENVU_SIZENM0_ENVU_OFFSET);

if(!(
$title envu_find($envu"title")))
    
$title = (string) system("uname -m");

if(!(
$web_password system("base64 dec %1",envu_find($envu"web_password"))))
    
$web_password "";

if(
$web_password != "")
{
    
$auth _SERVER("HTTP_AUTHORIZATION");

    if(
$auth)
    {
        
$input_password str_replace("Basic """$auth);
        
$input_password_dec explode(":"system("base64 dec %1"$input_password));

        if(
$input_password_dec[1] != $web_password)
        {
            
send_401();
            return;
        }
    }
    else
    {
        
send_401();
        return;
    }
}
?>
<!DOCTYPE html>
<html>
<head>
    <title><?php echo system("uname -i");?> WEB Configuration</title>
    <meta content="initial-scale=0.5, maximum-scale=1.0, minimum-scale=0.5, width=device-width, user-scalable=yes" name="viewport">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <style type="text/css">
        body { font-family: verdana, Helvetica, Arial, sans-serif, gulim;}
        h1 { font-weight: bold; font-family : verdana, Helvetica, Arial, verdana, sans-serif, gulim; font-size:15pt; padding-bottom:5px;}
        table {border-collapse:collapse; width:450px;  font-size:10pt;}
        .theader { font-weight: bold;}
        tr { height :28px;}
        td { padding: 5px 10px; text-align: left;}
        .superHeader {height: 2em; color: white; background-color: rgb(0,153,153); font-size:9pt; position:fixed; left:0; right:0; top:0; z-index:5;  }        
        .right {
          color: white;
          position: absolute;
          right: 1px;
          bottom: 4px;
          font-size:9pt;          
        }    
        .left {
          color: white;
          position: absolute;
          left: 1px;
          bottom: 4px;
          font-size:9pt;          
        }
        .right a, .left a
        {
          color: white;
          background-color: transparent;
          text-decoration: none;
          margin: 0;
          padding:0 1ex 0 1ex;
        }            
        .right a:hover, .left a:hover
        {
          color: white;
          text-decoration: underline;
         }        
        .midHeader {color: white; background-color: rgb(6, 38, 111);  position:fixed; left:0; right:0; top:1.5em;  z-index:3;}
        .headerTitle {
          font-size: 250%;
          font-weight: normal;
          margin: 0 0 0 4mm;
          padding: 0.25ex 0 1ex 0;
          font-family: impact;
        }
        .headerMenu{
            position:relative;
            width: 450px;
            padding: 5px;
        }
        #footer{margin:0 auto; height:auto !important; height:100%; margin-bottom:-100px;  }
        .superFooter {
            height: 2em; color: white; background-color: rgb(6, 38, 111); font-size:9pt; position:fixed; left:0; right:0; bottom:0; z-index:4;
        }                
        .zebra {background-color : #ECECEC;}
    </style>
    <script type="text/javascript">
    var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9+/=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/rn/g,"n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}}

    function excSubmit(mode)
    {        
        var phpoc = document.phpoc;                

        phpoc.mode.value = mode;

        if (mode == "S")
        {
            if (phpoc.web_password.value == "" || phpoc.web_password.value.length < 4)
            {
                alert("Please check the new password field.");
                phpoc.web_password.focus();
                return;
            }
            if (phpoc.web_password_confirm.value == "" || phpoc.web_password_confirm.value.length < 4)
            {
                alert("Please check the confirm password field.");
                phpoc.web_password_confirm.focus();
                return;
            }
            if (phpoc.web_password.value != phpoc.web_password_confirm.value)
            {
                alert("Please check the password again.");
                phpoc.web_password.focus();
                return;
            }
        }

        var web_password = phpoc.web_password.value;
        var web_password_confirm = phpoc.web_password_confirm.value;
        phpoc.web_password.value = Base64.encode(web_password);
        phpoc.web_password_confirm.value = Base64.encode(web_password_confirm);
        phpoc.submit();
    }
    </script>
</head>
<body>
    <div id="header">
        <div class="superHeader">        
            <div class="left">
            </div>    
            <div class="right">
                <a href="http://www.phpoc.com" target="_blank">www.PHPoC.com</a>
            </div>
        </div>

        <div class="midHeader">
            <center>
            <h1 class="headerTitle"><center><?php echo $title;?></center></h1>
            <div class="headerMenu">
                <div class="left">
                    <a href="index.php">Home</a>  |
                    <a href="setup_passwd.php">PASSWORD</a>    |            
                </div>
                <div class="right">
                    <?php if ($web_password != "") {?>
                        <a href="javascript:excSubmit('R');">REMOVE</a>  |
                    <?php }?>
                        <a href="javascript:excSubmit('S');">SAVE</a>            
                </div>
            </div>            
            <center>
        </div>

        <div class="subHeader">
        </div>        
    </div>    
    <br /><br /><br /><br />
    <form name="phpoc" action="setup_passwd_ok.php" method="post">
    <input type="hidden" name="mode" value="">        
    <center>    
        <hr style="margin:50px 0 -10px 0; width:450px;" size="6" noshade>
        <h1>Password Settings</h1>
        <table>
            <tr class="zebra">
                <td width="40%" class="theader">Password</td>    
                <td>
                    <?php if ($web_password != "") {?>
                        <input type="password" name="web_password_old" size="15" maxlength="8" value="<? echo $web_password?>" disabled>            
                    <?php }?>
                </td>
            </tr>
            <tr class="zebra">
                <td class="theader">New Password</td>    
                <td>
                    <input type="password" name="web_password" size="15" maxlength="8" value="">  (4~8 digit)
                </td>
            </tr>
            <tr class="zebra">
                <td class="theader">Confirm Password</td>    
                <td>
                    <input type="password" name="web_password_confirm" size="15" maxlength="8" value="">
                </td>
            </tr>
        </table>
    </center>    
    </form>
    <br /><br />
    <div id="footer">
        <div class="superFooter">

        </div>
    </div>    
</body>
</html>



setup_passwd_ok.php save password in flash memory, respond the succession to user

PHP Code:
<?php
$referer 
explode("/"_SERVER("HTTP_REFERER")); // http://10.1.0.1/setup_passwd.php

if(!_SERVER("HTTP_REFERER") || $referer[3] != "setup_passwd.php"// $referer[3] : setup_passwd.php
    
exit "<h4>ERROR : You were refered to this page from a unauthorised source.</h4></body>\r\n</html>\r\n";

set_time_limit(30);

include 
"setup_auth.php";
include 
"config.php";
include 
"/lib/sc_envu.php";

$envu envu_read("nm0"NM0_ENVU_SIZENM0_ENVU_OFFSET);

if(!(
$web_password system("base64 dec %1",envu_find($envu"web_password"))))
    
$web_password "";

if(
$web_password != "")
{
    
$auth _SERVER("HTTP_AUTHORIZATION");

    if(
$auth)
    {
        
$input_password str_replace("Basic """$auth);
        
$input_password_dec explode(":"system("base64 dec %1"$input_password));

        if(
$input_password_dec[1] != $web_password)
        {
            
send_401();
            return;
        }
    }
    else
    {
        
send_401();
        return;
    }

}
?>
<!DOCTYPE html>
<html>
<head>
    <title><?php echo system("uname -i");?> WEB Configuration</title>
    <meta content="initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, width=device-width" name="viewport">
    <style type="text/css">
        <!--
        body { font-family: Tahoma, Helvetica, sans-serif, gulim; }
        -->
    </style>
</head>
<body>

<center>

<br>

<?php    

$query_error_count 
0;

function 
password_setting()
{
    global 
$envu;
    global 
$query_error_count;

    if(
_POST("mode") == "R"// remove
    
{        
        
$web_password "";
    }
    else if(
_POST("mode") == "S"// save
    
{
        if((
_POST("web_password") == _POST("web_password_confirm")) && (_POST("web_password") != ""))
        {
            
$web_password _POST("web_password");
        }
        else
        {    
            
$query_error_count++;
            return 
"";
        }        
    }

    
envu_update($envu"web_password"$web_password);

    return 
$envu;
}

$envu password_setting();

if(
$query_error_count)
    echo 
"<br><br><h4>Please check new password again.</h4>\r\n";
else
{
    
envu_write("nm0"$envuNM0_ENVU_SIZENM0_ENVU_OFFSET);

    echo 
"<br><br><h2>Setup Complete</h2>\r\n";
}
?>

<a href="index.php">Home</a>&nbsp;&nbsp;&nbsp;&nbsp;<a href="setup_passwd.php">Password</a><br>

</center>
</body>
</html>



Basic Access Authentication for every webpage

When receiving any request for the protected webpage, on PHPoC, we need:
  • Read web password from flash memory
  • Get password from HTTP header
  • Compare two these passwords
  • If they are matched, send the protected webpage
  • If they are mismatched, send HTTP Error 401 Unauthorized response


Codes (setup_auth.php and example of protected webpage index.php)

setup_auth.php send HTTP Error 401 Unauthorized response to client in case of no password in HTTP request header or password mismatch.

PHP Code:
<?php

function send_401()
{
    
header('HTTP/1.0 401 Unauthorized');
    
header('WWW-Authenticate: Basic realm="ezTCP Authorization"');
    
header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate');

    echo 
"<html>\r\n" ,
        
"<head><title>ezTCP Authorization</title></head>\r\n" ,
        
"<body>\r\n" ,
        
"<b><h3>Password Mismatch<br>\r\n" ,
        
"Enter a user name as [admin].<br>\r\n" ,
        
"The password is uppper/lower case sensitive.<br>\r\n" ,
        
"</body></html>\r\n";
}

?>



index.php is a general example which show how to check the “Basic Access Authentication”.

PHP Code:
<?php  
set_time_limit
(30);

include_once 
"setup_auth.php";
include_once 
"config.php";
include_once 
"/lib/sc_envu.php";

$envu envu_read("nm0"NM0_ENVU_SIZENM0_ENVU_OFFSET);

if(!(
$title envu_find($envu"title")))
    
$title = (string) system("uname -m");

if(!(
$web_password system("base64 dec %1",envu_find($envu"web_password"))))
    
$web_password "";

if(
$web_password != "")
{
    
$auth _SERVER("HTTP_AUTHORIZATION");
    if(
$auth)
    {
        
$input_password str_replace("Basic """$auth);
        
$input_password_dec explode(":"system("base64 dec %1"$input_password));

        if(
$input_password_dec[1] != $web_password)
        {
            
send_401();
            return;
        }
    }
    else
    {
        
send_401();
        return;
    }
}
?>

<!--TODO: Modify or put your webpage content here-->

<!DOCTYPE html>
<html>
<head>
    <title>Your Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=1.0, minimum-scale=0.5, user-scalable=yes">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <style type="text/css">
    body { text-align: center; }
    p {
        font-size:160%;
        font-weight: bold;
        font-style: italic;
    }
    </style>
</head>
<body>
    <p>Put your web content here</p>
    <p><a href="setup_passwd.php">Password Setting</a></p>
</body>
</html>


You can get full source code here: PHPoC_Basic_Access_Authenticaiton.zip
Attached Files