How To: Use PHP to Change a Bluehost Email Account Password

This is a PHP script I wrote that allows you to incorporate an email password update option into your own webpage styling. Normally, with Bluehost (and FastDomain and HostMonster), your users can only change their email account password by either having the admin login to their cpanel and changing it for them, or by accessing the Bluehost provided webmail interface. Neither are very inviting solutions, especially in a professional environment.

The script works by accessing the shadow file associated with the email account’s domain name. It looks for the username whose password is being changed in the shadow file and, if found, compares the password stored in the shadow file with the password that the user enters as their current password. If those passwords match, it then updates the shadow file with the users’ new password. The password is stored in the shadow file as a Base64 encoded salted MD5 hash which is generated using either the PHP crypt function (if available) or the openssl command line.

This script will work out of the box, however, I recommend using it with the current visual theme of your website otherwise you’re defeating the purpose. The hidden input field named “domain” may be edited so that users only have to enter the name portion (ie: joesnuffy) of their email address without the domain portion. If left un-edited, your users will be required to enter in the domain portion of their email address along with the name portion (ie: joesnuffy@linuxr0ckz.com).

This script will work with Bluehost, FastDomain, HostMonster (all operated by Bluehost) and I’m guessing it will also work with any other host that uses cpanel (unverified).

<?php

$message = "";
$found = $valid = false;

if (isset($_POST['username']) && $_POST['username'] != "") {
    $domain_pos = strpos($_POST['username'], "@");
    if ($domain_pos === false) {
        $username = $_POST['username'];
        $domain = $_POST['domain'];
    } else {
        $username = substr($_POST['username'], 0, $domain_pos);
        $domain = substr($_POST['username'], $domain_pos + 1);
    }
    
    $current_password = $_POST['current_password'];
    $new_password1 = $_POST['new_password1'];
    $new_password2 = $_POST['new_password2'];
    
    $root = $_SERVER['DOCUMENT_ROOT'];
    $path_elements = explode('/', $root);
    $root = "/{$path_elements[1]}/{$path_elements[2]}"; // for bluehost, extracts homedir ex: /homeN/blueuser may work with other hosts?
    $shadow_file = "$root/etc/$domain/shadow";

    // check if the shadow file exists. if not, either an invalid
    // domain was entered or this may not be a bluehost account...?
    if (file_exists($shadow_file)) {
        // compare the new passwords entered to ensure they match.    
        if ($new_password1 == $new_password2) {
            if (trim($new_password1) != "") {
                // get the contents of the shadow file.
                $shadow = file_get_contents($shadow_file);
                $lines = explode("\n", $shadow);
                
                // go through each line of shadow file, looking for username entered.
                for ($i = 0; $i < count($lines); $i++) {
                    $elements = explode(":", $lines[$i]);
                    // found the user...
                    if ($elements[0] == $username) {
                        $found = true;
                        $passwd = explode("$", $elements[1]);
                        $salt = $passwd[2]; // get the salt currently used 
                        
                        // crypt the "Current Password" entered by user. Can use either builtin 
                        // php crypt function or command line openssl command.
                        if (CRYPT_MD5 == 1) { // indicates whether or not the crypt command supports MD5.
                            $current = crypt($current_password, '$1$'.$salt.'$');
                        } else {
                            $current = trim(`openssl passwd -1 -salt "$salt" "$current_password"`);
                        }
                        // check if the current password entered by the user
                        // matches the password in the shadow file.
                        $valid = ($current == $elements[1]);
                        
                        if ($valid) {
                            // if they match then crypt the new password using the same salt
                            // and modify the line in the shadow file with the new hashed password
                            if (CRYPT_MD5 == 1) {
                                $new = crypt($new_password1, '$1$'.$salt.'$');
                            } else {
                                $new = trim(`openssl passwd -1 -salt "$salt" "$new_password1"`);
                            }
                            $elements[1] = $new;
                            $lines[$i] = implode(":", $elements);
                        }
                        
                        break;
                    }
                }
                
                if (!$found) {
                    $message = "The username you entered is not valid.";
                } else if (!$valid) {
                    $message = "The password you entered is not valid.";
                } else {
                    // write the new contents of the shadow back to the shadow file.
                    $shadow = implode("\n", $lines);
                    file_put_contents($shadow_file, $shadow);
                    $message = 'Your password has been updated.';
                }
            } else {
                $message = "Your password cannot be blank.";
            }
        } else {
            $message = "Both new passwords must match.";
        }
    } else {
        $message = "The domain you entered is not valid.";
    }
}

?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
    <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> 
        <title>Change Password</title>
    </head>
    
    <body>            
        <?php
            if ($message != "") {
                $color = $found && $valid ? "green" : "red";
                echo "<span style=\"color:$color;\">$message</span>";
            }
        ?>
        
        <form action="" method="post">
            <input type="hidden" name="domain" value="somebluehostdomain.com" />
            <table>
                <tbody>
                    <tr>
                        <td><label for="username">Username</label></td> 
                        <td><input name="username" id="username" type="text" /></td> 
                    </tr>
                    <tr>
                        <td><label for="current_password">Current Password</label></td> 
                        <td><input name="current_password" id="current_password" type="password" /></td> 
                    </tr>
                    <tr>
                        <td><label for="new_password1">New Password</label></td> 
                        <td><input name="new_password1" id="new_password1" type="password" /></td> 
                    </tr>
                    <tr>
                        <td><label for="new_password2">New Password</label></td> 
                        <td><input name="new_password2" id="new_password2" type="password" /></td> 
                    </tr>
                    <tr>
                        <td colspan="2" style="text-align:center;">
                            <input type="submit" value="Update Password" />
                        </td>
                    </tr>
                </tbody> 
            </table> 
        </form>
    </body>
</html>

One thought to “How To: Use PHP to Change a Bluehost Email Account Password”

  1. I’m not using bluehost but I think it should work with my server.
    I tried this script but it always gives me this error “The password you entered is not valid.”
    However, it can check whether the username exist or not.
    Can you help?

Leave a Reply

Your email address will not be published. Required fields are marked *