<?php
    
/*
        ======================================================================
        HOW TO USE:

        - Copy all text and paste into a text editor
        - Save as "htpasswd.php"
        - Upload to your own web server
        - Enable Bootstrap (optional, see comment in <head> section)
        - Done

        NOTE:
        This file is not capable of updating your ".htpasswd" file
        but will show you the lines to modify.
        ======================================================================
    */

    //Set to FALSE to disallow source code viewing
    
define('ALLOW_SOURCE',TRUE);
    
    
$u=isset($_POST['usr'])?$_POST['usr']:'';
    
$p=isset($_POST['pwd'])?$_POST['pwd']:'';
    
$m=isset($_POST['mode'])?strtolower($_POST['mode']):'y2';
    
    function 
getPassLine($name,$pass){
        global 
$m;
        switch(
$m){
            case 
'sha':
                return 
$name ':{SHA}' base64_encode(sha1($pass,TRUE));
            case 
'apr1':
                return 
$name ':' crypt_apr1_md5($pass);
            case 
'a2i':
                return 
$name ':' password_hash($pass,PASSWORD_ARGON2I);
            
//case 'y2': <= Default
            
default:
                return 
$name ':' password_hash($pass,PASSWORD_BCRYPT);
        }
        return 
'ERR';
    }
    
    
//creates the apache specific md5 result of a password.
    
function crypt_apr1_md5($plainpasswd)
    {
        
$tmp='';
        
//Create 8 character salt
        
$salt=substr(str_shuffle('abcdefghijklmnopqrstuvwxyz0123456789'),0,8);
        
$len=strlen($plainpasswd);
        
//construct password string
        
$text=$plainpasswd '$apr1$' $salt;
        
//construct binary password hex
        
$bin=pack('H32',md5($plainpasswd $salt $plainpasswd));
        
        for(
$i=$len;$i>0;$i-=16){
            
$text.=substr($bin,0,min(16,$i));
        }
        for(
$i=$len;$i>0;$i>>=1){
            
$text.=($i 1)?chr(0):$plainpasswd[0];
        }
        
$bin=pack('H32',md5($text));
        
//1000 Rounds of md5 with salt
        
for($i=0;$i<1000;$i++)
        {
            
$new=($i 1)?$plainpasswd:$bin;
            if(
$i%3){
                
$new.=$salt;
            }
            if(
$i%7){
                
$new.=$plainpasswd;
            }
            
$new.=($i 1)?$bin:$plainpasswd;
            
$bin=pack('H32',md5($new));
        }
        for (
$i=0;$i<5;$i++)
        {
            
$k=$i+6;
            
$j=$i+12;
            if(
$j===16){
                
$j=5;
            }
            
$tmp $bin[$i] . $bin[$k] . $bin[$j] . $tmp;
        }
        
$tmp=chr(0) . chr(0) . $bin[11] . $tmp;
        
$tmp=strtr(strrev(substr(base64_encode($tmp),2)),
        
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
        
'./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');

        return 
'$apr1$' $salt '$' $tmp;
    }

    
//The PHP function name is longer than it should be
    
function he($x){return htmlspecialchars($x);}
    
    
//provides file source code
    
if(ALLOW_SOURCE && isset($_GET['source']))
    {
        
highlight_file(__FILE__);
        exit(
0);
    }
?><!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>htpasswd generator</title>
        <?php
            
/*
            <!--
                Use the Bootstrap Framework on your server.
                You can get it from getbootstrap.com.
                You only need the CSS parts for this Website
            -->
            */
        
?>
        <link rel="stylesheet" type="text/css" href="/bootstrap4/bootstrap.min.css" />
        <?php
            
/*
                <!--
                Bootstrap CDN.
                This uses a remote Server to use bootstrap from.
                It's simpler to use because it works "as-is" but if the CDN fails your site will look ugly and might take minutes to load.
                -->
            <link
                rel="stylesheet"
                href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
                integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
                crossorigin="anonymous" />
            */
        
?>
        <style>
            pre{
                padding:1em;
                border:1px solid #333;
                border-radius:5px;
                background-color:#AAA;
            }
        </style>
    </head>
    <body>
        <div class="container" style="padding-bottom:200px">
            <h1>.htpasswd User generator</h1>
            <?php
                
//make sure values are set and not empty
                
if(isset($_POST['pwd']) && isset($_POST['usr']) && isset($_POST['mode']) && strlen($_POST['usr'])>&& strlen($_POST['pwd'])>0)
                {
                    
//print header
                    
echo '<h2>.htpasswd line</h2>';
                    echo 
'<p>
                        Add the line below to the top of your .htpasswd file.
                        If the file already contains a user named <code>' 
he($_POST['usr']) . '</code>
                        you have to remove the old entry.
                    </p>'
;
                    echo 
'<div class="form-group"><input type="text" class="form-control" readonly size="42" value="'.
                        
he(getPassLine($_POST['usr'],$_POST['pwd'])).
                        
'" /></div>';
                }
            
?>
            <form method="post" action="<?=$_SERVER['PHP_SELF']; ?>">
                <div class="row">
                    <div class="col-md-4 form-group">
                        <label class="control-label">Username:</label>
                        <input type="text" name="usr" class="form-control" placeholder="Username" value="<?=he($u); ?>" />
                    </div>
                    <div class="col-md-4 form-group">
                        <label class="control-label">Password:</label>
                        <input type="password" name="pwd" class="form-control" placeholder="Password" value="<?=he($p); ?>" />
                    </div>
                    <div class="col-md-4 form-group">
                        <label class="control-label">Mode:</label>
                        <select class="form-control" name="mode">
                            <option value="sha"  <?php if($m==='sha') {echo 'selected';}?>>SHA1 (insecure)</option>
                            <option value="apr1" <?php if($m==='apr1'){echo 'selected';}?>>* Apache specific salted MD5 (insecure but common)</option>
                            <option value="y2"   <?php if($m==='y2')  {echo 'selected';}?>>* Bcrypt (Apache v2.4 onwards)</option>
                            <option value="a2i"  <?php if($m==='a2i') {echo 'selected';}?>>* Argon2 (experimental)</option>
                        </select>
                    </div>
                </div>
                <div class="form-group">
                    <input type="submit" class="btn btn-primary" value="generate" />
                    <a href="<?=$_SERVER['PHP_SELF']; ?>" class="btn btn-danger pull-right">Clear</a>
                </div>
                <div class="alert alert-info">
                    Modes with an asterisk <kbd>*</kbd> are "salted".
                    Each time you generate an entry with a salted algorithm it will look different
                    even if the input is identical.<br />
                    Salted modes are generally better than unsalted. If possible, use bcrypt.
                    Argon is experimental and likely only works with a custom authentication plugin.<br />
                    Regardless of the mode, the username is always stored in cleartext.
                </div>
            </form>
            <h2>Enabling password protection</h2>
            <p>Add this to the .htaccess file of the directory you want to protect:</p>

            <pre>AuthType Basic
AuthName "Description, that is sometimes shown to the user"
AuthUserFile .htpasswd
Require valid-user</pre>
            <p>This will also protect all subdirectories</p>

            <h2>Apache Configuration</h2>
            Make sure the auth_basic module is loaded.
            If it is not already, add this line to your Apache configuration (probably <code>httpd.conf</code>):<br />
            <code>LoadModule auth_basic_module modules/mod_auth_basic.so</code><br />
            Change the path as needed.<br />
            This module depends on other modules which are usually present in a default Installation.
            See <a
                href="https://httpd.apache.org/docs/2.4/mod/mod_auth_basic.html"
                target="_blank"
                rel="nofollow noopener">this site</a>
            for details.

            <h2>Path of .htpasswd</h2>
            <code>AuthUserFile</code> might need to be changed.
            It is relative to whatever directory is set as main directory in the server configuration.<br />
            If you are not sure what that path is, just leave the entry "as-is" and load the site.
            If the file doesn't exists (Server will generate HTTP 500 error), you can check the <code>error.log</code> of Apache to find the full path ot tried to look up.<br />
            If you don't have access to the error logs,
            you can just try your webservers main http directory (the one you are in when calling your website).
            <br />
            If you have access to the server Configuration, you can look for the <code>ServerRoot</code> line.

            <h2>No .htaccess</h2>
            If the file does not exists, create it in the directory you want to protect and name it <code>.htaccess</code>.
            If you use Windows, you might find yourself unable to create a file without a name and just an extension.
            To work around this issue, open the comamnd prompt (<code>cmd.exe</code>) and type this:<br />
            <code>COPY NUL "C:\Path\To\Your\.htaccess"</code><br />
            The quotes are optional if your path doesn't contains spaces.<br />
            <br />
            <?php if(ALLOW_SOURCE){ ?><a class="pull-right" href="<?=$_SERVER['PHP_SELF']; ?>?source">View Source</a><?php ?>
        </div>
    </body>
</html>